From 27d5fb88fa9e0d2d941d3681e1f3363e703068a7 Mon Sep 17 00:00:00 2001 From: Mathieu Saby Date: Sun, 4 Aug 2019 14:37:36 +0200 Subject: [PATCH 1/4] Init commit --- .../webapp/modules/core/MOD-INF/controller.js | 1 + .../modules/core/langs/translation-en.json | 13 ++ .../scripts/views/data-table/column-join.html | 60 +++++ .../views/data-table/menu-edit-column.js | 206 +++++++++++++++++- .../core/styles/views/column-join.less | 84 +++++++ 5 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 main/webapp/modules/core/scripts/views/data-table/column-join.html create mode 100644 main/webapp/modules/core/styles/views/column-join.less diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js index 226e77d33..cae639734 100644 --- a/main/webapp/modules/core/MOD-INF/controller.js +++ b/main/webapp/modules/core/MOD-INF/controller.js @@ -516,6 +516,7 @@ function init() { "styles/widgets/slider-widget.less", "styles/views/data-table-view.less", + "styles/views/column-join.less", "styles/dialogs/expression-preview-dialog.less", "styles/dialogs/clustering-dialog.less", diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index 39a732d5e..f912b5daf 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -550,6 +550,7 @@ "core-views/url-fetch": "Formulate the URLs to fetch:", "core-views/http-headers": "HTTP headers to be used when fetching URLs:", "core-views/enter-col-name": "Enter new column name", + "core-views/join-col":"Join columns", "core-views/split-col": "Split column", "core-views/several-col": "into several columns", "core-views/how-split": "How to Split Column", @@ -680,6 +681,18 @@ "core-views/use-values-as-identifiers2": "Mark cells as reconciled with their values as identifiers", "core-views/choose-reconciliation-service": "Choose a reconciliation service", "core-views/choose-reconciliation-service-alert": "Please choose a reconciliation service first.", + "core-views/column-join": "Join columns", + "core-views/column-join-before-column-picker": "Select and order columns to join", + "core-views/column-join-before-options": "Select options", + "core-views/column-join-field-separator": "Separator between the content of each column :", + "core-views/column-join-field-separator-advice": "Enter one or more character, or keep blank to juxtapose the columns.", + "core-views/column-join-replace-nulls": "Replace nulls with...", + "core-views/column-join-skip-nulls": "Skip nulls.", + "core-views/column-join-replace-nulls-advice": "Enter one or more character, or keep blank to replace nulls with blank strings.", + "core-views/column-join-skip-nulls-advice": "Skipping nulls will make impossible the reverse the join.", + "core-views/column-join-write-active-column": "Write result in active column.", + "core-views/column-join-copy-to-new-column": "Write result in new column named...", + "core-views/column-join-delete-joined-columns": "Delete joined columns.", "core-buttons/cancel": "Cancel", "core-buttons/ok": "  OK  ", "core-buttons/import-proj": "Import Project", diff --git a/main/webapp/modules/core/scripts/views/data-table/column-join.html b/main/webapp/modules/core/scripts/views/data-table/column-join.html new file mode 100644 index 000000000..753c09181 --- /dev/null +++ b/main/webapp/modules/core/scripts/views/data-table/column-join.html @@ -0,0 +1,60 @@ +
+
+
+
+
+

+
+
+
+ + +
+
+
+
+

+
+
+ + +

+
+
+
+ + + +

+ +
+
+ + +

+
+
+
+
+ + +
+
+ + + +
+
+
+ + +
+
+
+
+
+ +
\ No newline at end of file diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js index 845ab5c3f..e68ec1e9b 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js @@ -33,6 +33,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var columnIndex = Refine.columnNameToColumnIndex(column.name); + var doTextTransform = function(columnName, expression, onError, repeat, repeatCount) { + Refine.postCoreProcess( + "text-transform", + { + columnName: columnName, + onError: onError, + repeat: repeat, + repeatCount: repeatCount + }, + { expression: expression }, + { cellsChanged: true } + ); + }; + var doAddColumn = function() { var frame = $( DOM.loadHTML("core", "scripts/views/data-table/add-column-dialog.html") @@ -320,14 +334,204 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { ); dismiss(); }); + }; + + var doJoinColumns = function() { + var self = this; + self.onError = "keep-original" ; + self.repeat = false ; + self.repeatCount = ""; + /* + * Close the dialog window + */ + var dismiss = function() { + DialogSystem.dismissUntil(self.level - 1); + }; + /* + * Join the columns according to user input + */ + var transform = function() { + // get options + self.deleteJoinedColumns = self.elmts.delete_joined_columnsInput[0].checked; + self.writeOrCopy = $("input[name='write-or-copy']:checked")[0].value; + self.newColumnName = self.elmts.new_column_nameInput[0].value; + self.manageNulls = $("input[name='manage-nulls']:checked")[0].value; + self.nullSubstitute = self.elmts.null_substituteInput[0].value; + self.fieldSeparator = self.elmts.field_separatorInput[0].value; + // correct options if they are not consistent + if (self.newColumnName != "") { + self.writeOrCopy ="copy-to-new-column"; + } else + { + self.writeOrCopy ="write-active-column"; + }; + if (self.nullSubstitute != "") { + self.manageNulls ="replace-nulls"; + }; + // build GREL expression + var columnsToJoin = []; + self.elmts.column_join_columnPicker + .find('.column-join-column input[type="checkbox"]:checked') + .each(function() { + columnsToJoin.push (this.closest ('.column-join-column').getAttribute('column')); + }); + self.expression = columnsToJoin.map (function (colName) { + if (self.manageNulls == "skip-nulls") { + return "cells['"+colName+"'].value"; + } + else { + return "coalesce(cells['"+colName+"'].value, '"+self.nullSubstitute+ "')"; + } + }).join (','); + self.expression = 'join ([' + self.expression + '],"' + self.fieldSeparator + '")'; + // apply expression to active column or new column + if (self.writeOrCopy =="copy-to-new-column") { + Refine.postCoreProcess( + "add-column", + { + baseColumnName: column.name, + newColumnName: self.newColumnName, + columnInsertIndex: columnIndex + 1, + onError: self.onError + }, + { expression: self.expression }, + { modelsChanged: true } + ); + } + else { + doTextTransform(column.name, self.expression, self.onError, self.repeat, self.repeatCount); + } + // delete joined columns + if (self.deleteJoinedColumns) { + // do not delete the active cell if it contains the result + if (self.writeOrCopy !="copy-to-new-column") { + columnsToJoin = columnsToJoin.filter(function(item) { + return item !== column.name + }); + } + columnsToJoin.forEach (function (columnName) { + // FIXME ERREUR dans Refine.postProcess (project.js l. 359) : + // Index: 5, Size: 5 + // Index: 6, Size: 5 + // No column named e + Refine.postCoreProcess( + "remove-column", + { + columnName: columnName + }, + null, + { modelsChanged: true } + ); + }); + } + }; + // core of doJoinColumn + self.dialog = $(DOM.loadHTML("core","scripts/views/data-table/column-join.html")); + self.elmts = DOM.bind(self.dialog); + self.level = DialogSystem.showDialog(self.dialog); + self.elmts.dialogHeader.text($.i18n('core-views/column-join')); + self.elmts.or_views_column_join_before_column_picker.text($.i18n('core-views/column-join-before-column-picker')); + self.elmts.or_views_column_join_before_options.text($.i18n('core-views/column-join-before-options')); + self.elmts.or_views_column_join_replace_nulls.text($.i18n('core-views/column-join-replace-nulls')); + self.elmts.or_views_column_join_replace_nulls_advice.text($.i18n('core-views/column-join-replace-nulls-advice')); + self.elmts.or_views_column_join_skip_nulls.text($.i18n('core-views/column-join-skip-nulls')); + self.elmts.or_views_column_join_skip_nulls_advice.text($.i18n('core-views/column-join-skip-nulls-advice')); + self.elmts.or_views_column_join_write_active_column.text($.i18n('core-views/column-join-write-active-column')); + self.elmts.or_views_column_join_copy_to_new_column.text($.i18n('core-views/column-join-copy-to-new-column')); + self.elmts.or_views_column_join_delete_joined_columns.text($.i18n('core-views/column-join-delete-joined-columns')); + self.elmts.or_views_column_join_field_separator.text($.i18n('core-views/column-join-field-separator')); + self.elmts.or_views_column_join_field_separator_advice.text($.i18n('core-views/column-join-field-separator-advice')); + self.elmts.selectAllButton.html($.i18n('core-buttons/select-all')); + self.elmts.deselectAllButton.html($.i18n('core-buttons/deselect-all')); + self.elmts.okButton.html($.i18n('core-buttons/ok')); + self.elmts.cancelButton.html($.i18n('core-buttons/cancel')); + /* + * Populate column list. + */ + for (var i = 0; i < theProject.columnModel.columns.length; i++) { + var col = theProject.columnModel.columns[i]; + var colName = col.name; + var div = $('
'). + addClass("column-join-column") + .attr("column", colName) + .appendTo(this.elmts.column_join_columnPicker); + $(''). + attr('type', 'checkbox') + .prop('checked',(i == columnIndex) ? true : false) + .appendTo(div); + $('') + .text(colName) + .appendTo(div); + } + // Move the active column on the top of the list + if (columnIndex > 0) { + activeColumn = self.elmts.column_join_columnPicker + .find('.column-join-column') + .eq(columnIndex); + activeColumn.parent().prepend(activeColumn); + } + // Make the list sortable + self.elmts.column_join_columnPicker.sortable({}); + /* + * Hook up event handlers. + */ + self.elmts.column_join_columnPicker + .find('.column-join-column') + .click(function() { + self.elmts.column_join_columnPicker + .find('.column-join-column') + .removeClass('selected'); + $(this).addClass('selected'); + }); + self.elmts.selectAllButton + .click(function() { + self.elmts.column_join_columnPicker + .find('input[type="checkbox"]') + .prop('checked',true); + }); + self.elmts.deselectAllButton + .click(function() { + self.elmts.column_join_columnPicker + .find('input[type="checkbox"]') + .prop('checked',false); + }); + self.elmts.okButton.click(function() { + transform(); + dismiss(); + }); + self.elmts.cancelButton.click(function() { + dismiss(); + }); + self.elmts.new_column_nameInput.change(function() { + if (self.elmts.new_column_nameInput[0].value != "") { + self.elmts.copy_to_new_columnInput.prop('checked',true); + } else + { + self.elmts.write_active_columnInput.prop('checked',true); + }; + }); + self.elmts.null_substituteInput.change(function() { + self.elmts.replace_nullsInput.prop('checked',true); + }); + + + }; +/* + * Create global menu + */ MenuSystem.appendTo(menu, [ "core/edit-column" ], [ { id: "core/split-column", label: $.i18n('core-views/split-into-col')+"...", click: doSplitColumn }, + { + id: "core/join-column", + label: $.i18n('core-views/join-col')+"...", + click : doJoinColumns + }, {}, { id: "core/add-column", @@ -369,7 +573,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { { id: "core/move-column-to-left", label: $.i18n('core-views/move-to-left'), - click: function() { doMoveColumnBy(-1); } + click: function() { doMoveColumnBy(-1);} }, { id: "core/move-column-to-right", diff --git a/main/webapp/modules/core/styles/views/column-join.less b/main/webapp/modules/core/styles/views/column-join.less new file mode 100644 index 000000000..08e622230 --- /dev/null +++ b/main/webapp/modules/core/styles/views/column-join.less @@ -0,0 +1,84 @@ +/* + +Copyright 2019, Mathieu Saby. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* +Same properties as dialogs/custom-tabular-exporter-dialog.less, applied to the class and ids of colum-join.html +*/ + +@import-less url("../theme.less"); + +.dialog-header,.dialog_body,.dialog-footer,.global_selection { + clear:both; +} + +.column-join-left-pane { + float:left; + width : 40%; +} + +.column-join-inner-left-pane,.column-join-inner-right-pane { + border: 1px solid @chrome_primary; + height: 24em; + padding: @padding_loose; + } + +.column-join-inner-left-pane-list { + overflow: auto; + height:22em; + } + + +.column-join-global-selection { + margin-top:0.5em; +} + +.column-join-column { + border: 1px solid @chrome_primary; + background: @fill_secondary; + padding: @padding_tighter; + margin: @padding_tight; + cursor: move; + .rounded_corners(); + } +.column-join-column.selected { + background: @chrome_primary; + font-weight: bold; + } + +.column-join-inner-right-pane .option { + margin-top:1em; +} + +.column-join-inner-right-pane .tip,.column-join-inner-left-pane .tip{ + font-size:0.9em; +} From 7836e5b2dbba6cb6d96e8391a07aa35bba2252a3 Mon Sep 17 00:00:00 2001 From: Mathieu Saby Date: Wed, 7 Aug 2019 23:20:28 +0200 Subject: [PATCH 2/4] Fix grammar and use callbacks --- .../modules/core/langs/translation-en.json | 7 +- .../scripts/views/data-table/column-join.html | 4 +- .../views/data-table/menu-edit-column.js | 198 +++++++++--------- 3 files changed, 107 insertions(+), 102 deletions(-) diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index f912b5daf..f4b9128a6 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -685,12 +685,11 @@ "core-views/column-join-before-column-picker": "Select and order columns to join", "core-views/column-join-before-options": "Select options", "core-views/column-join-field-separator": "Separator between the content of each column :", - "core-views/column-join-field-separator-advice": "Enter one or more character, or keep blank to juxtapose the columns.", + "core-views/column-join-field-separator-advice": "Enter one or more characters, or keep blank to join the columns without separator.", "core-views/column-join-replace-nulls": "Replace nulls with...", "core-views/column-join-skip-nulls": "Skip nulls.", - "core-views/column-join-replace-nulls-advice": "Enter one or more character, or keep blank to replace nulls with blank strings.", - "core-views/column-join-skip-nulls-advice": "Skipping nulls will make impossible the reverse the join.", - "core-views/column-join-write-active-column": "Write result in active column.", + "core-views/column-join-replace-nulls-advice": "Enter one or more characters, or keep blank to replace nulls with blank strings.", + "core-views/column-join-write-selected-column": "Write result in selected column.", "core-views/column-join-copy-to-new-column": "Write result in new column named...", "core-views/column-join-delete-joined-columns": "Delete joined columns.", "core-buttons/cancel": "Cancel", diff --git a/main/webapp/modules/core/scripts/views/data-table/column-join.html b/main/webapp/modules/core/scripts/views/data-table/column-join.html index 753c09181..2d5058e72 100644 --- a/main/webapp/modules/core/scripts/views/data-table/column-join.html +++ b/main/webapp/modules/core/scripts/views/data-table/column-join.html @@ -36,8 +36,8 @@
- - + +
diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js index e68ec1e9b..a90200614 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js @@ -33,7 +33,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var columnIndex = Refine.columnNameToColumnIndex(column.name); - var doTextTransform = function(columnName, expression, onError, repeat, repeatCount) { + var doTextTransform = function(columnName, expression, onError, repeat, repeatCount, callbacks) { + callbacks = callbacks || {}; Refine.postCoreProcess( "text-transform", { @@ -43,7 +44,8 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { repeatCount: repeatCount }, { expression: expression }, - { cellsChanged: true } + { cellsChanged: true }, + callbacks ); }; @@ -338,113 +340,120 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var doJoinColumns = function() { var self = this; - self.onError = "keep-original" ; - self.repeat = false ; - self.repeatCount = ""; + var dialog = $(DOM.loadHTML("core","scripts/views/data-table/column-join.html")); + var elmts = DOM.bind(dialog); + var level = DialogSystem.showDialog(dialog); /* * Close the dialog window */ var dismiss = function() { - DialogSystem.dismissUntil(self.level - 1); + DialogSystem.dismissUntil(level - 1); }; /* * Join the columns according to user input */ var transform = function() { + // function called in a callback + var deleteColumns = function() { + if (deleteJoinedColumns) { + // do not delete the selected cell if it contains the result + if (writeOrCopy !="copy-to-new-column") { + columnsToJoin = columnsToJoin.filter(function(item) { + return item !== column.name + }); + } + var columnName = ""; + var onJoinError = function(e) { + alert (e); + } + var recursiveRemoval = function() { + if (columnsToJoin.length > 0) { + columnName = columnsToJoin.shift(); + Refine.postCoreProcess( + "remove-column", + { + columnName: columnName + }, + null, + { modelsChanged: true }, + { onDone:recursiveRemoval, onError:onJoinError } + ); + } + } + recursiveRemoval (); + } + }; // get options - self.deleteJoinedColumns = self.elmts.delete_joined_columnsInput[0].checked; - self.writeOrCopy = $("input[name='write-or-copy']:checked")[0].value; - self.newColumnName = self.elmts.new_column_nameInput[0].value; - self.manageNulls = $("input[name='manage-nulls']:checked")[0].value; - self.nullSubstitute = self.elmts.null_substituteInput[0].value; - self.fieldSeparator = self.elmts.field_separatorInput[0].value; + var onError = "keep-original" ; + var repeat = false ; + var repeatCount = ""; + var deleteJoinedColumns = elmts.delete_joined_columnsInput[0].checked; + var writeOrCopy = $("input[name='write-or-copy']:checked")[0].value; + var newColumnName = elmts.new_column_nameInput[0].value; + var manageNulls = $("input[name='manage-nulls']:checked")[0].value; + var nullSubstitute = elmts.null_substituteInput[0].value; + var fieldSeparator = elmts.field_separatorInput[0].value; // correct options if they are not consistent - if (self.newColumnName != "") { - self.writeOrCopy ="copy-to-new-column"; + if (newColumnName != "") { + writeOrCopy ="copy-to-new-column"; } else { - self.writeOrCopy ="write-active-column"; - }; - if (self.nullSubstitute != "") { - self.manageNulls ="replace-nulls"; - }; + writeOrCopy ="write-selected-column"; + } + if (nullSubstitute != "") { + manageNulls ="replace-nulls"; + } // build GREL expression var columnsToJoin = []; - self.elmts.column_join_columnPicker + elmts.column_join_columnPicker .find('.column-join-column input[type="checkbox"]:checked') .each(function() { columnsToJoin.push (this.closest ('.column-join-column').getAttribute('column')); }); - self.expression = columnsToJoin.map (function (colName) { - if (self.manageNulls == "skip-nulls") { + expression = columnsToJoin.map (function (colName) { + if (manageNulls == "skip-nulls") { return "cells['"+colName+"'].value"; } else { - return "coalesce(cells['"+colName+"'].value, '"+self.nullSubstitute+ "')"; + return "coalesce(cells['"+colName+"'].value, '"+nullSubstitute+ "')"; } }).join (','); - self.expression = 'join ([' + self.expression + '],"' + self.fieldSeparator + '")'; - // apply expression to active column or new column - if (self.writeOrCopy =="copy-to-new-column") { + expression = 'join ([' + expression + '],"' + fieldSeparator + '")'; + // apply expression to selected column or new column + if (writeOrCopy =="copy-to-new-column") { Refine.postCoreProcess( "add-column", { baseColumnName: column.name, - newColumnName: self.newColumnName, + newColumnName: newColumnName, columnInsertIndex: columnIndex + 1, - onError: self.onError + onError: onError }, - { expression: self.expression }, - { modelsChanged: true } + { expression: expression }, + { modelsChanged: true }, + { onDone: deleteColumns} ); } else { - doTextTransform(column.name, self.expression, self.onError, self.repeat, self.repeatCount); + doTextTransform(column.name, expression, onError, repeat, repeatCount,{onDone: deleteColumns}); } - // delete joined columns - if (self.deleteJoinedColumns) { - // do not delete the active cell if it contains the result - if (self.writeOrCopy !="copy-to-new-column") { - columnsToJoin = columnsToJoin.filter(function(item) { - return item !== column.name - }); - } - columnsToJoin.forEach (function (columnName) { - // FIXME ERREUR dans Refine.postProcess (project.js l. 359) : - // Index: 5, Size: 5 - // Index: 6, Size: 5 - // No column named e - Refine.postCoreProcess( - "remove-column", - { - columnName: columnName - }, - null, - { modelsChanged: true } - ); - }); - } }; // core of doJoinColumn - self.dialog = $(DOM.loadHTML("core","scripts/views/data-table/column-join.html")); - self.elmts = DOM.bind(self.dialog); - self.level = DialogSystem.showDialog(self.dialog); - self.elmts.dialogHeader.text($.i18n('core-views/column-join')); - self.elmts.or_views_column_join_before_column_picker.text($.i18n('core-views/column-join-before-column-picker')); - self.elmts.or_views_column_join_before_options.text($.i18n('core-views/column-join-before-options')); - self.elmts.or_views_column_join_replace_nulls.text($.i18n('core-views/column-join-replace-nulls')); - self.elmts.or_views_column_join_replace_nulls_advice.text($.i18n('core-views/column-join-replace-nulls-advice')); - self.elmts.or_views_column_join_skip_nulls.text($.i18n('core-views/column-join-skip-nulls')); - self.elmts.or_views_column_join_skip_nulls_advice.text($.i18n('core-views/column-join-skip-nulls-advice')); - self.elmts.or_views_column_join_write_active_column.text($.i18n('core-views/column-join-write-active-column')); - self.elmts.or_views_column_join_copy_to_new_column.text($.i18n('core-views/column-join-copy-to-new-column')); - self.elmts.or_views_column_join_delete_joined_columns.text($.i18n('core-views/column-join-delete-joined-columns')); - self.elmts.or_views_column_join_field_separator.text($.i18n('core-views/column-join-field-separator')); - self.elmts.or_views_column_join_field_separator_advice.text($.i18n('core-views/column-join-field-separator-advice')); - self.elmts.selectAllButton.html($.i18n('core-buttons/select-all')); - self.elmts.deselectAllButton.html($.i18n('core-buttons/deselect-all')); - self.elmts.okButton.html($.i18n('core-buttons/ok')); - self.elmts.cancelButton.html($.i18n('core-buttons/cancel')); + elmts.dialogHeader.text($.i18n('core-views/column-join')); + elmts.or_views_column_join_before_column_picker.text($.i18n('core-views/column-join-before-column-picker')); + elmts.or_views_column_join_before_options.text($.i18n('core-views/column-join-before-options')); + elmts.or_views_column_join_replace_nulls.text($.i18n('core-views/column-join-replace-nulls')); + elmts.or_views_column_join_replace_nulls_advice.text($.i18n('core-views/column-join-replace-nulls-advice')); + elmts.or_views_column_join_skip_nulls.text($.i18n('core-views/column-join-skip-nulls')); + elmts.or_views_column_join_write_selected_column.text($.i18n('core-views/column-join-write-selected-column')); + elmts.or_views_column_join_copy_to_new_column.text($.i18n('core-views/column-join-copy-to-new-column')); + elmts.or_views_column_join_delete_joined_columns.text($.i18n('core-views/column-join-delete-joined-columns')); + elmts.or_views_column_join_field_separator.text($.i18n('core-views/column-join-field-separator')); + elmts.or_views_column_join_field_separator_advice.text($.i18n('core-views/column-join-field-separator-advice')); + elmts.selectAllButton.html($.i18n('core-buttons/select-all')); + elmts.deselectAllButton.html($.i18n('core-buttons/deselect-all')); + elmts.okButton.html($.i18n('core-buttons/ok')); + elmts.cancelButton.html($.i18n('core-buttons/cancel')); /* * Populate column list. */ @@ -454,7 +463,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var div = $('
'). addClass("column-join-column") .attr("column", colName) - .appendTo(this.elmts.column_join_columnPicker); + .appendTo(elmts.column_join_columnPicker); $(''). attr('type', 'checkbox') .prop('checked',(i == columnIndex) ? true : false) @@ -463,59 +472,56 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { .text(colName) .appendTo(div); } - // Move the active column on the top of the list + // Move the selected column on the top of the list if (columnIndex > 0) { - activeColumn = self.elmts.column_join_columnPicker + selectedColumn = elmts.column_join_columnPicker .find('.column-join-column') .eq(columnIndex); - activeColumn.parent().prepend(activeColumn); + selectedColumn.parent().prepend(selectedColumn); } // Make the list sortable - self.elmts.column_join_columnPicker.sortable({}); + elmts.column_join_columnPicker.sortable({}); /* * Hook up event handlers. */ - self.elmts.column_join_columnPicker + elmts.column_join_columnPicker .find('.column-join-column') .click(function() { - self.elmts.column_join_columnPicker + elmts.column_join_columnPicker .find('.column-join-column') .removeClass('selected'); $(this).addClass('selected'); }); - self.elmts.selectAllButton + elmts.selectAllButton .click(function() { - self.elmts.column_join_columnPicker + elmts.column_join_columnPicker .find('input[type="checkbox"]') .prop('checked',true); }); - self.elmts.deselectAllButton + elmts.deselectAllButton .click(function() { - self.elmts.column_join_columnPicker + elmts.column_join_columnPicker .find('input[type="checkbox"]') .prop('checked',false); }); - self.elmts.okButton.click(function() { + elmts.okButton.click(function() { transform(); dismiss(); }); - self.elmts.cancelButton.click(function() { + elmts.cancelButton.click(function() { dismiss(); }); - self.elmts.new_column_nameInput.change(function() { - if (self.elmts.new_column_nameInput[0].value != "") { - self.elmts.copy_to_new_columnInput.prop('checked',true); + elmts.new_column_nameInput.change(function() { + if (elmts.new_column_nameInput[0].value != "") { + elmts.copy_to_new_columnInput.prop('checked',true); } else { - self.elmts.write_active_columnInput.prop('checked',true); - }; + elmts.write_selected_columnInput.prop('checked',true); + } }); - self.elmts.null_substituteInput.change(function() { - self.elmts.replace_nullsInput.prop('checked',true); + elmts.null_substituteInput.change(function() { + elmts.replace_nullsInput.prop('checked',true); }); - - - }; /* From f0616eff790ffe2972fb4ed23c855f8c26ef6580 Mon Sep 17 00:00:00 2001 From: Mathieu Saby Date: Fri, 9 Aug 2019 02:03:58 +0200 Subject: [PATCH 3/4] Remove all columns at once and escape input --- .../modules/core/langs/translation-en.json | 1 + .../scripts/views/data-table/column-join.html | 6 +- .../views/data-table/menu-edit-column.js | 96 +++++++++++-------- 3 files changed, 62 insertions(+), 41 deletions(-) diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index f4b9128a6..edf25b8f3 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -692,6 +692,7 @@ "core-views/column-join-write-selected-column": "Write result in selected column.", "core-views/column-join-copy-to-new-column": "Write result in new column named...", "core-views/column-join-delete-joined-columns": "Delete joined columns.", + "core-views/column-join-dont-escape": "In separator and nulls substitutes, use \\n for new lines, \\t for tabulation, \\\\n for \\n, \\\\t for \\t.", "core-buttons/cancel": "Cancel", "core-buttons/ok": "  OK  ", "core-buttons/import-proj": "Import Project", diff --git a/main/webapp/modules/core/scripts/views/data-table/column-join.html b/main/webapp/modules/core/scripts/views/data-table/column-join.html index 2d5058e72..275bb7231 100644 --- a/main/webapp/modules/core/scripts/views/data-table/column-join.html +++ b/main/webapp/modules/core/scripts/views/data-table/column-join.html @@ -34,6 +34,10 @@

+
+ + +
@@ -48,7 +52,7 @@
-
+
diff --git a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js index a90200614..676ced7ab 100644 --- a/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js +++ b/main/webapp/modules/core/scripts/views/data-table/menu-edit-column.js @@ -343,44 +343,52 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var dialog = $(DOM.loadHTML("core","scripts/views/data-table/column-join.html")); var elmts = DOM.bind(dialog); var level = DialogSystem.showDialog(dialog); - /* - * Close the dialog window - */ + // Escape strings + function escapeString(s,dontEscape) { + var dontEscape = dontEscape || false; + var temp = s; + if (dontEscape) { + // replace "\n" with newline and "\t" with tab + temp = temp.replace(/\\n/g, '\n').replace(/\\t/g, '\t'); + // replace "\" with "\\" + temp = temp.replace(/\\/g, '\\\\'); + // replace "\newline" with "\n" and "\tab" with "\t" + temp = temp.replace(/\\\n/g, '\\n').replace(/\\\t/g, '\\t'); + // replace ' with \' + temp = temp.replace(/'/g, "\\'"); + } + else { + // escape \ and ' + temp = s.replace(/\\/g, '\\\\').replace(/'/g, "\\'") ; + // useless : .replace(/"/g, '\\"') + } + return temp; + }; + // Close the dialog window var dismiss = function() { DialogSystem.dismissUntil(level - 1); }; - /* - * Join the columns according to user input - */ + // Join the columns according to user input var transform = function() { // function called in a callback var deleteColumns = function() { if (deleteJoinedColumns) { - // do not delete the selected cell if it contains the result - if (writeOrCopy !="copy-to-new-column") { - columnsToJoin = columnsToJoin.filter(function(item) { - return item !== column.name - }); - } - var columnName = ""; - var onJoinError = function(e) { - alert (e); - } - var recursiveRemoval = function() { - if (columnsToJoin.length > 0) { - columnName = columnsToJoin.shift(); - Refine.postCoreProcess( - "remove-column", - { - columnName: columnName - }, - null, - { modelsChanged: true }, - { onDone:recursiveRemoval, onError:onJoinError } - ); - } - } - recursiveRemoval (); + console.log (theProject); + var columnsToKeep = theProject.columnModel.columns + .map (function (col) {return col.name;}) + .filter (function(colName) { + // keep the selected column if it contains the result + return ( + (columnsToJoin.indexOf (colName) == -1) || + ((writeOrCopy !="copy-to-new-column") && (colName == column.name))); + }); + Refine.postCoreProcess( + "reorder-columns", + null, + { "columnNames" : JSON.stringify(columnsToKeep) }, + { modelsChanged: true }, + { includeEngine: false } + ); } }; // get options @@ -389,11 +397,12 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { var repeatCount = ""; var deleteJoinedColumns = elmts.delete_joined_columnsInput[0].checked; var writeOrCopy = $("input[name='write-or-copy']:checked")[0].value; - var newColumnName = elmts.new_column_nameInput[0].value; + var newColumnName = $.trim(elmts.new_column_nameInput[0].value); var manageNulls = $("input[name='manage-nulls']:checked")[0].value; var nullSubstitute = elmts.null_substituteInput[0].value; var fieldSeparator = elmts.field_separatorInput[0].value; - // correct options if they are not consistent + var dontEscape = elmts.dont_escapeInput[0].checked; + // fix options if they are not consistent if (newColumnName != "") { writeOrCopy ="copy-to-new-column"; } else @@ -402,7 +411,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { } if (nullSubstitute != "") { manageNulls ="replace-nulls"; - } + } // build GREL expression var columnsToJoin = []; elmts.column_join_columnPicker @@ -412,13 +421,13 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { }); expression = columnsToJoin.map (function (colName) { if (manageNulls == "skip-nulls") { - return "cells['"+colName+"'].value"; + return "cells['"+escapeString(colName) +"'].value"; } - else { - return "coalesce(cells['"+colName+"'].value, '"+nullSubstitute+ "')"; + else { + return "coalesce(cells['"+escapeString(colName)+"'].value,'"+ escapeString(nullSubstitute,dontEscape) + "')"; } }).join (','); - expression = 'join ([' + expression + '],"' + fieldSeparator + '")'; + expression = 'join ([' + expression + '],\'' + escapeString(fieldSeparator,dontEscape) + "')"; // apply expression to selected column or new column if (writeOrCopy =="copy-to-new-column") { Refine.postCoreProcess( @@ -431,11 +440,17 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { }, { expression: expression }, { modelsChanged: true }, - { onDone: deleteColumns} + { onFinallyDone: deleteColumns} ); } else { - doTextTransform(column.name, expression, onError, repeat, repeatCount,{onDone: deleteColumns}); + doTextTransform( + column.name, + expression, + onError, + repeat, + repeatCount, + { onFinallyDone: deleteColumns}); } }; // core of doJoinColumn @@ -450,6 +465,7 @@ DataTableColumnHeaderUI.extendMenu(function(column, columnHeaderUI, menu) { elmts.or_views_column_join_delete_joined_columns.text($.i18n('core-views/column-join-delete-joined-columns')); elmts.or_views_column_join_field_separator.text($.i18n('core-views/column-join-field-separator')); elmts.or_views_column_join_field_separator_advice.text($.i18n('core-views/column-join-field-separator-advice')); + elmts.or_views_column_join_dont_escape.text($.i18n('core-views/column-join-dont-escape')); elmts.selectAllButton.html($.i18n('core-buttons/select-all')); elmts.deselectAllButton.html($.i18n('core-buttons/deselect-all')); elmts.okButton.html($.i18n('core-buttons/ok')); From 328df189ec7c2042a9b04e29934f2f232b7ffd18 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Fri, 16 Aug 2019 13:31:23 +0100 Subject: [PATCH 4/4] Add margins in Join columns dialog --- main/webapp/modules/core/styles/views/column-join.less | 1 + 1 file changed, 1 insertion(+) diff --git a/main/webapp/modules/core/styles/views/column-join.less b/main/webapp/modules/core/styles/views/column-join.less index 08e622230..bc6187125 100644 --- a/main/webapp/modules/core/styles/views/column-join.less +++ b/main/webapp/modules/core/styles/views/column-join.less @@ -44,6 +44,7 @@ Same properties as dialogs/custom-tabular-exporter-dialog.less, applied to the c .column-join-left-pane { float:left; width : 40%; + margin-right: 10px; } .column-join-inner-left-pane,.column-join-inner-right-pane {