diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css index 86949b1d8e8b73cb0ed32337ac2317e4b9eff3f8..a211afa2e80fa0093e589bf8d9eee3e928727a0d 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/css/style.css @@ -722,4 +722,15 @@ table.downloads { legend { margin-top: 0; margin-bottom: 5px; -} \ No newline at end of file +} + +/* + * Settgins + */ +.borderless td, .borderless th { + border: none !important; +} + +#settings-slider-low-storage, #settings-slider-low-box { + width : 100% !important; +} diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js index 5d1e6eb6274e939c2dddabb439c401edf7ef2e5f..037d2ce4645a7a1be1d18fd35841eba86fe20758 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js @@ -446,7 +446,14 @@ $.extend(DefaultProfile.prototype, { /* * Used by DataSet Uploader */ + this.dataSetTypeForFileNameMap = []; + this.getDataSetTypeForFileName = function(allDatasetFiles, fileName) { + for (var dataSetTypeForFileName of this.dataSetTypeForFileNameMap) { + if (fileName.endsWith(dataSetTypeForFileName.filenameExtension)) { + return dataSetTypeForFileName.dataSetType; + } + } return null; } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js index 2746d770569ed324d1f858c0b752c73b8aef98f7..48a4df2658179beeafd91e1e0902a01046eed92e 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js @@ -801,12 +801,10 @@ $.extend(StandardProfile.prototype, DefaultProfile.prototype, { } } - this.getDataSetTypeForFileName = function(allDatasetFiles, fileName) { - if(fileName.endsWith("fasta")) { - return "SEQ_FILE"; - } else { - return null; - } - } + + this.dataSetTypeForFileNameMap = [ + { fileNameExtension : "fasta", dataSetType : "SEQ_FILE" }, + ]; + } }); \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js index 8443216a84794b2daafc945db0ff2fd1dca9b6b2..9f8bad7a10a498b0e5271d8284a30265878be44f 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js @@ -654,7 +654,7 @@ function MainController(profile) { if(!data[0]) { window.alert("The item is no longer available, refresh the page, if the problem persists tell your admin that the Lucene index is probably corrupted."); } else { - var newView = new SettingsFormController(this, data[0], mode); + var newView = new SettingsFormController(_this, data[0], mode); var views = _this._getNewViewModel(true, true, false); newView.init(views); _this.currentView = newView; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js index 718c15c1d1a931b66d3df753242a68d80bdb9f35..938a16403f01893a213bc436f3884e723a5c6e35 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js @@ -23,7 +23,148 @@ function SettingsFormController(mainController, settingsSample, mode) { this._settingsFormView.repaint(views); } - this.save = function() { - alert("Save Clicked"); + this.save = function(settings) { + console.log("settings:"); + console.log(settings); + var errors = this._validateSettings(settings); + if (errors.length > 0) { + // TODO handle errors + alert(errors.join("\n\n")); + } else { + // TODO store settings + this._applySettingsToProfile(settings); + this._mainController.changeView("showSettingsPage"); + } } -} \ No newline at end of file + + this.getAllDatasetTypeCodeOptions = function() { + return profile.allDatasetTypeCodes; + } + + this.getForcedDisableRTFOptions = function() { + return profile.allPropertyTypes.map(function(propertyType) { + return propertyType.code; + }); + } + + this.getForcedMonospaceFontOptions = function() { + return profile.allPropertyTypes.map(function(propertyType) { + return propertyType.code; + }); + } + + this.getSampleTypeProtocolsOptions = function() { + return profile.allSampleTypes.map((sampleType) => {return sampleType.code}) + } + + this.getInventorySpacesOptions = function() { + return profile.allSpaces; // TODO is this correct? what about "STOCK_CATALOG"? + } + + this._applySettingsToProfile = function(settings) { + var fields = [ + "dataSetTypeForFileNameMap", + "mainMenu", + "forcedDisableRTF", + "forceMonospaceFont", + "inventorySpaces", + "sampleTypeProtocols", + ]; + for (var field of fields) { + if (settings[field]) { + profile[field] = settings[field]; + } + } + } + + // TODO move validation to some place where it can be called on application startup as well + this._validateSettings = function(settings) { + var errors = []; + this._validateMainMenu(settings, errors); + this._validateForcedDisableRTF(settings, errors); + this._validateForcedMonospaceFont(settings, errors); + this._validateInventorySpaces(settings, errors); + this._validateDataSetTypeForFileNameMap(settings, errors); + this._validateSampleTypeProtocols(settings, errors); + return errors; + } + + this._validateMainMenu = function(settings, errors) { + if (settings.mainMenu) { + var availableMenuItems = Object.keys(profile.mainMenu); + for (var menuItem of Object.keys(settings.mainMenu)) { + if (availableMenuItems.indexOf(menuItem) === -1) { + errors.push(menuItem + + " is not a Main Menu Item. Available items: " + + availableMenuItems.join(", " + + ".")); + } + if (typeof(settings.mainMenu[menuItem]) !== "boolean") { + errors.push(menuItem + ": " + settings.mainMenu[menuItem] + " is not of type 'boolean'."); + } + } + } + } + + this._validateForcedDisableRTF = function(settings, errors) { + if (settings.forcedDisableRTF) { + for (var item of settings.forcedDisableRTF) { + if (this.getForcedDisableRTFOptions().indexOf(item) === -1) { + errors.push(item + " is not a property type."); + } + } + } + } + + this._validateForcedMonospaceFont = function(settings, errors) { + if (settings.forceMonospaceFont) { + for (var item of settings.forceMonospaceFont) { + if (this.getForcedMonospaceFontOptions().indexOf(item) === -1) { + errors.push(item + " is not a property type."); + } + } + } + } + + this._validateInventorySpaces = function(settings, errors) { + if (settings.inventorySpaces) { + for (var item of settings.inventorySpaces) { + if (this.getInventorySpacesOptions().indexOf(item) === -1) { + errors.push(item + " is not space."); + } + } + } + } + + this._validateSampleTypeProtocols = function(settings, errors) { + if (settings.sampleTypeProtocols) { + for (var item of settings.sampleTypeProtocols) { + if (this.getSampleTypeProtocolsOptions().indexOf(item) === -1) { + errors.push(item + " is not a sample type protocol."); + } + } + } + } + + this._validateDataSetTypeForFileNameMap = function(settings, errors) { + if (settings.dataSetTypeForFileNameMap) { + for (var dataSetTypeForFileName of settings.dataSetTypeForFileNameMap) { + if ( ! dataSetTypeForFileName.dataSetType) { + errors.push("dataSetTypeForFileNameMap must contain a field named dataSetType."); + } else if (this.getAllDatasetTypeCodeOptions().indexOf(dataSetTypeForFileName.dataSetType) === -1) { + errors.push("Dataset type " + + dataSetTypeForFileName.dataSetType + + " is not allowed. Available types: " + + this.getAllDatasetTypeCodeOptions().join(", ") + + "."); + } + if ( ! dataSetTypeForFileName.fileNameExtension) { + errors.push("dataSetTypeForFileNameMap must contain a field named fileNameExtension."); + } else if (dataSetTypeForFileName.fileNameExtension.length == 0) { + errors.push("Filename extension can't be empty."); + } + } + } + } + +} diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormView.js index da377ace2080d39f68a0ce87442656d0eab8f81c..88089a2cd1a77e7db4d04fa69b32ed8a7dde2e8d 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormView.js @@ -17,37 +17,43 @@ function SettingsFormView(settingsFormController, settingsFormModel) { this._settingsFormController = settingsFormController; this._settingsFormModel = settingsFormModel; - + + this._datasetTypesTableModel = null; + + this._mainMenuItemsTableModel = null; + this._forcedDisableRTFTableModel = null; + this._forcedMonospaceTableModel = null; + this._inventorySpacesTableModel = null; + this._sampleTypeProtocolsTableModel = null; + this.repaint = function(views) { - var _this = this; + var $container = views.content; - + var $form = $("<div>"); var $formColumn = $("<div>"); - $form.append($formColumn); - + var typeTitle = "Settings"; - + var $formTitle = $("<h2>").append(typeTitle); - + // // Toolbar // - var toolbarModel = []; - - + var toolbarModel = []; + if(this._settingsFormModel.mode === FormMode.VIEW) { //Edit var $editButton = FormUtil.getButtonWithIcon("glyphicon-edit", function () { - mainController.changeView('showEditSettingsPage'); + mainController.changeView("showEditSettingsPage"); }); toolbarModel.push({ component : $editButton, tooltip: "Edit" }); } else { //Create and Edit //Save - var $saveBtn = FormUtil.getButtonWithIcon("glyphicon-floppy-disk", function() { - _this._settingsFormController.save(); - }, "Save"); + var $saveBtn = FormUtil.getButtonWithIcon("glyphicon-floppy-disk", (function() { + this._settingsFormController.save(this._getSettings()); + }).bind(this), "Save"); $saveBtn.removeClass("btn-default"); $saveBtn.addClass("btn-primary"); toolbarModel.push({ component : $saveBtn, tooltip: "Save" }); @@ -56,7 +62,391 @@ function SettingsFormView(settingsFormController, settingsFormModel) { var $header = views.header; $header.append($formTitle); $header.append(FormUtil.getToolbar(toolbarModel)); - + $container.append($form); + + this._paintGeneralSection($formColumn); + this._paintStorageSection($formColumn); + this._paintDataSetTypesForFileNamesSection($formColumn); + + } + + this._getSettings = function() { + return { + dataSetTypeForFileNameMap : this._datasetTypesTableModel.getValues(), + mainMenu : this._mainMenuItemsTableModel.getValues(), + forcedDisableRTF : this._forcedDisableRTFTableModel.getValues(), + forceMonospaceFont : this._forcedMonospaceTableModel.getValues(), + inventorySpaces : this._inventorySpacesTableModel.getValues(), + sampleTypeProtocols : this._sampleTypeProtocolsTableModel.getValues(), + }; + } + + // + // general + // + this._paintGeneralSection = function($container) { + var $fieldset = this._getFieldset($container, "General", "settings-section-general"); + + this._mainMenuItemsTableModel = this._getMainMenuItemsTableModel(); + $fieldset.append(this._getTable(this._mainMenuItemsTableModel)); + + this._forcedDisableRTFTableModel = this._getForcedDisableRTFTableModel(); + $fieldset.append(this._getTable(this._forcedDisableRTFTableModel)); + + this._forcedMonospaceTableModel = this._getForcedMonospaceTableModel(); + $fieldset.append(this._getTable(this._forcedMonospaceTableModel)); + + this._inventorySpacesTableModel = this._getInventorySpacesTableModel(); + $fieldset.append(this._getTable(this._inventorySpacesTableModel)); + + this._sampleTypeProtocolsTableModel = this._getSampleTypeProtocolsTableModel(); + $fieldset.append(this._getTable(this._sampleTypeProtocolsTableModel)); + } + + this._getMainMenuItemsTableModel = function() { + var tableModel = this._getTableModel(); + // define columns + tableModel.columnNames = ["Main Menu Item", "enabled"]; + tableModel.rowBuilders = { + "Main Menu Item" : function(rowData) { + return $("<span>").text(rowData.menuItemName); + }, + "enabled" : function(rowData) { + var $checkbox = $("<input>", { type : "checkbox", name : "cb" }); + if (rowData.enabled) { + $checkbox.attr("checked", true); + } + return $checkbox; + } + }; + // add data + for (var menuItemName of Object.keys(profile.mainMenu)) { + tableModel.addRow({ + menuItemName : menuItemName, + enabled : profile.mainMenu[menuItemName] }); + } + // transform output + tableModel.valuesTransformer = function(values) { + var mainMenu = {}; + for (var value of values) { + mainMenu[value["Main Menu Item"]] = value["enabled"]; + } + return mainMenu; + }; + return tableModel; + } + + this._getForcedDisableRTFTableModel = function() { + return this._getSingleColumnDropdownTableModel({ + columnName : "Forced Disable RTF", + placeholder : "select property type", + options : this._settingsFormController.getForcedDisableRTFOptions(), + initialValues : profile.forcedDisableRTF, + }); + } + + this._getForcedMonospaceTableModel = function() { + return this._getSingleColumnDropdownTableModel({ + columnName : "Forced Monospace Font", + placeholder : "select property type", + options : this._settingsFormController.getForcedMonospaceFontOptions(), + initialValues : profile.forceMonospaceFont, + }); + } + + this._getInventorySpacesTableModel = function() { + return this._getSingleColumnDropdownTableModel({ + columnName : "Inventory Spaces", + placeholder : "select space", + options : this._settingsFormController.getInventorySpacesOptions(), + initialValues : profile.inventorySpaces, + }); + } + + this._getSampleTypeProtocolsTableModel = function() { + return this._getSingleColumnDropdownTableModel({ + columnName : "Sample Type Protocols", + placeholder : "select protocol", + options : this._settingsFormController.getSampleTypeProtocolsOptions(), + initialValues : profile.sampleTypeProtocols, + }); + } + + // + // Storage + // + this._paintStorageSection = function($container) { + var $fieldset = this._getFieldset($container, "Storage", "settings-section-storage"); + + // enabled + var $checkbox = $("<input>", { type : "checkbox" }); + $checkbox.attr("checked", true); // TODO get value + $fieldset.append(this._getFormGroup($checkbox, "Enabled:")); + + this._appendSlider({ + $container : $fieldset, + value : profile.storagesConfiguration.storageSpaceLowWarning, + labelText : "Low storage space warning:", + id : "settings-slider-low-storage", + }); + + this._appendSlider({ + $container : $fieldset, + value : profile.storagesConfiguration.boxSpaceLowWarning, + labelText : "Low box space warning:", + id : "settings-slider-low-box", + }); + + // var storageProperties = this._getStorageProperties(); + + // var $storagePropertySection = $("<input>", { type : "text", class : "form-control" }); + // $storagePropertySection.val(storageProperties.STORAGE_PROPERTY_GROUP); // TODO get value + // $fieldset.append(this._getFormGroup($storagePropertySection, "Storage property section:")); + + // var $storagePropertySection = $("<input>", { type : "text", class : "form-control" }); + // $storagePropertySection.val(storageProperties.STORAGE_GROUP_DISPLAY_NAME); // TODO get value + // $fieldset.append(this._getFormGroup($storagePropertySection, "Storage section display name:")); + } + + this._getStorageProperties = function() { + var storageProperties = storagesConfiguration.STORAGE_PROPERTIES; + if (storageProperties && storageProperties === Array && storageProperties.legendText > 0) { + return storagesConfiguration.STORAGE_PROPERTIES[0]; + } + return {}; + } + + this._appendSlider = function(params) { + var $slider = $("<input>", { + class : "span2", + type : "text", + "data-slider-min" :"0", + "data-slider-max" : "100", + "data-slider-step" : "1", + "data-slider-value" : params.value * 100, + "data-slider-id" : params.id, + }); + params.$container.append(this._getFormGroup($slider, params.labelText)); + + $slider.slider({ + // tooltip : "always" + }); + + // TODO get value } + + this._getFormGroup = function($input, labelText) { + var $formGroup = $("<div>", { class : "form-group" }); + $formGroup.append($("<label>", { class : "control-label" }).text(labelText)); + var $controls = $("<div>", { class : "controls" }); + $formGroup.append($controls); + $controls.append($input); + return $formGroup; + } + + // + // dataset types for filenames + // + this._paintDataSetTypesForFileNamesSection = function($container) { + var $fieldset = this._getFieldset($container, "Dataset types for filenames", "settings-section-dataset-types"); + this._datasetTypesTableModel = this._getDatasetTypesTableModel(); + $fieldset.append(this._getTable(this._datasetTypesTableModel)); + } + + this._getDatasetTypesTableModel = function() { + var tableModel = this._getTableModel(); + tableModel.dynamicRows = true; + // define columns + tableModel.columnNames = ["Filename extension", "Dataset type"]; + tableModel.rowBuilders = { + "Filename extension" : function(rowData) { + return $("<input>", { type : "text", class : "form-control" }).val(rowData.fileNameExtension); + }, + "Dataset type" : (function(rowData) { + var allDatasetTypeCodes = this._settingsFormController.getAllDatasetTypeCodeOptions(); + return FormUtil.getDropdown(allDatasetTypeCodes.map(function(option) { + return { + label : option, + value : option, + selected : option === rowData.dataSetType, + }; + }), "select Dataset type"); + }).bind(this), + }; + // add data + for (var dataSetTypeForFileName of profile.dataSetTypeForFileNameMap) { + tableModel.addRow(dataSetTypeForFileName); + } + // transform output + tableModel.valuesTransformer = function(values) { + return values.map(function(value) { + return { + fileNameExtension : value["Filename extension"], + dataSetType : value["Dataset type"], + }; + }); + } + return tableModel; + } + + // + // general + // + + this._getSingleColumnDropdownTableModel = function(params) { + var tableModel = this._getTableModel(); + tableModel.dynamicRows = true; + // define columns + tableModel.columnNames = [params.columnName]; + tableModel.rowBuilders[params.columnName] = function(rowData) { + return FormUtil.getDropdown(params.options.map(function(option) { + return { + label : option, + value : option, + selected : option === rowData, + }; + }), params.placeholder); + } + // add data + for (var item of params.initialValues) { + tableModel.addRow(item); + } + // transform output + tableModel.valuesTransformer = function(values) { + return values.map(function(value) { + return value[params.columnName]; + }); + } + return tableModel; + } + + this._getTableModel = function() { + var tableModel = {}; + tableModel.columnNames = []; // array of strings + tableModel.rowBuilders = {}; // key (column name); value (function to build widget) + tableModel.rows = []; // array of maps with key (column name); value (widget) + tableModel.dynamicRows = false; // allows adding / removing rows + tableModel.valuesTransformer = function(values) { return values }; // optional transformer + tableModel.getValues = (function() { + var values = []; + for (var row of tableModel.rows) { + var rowValues = {}; + for (var columnName of tableModel.columnNames) { + var $widget = row[columnName]; + var value = this._getWidgetValue($widget); + rowValues[columnName] = value; + } + values.push(rowValues); + } + return tableModel.valuesTransformer(values); + }).bind(this); + tableModel.addRow = function(rowData) { + var rowWidgets = {}; + for (var columnName of tableModel.columnNames) { + var rowBuilder = tableModel.rowBuilders[columnName]; + rowWidgets[columnName] = rowBuilder(rowData); + } + tableModel.rows.push(rowWidgets); + return rowWidgets; + }; + return tableModel; + } + + this._getWidgetValue = function($widget) { + if ($widget.is("span")) { + return $widget.text(); + } else if ($widget.is("input") && $widget.attr("type") === "checkbox") { + return $widget.is(":checked"); + } else { + return $widget.val(); + } + } + + this._getTable = function(tableModel) { + var $table = $("<table>", { class : "table borderless table-compact" }); + if ( ! tableModel.dynamicRows) { + $table.css("width", "initial"); + } + // head + var $thead = $("<thead>"); + var $trHead = $("<tr>"); + for (var columnName of tableModel.columnNames) { + $trHead.append($("<th>").css("vertical-align", "middle").text(columnName)); + } + // add row button + if (tableModel.dynamicRows) { + var $addButton = $("<a>", { class : "btn btn-default" }) + .append($("<span>", { class : "glyphicon glyphicon-plus" } )); + if (this._settingsFormModel.mode === FormMode.VIEW) { + $addButton.addClass("disabled"); + } else { + $addButton.on("click", (function() { + var rowWidgets = tableModel.addRow({}); + this._addRow($tbody, tableModel, rowWidgets); + }).bind(this)) + } + $trHead.append($("<th>").append($addButton)); + } + $thead.append($trHead); + $table.append($thead); + // body + var $tbody = $("<tbody>"); + for (var row of tableModel.rows) { + this._addRow($tbody, tableModel, row); + } + $table.append($tbody); + return $table + } + + this._addRow = function($tbody, tableModel, tableModelRow) { + var $tr = $("<tr>"); + $tbody.append($tr); + for (var columnName of tableModel.columnNames) { + var $td = $("<td>"); + $tr.append($td); + var $widget = tableModelRow[columnName]; + $td.append($widget); + // disbale widget if in view mode + if (this._settingsFormModel.mode === FormMode.VIEW) { + $widget.prop("disabled", true); + } + this._styleWidget($widget); + } + // remove row button if in edit mode + if (tableModel.dynamicRows) { + $removeButton = $("<a>", { class : "btn btn-default" }) + .append($("<span>", { class : "glyphicon glyphicon-minus" })); + if (this._settingsFormModel.mode === FormMode.VIEW) { + $removeButton.addClass("disabled"); + } else { + $removeButton.on("click", function() { + $tr.remove(); + var rowIndex = tableModel.rows.indexOf(tableModelRow); + tableModel.rows.splice(rowIndex, 1); + }); + } + $tr.append($("<td>").append($removeButton)); + } + } + + this._styleWidget = function($widget) { + if ($widget.is("select")) { + $widget.select2({ width: '100%', theme: "bootstrap" }) + } else if ($widget.is("input") && $widget.attr("type") === "checkbox") { + // TODO avoid flickering + // $widget.bootstrapSwitch(); + } + } + + this._getFieldset = function($container, legendText, key) { + var $fieldsetOwner = $("<div>"); + var $fieldset = $("<div>"); + var $legend = $("<legend>").text(legendText); + $legend.prepend(FormUtil.getShowHideButton($fieldset, key)); + $fieldsetOwner.append($legend).append($fieldset); + $container.append($fieldsetOwner); + return $fieldset; + } + } \ No newline at end of file