diff --git a/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js b/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js index faad731..e043a44 100644 --- a/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js +++ b/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js @@ -113,10 +113,10 @@ var col = args.column; if (_.isUndefined(item[args.column.pos]) && col.has_default_val) { - $input.val(""); + $input.val(defaultValue = ""); } else if (item[args.column.pos] === "") { - $input.val("''"); + $input.val(defaultValue = "''"); } else { $input.val(defaultValue = item[args.column.pos]); @@ -146,7 +146,32 @@ }; this.applyValue = function (item, state) { - item[args.column.pos] = state; + var grid = args.grid; + if (!grid.copied_rows) { + grid.copied_rows = [[]]; + } + + var active_cell = grid.getActiveCell(), + row = active_cell['row'], + cell = active_cell['cell'], + last_value = item[args.column.pos]; + + // If a row is copied + if (item.is_row_copied) { + item[args.column.pos] = state; + + if ((_.isEmpty(last_value) || last_value) && _.isNull(state) && + (_.isUndefined(grid.copied_rows[row]) || + _.isUndefined(grid.copied_rows[row][cell])) + ) { + item[args.column.pos] = undefined; + if (grid.copied_rows[row] == undefined) grid.copied_rows[row] = []; + grid.copied_rows[row][cell] = 1; + } + } + else { + item[args.column.pos] = state; + } }; this.isValueChanged = function () { @@ -290,7 +315,32 @@ }; this.applyValue = function (item, state) { - item[args.column.pos] = state; + var grid = args.grid; + if (!grid.copied_rows) { + grid.copied_rows = [[]]; + } + + var active_cell = grid.getActiveCell(), + row = active_cell['row'], + cell = active_cell['cell'], + last_value = item[args.column.pos]; + + // If a row is copied + if (item.is_row_copied) { + item[args.column.pos] = state; + + if ((_.isEmpty(last_value) || last_value) && _.isNull(state) && + (_.isUndefined(grid.copied_rows[row]) || + _.isUndefined(grid.copied_rows[row][cell])) + ) { + item[args.column.pos] = undefined; + if (grid.copied_rows[row] == undefined) grid.copied_rows[row] = []; + grid.copied_rows[row][cell] = 1; + } + } + else { + item[args.column.pos] = state; + } }; this.isValueChanged = function () { @@ -855,7 +905,38 @@ }; this.applyValue = function (item, state) { - item[args.column.pos] = state; + + // Handling of [default] and [null] values for copied rows + // declare a 2-d array which tracks the status of each updated cell + // If a cell is edited for the 1st time and state is null, + // set cell value to [default] and update its status [row][cell] to 1. + // If same cell is edited again, and kept blank, set cell value to [null] + var grid = args.grid; + if (!grid.copied_rows) { + grid.copied_rows = [[]]; + } + + var active_cell = grid.getActiveCell(), + row = active_cell['row'], + cell = active_cell['cell'], + last_value = item[args.column.pos]; + + // If a row is copied + if (item.is_row_copied) { + item[args.column.pos] = state; + + if (last_value && _.isNull(state) && + (_.isUndefined(grid.copied_rows[row]) || + _.isUndefined(grid.copied_rows[row][cell])) + ) { + item[args.column.pos] = undefined; + if (grid.copied_rows[row] == undefined) grid.copied_rows[row] = []; + grid.copied_rows[row][cell] = 1; + } + } + else { + item[args.column.pos] = state; + } }; this.isValueChanged = function () { diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index 2bb90c2..4f65c34 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -460,11 +460,16 @@ class TableCommand(GridCommand): column_data[each_col] = None column_type[each_col] =\ self.columns_info[each_col]['type_name'] + else: + column_type[each_col] = \ + self.columns_info[each_col]['type_name'] + for each_row in changed_data[of_type]: data = changed_data[of_type][each_row]['data'] # Remove our unique tracking key data.pop('__temp_PK', None) + data.pop('is_row_copied', None) data = set_column_names(data) data_type = set_column_names(changed_data[of_type][each_row]['data_type']) list_of_rowid.append(data.get('__temp_PK')) diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js index d8ae73e..e777ab3 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js @@ -526,6 +526,21 @@ define( render_grid: function(collection, columns, is_editable) { var self = this; + // returns primary keys + self.handler.get_row_primary_key = function() { + var self = this, + tmp_keys = []; + _.each(self.primary_keys, function(p, idx) { + // For each columns search primary key position + _.each(self.columns, function(c) { + if(c.name == idx) { + tmp_keys.push(c.pos); + } + }); + }); + return tmp_keys; + }; + // This will work as data store and holds all the // inserted/updated/deleted data from grid self.handler.data_store = { @@ -666,7 +681,8 @@ define( primary_key_list = _.keys(this.keys), _tmp_keys = [], _columns = this.columns, - rows_for_stage = {}, selected_rows_list = []; + rows_for_stage = {}, + selected_rows_list = []; // Only if entire row(s) are selected via check box if(_.has(this.selection, 'getSelectedRows')) { @@ -686,14 +702,19 @@ define( // Check if selected is new row ? // Allow to delete if yes - var cell_el = this.grid.getCellNode(selected_rows_list[0], 0), + var count = selected_rows_list.length-1, + cell_el = this.grid.getCellNode(selected_rows_list[count],0), parent_el = $(cell_el).parent(), is_new_row = $(parent_el).hasClass('new_row'); // Clear selection model if row primary keys is set to default - var row_data = collection[selected_rows_list[0]]; + var row_data = _.clone(collection[selected_rows_list[count]]), + is_primary_key = _.has(row_data, primary_key_list) && + row_data[0] != undefined ? true : false; + if (primary_key_list.length && - !_.has(row_data, primary_key_list) && !is_new_row) { + !is_primary_key && !is_new_row + ) { this.selection.setSelectedRows([]); selected_rows_list = []; } @@ -716,22 +737,20 @@ define( // Collect primary key data from collection as needed for stage row _.each(selected_rows_list, function(row_index) { var row_data = collection[row_index], - pkey_data = _.pick(row_data, primary_key_list); + p_keys_list = _.pick(row_data, primary_key_list), + is_primary_key = Object.keys(p_keys_list).length ? + p_keys_list[0] : undefined; // Store Primary key data for selected rows - if (!_.isUndefined(row_data) && !_.isUndefined(pkey_data)) { + if (!_.isUndefined(row_data) && !_.isUndefined(p_keys_list)) { // check for invalid row - rows_for_stage[row_data.__temp_PK] = _.pick(row_data, primary_key_list); + rows_for_stage[row_data.__temp_PK] = p_keys_list; } }); } else { //clear staged rows clear_staged_rows(); } - if (!Object.keys(rows_for_stage).length) { - clear_staged_rows(); - } - // Update main data store this.editor.handler.data_store.staged_rows = rows_for_stage; }.bind(editor_data)); @@ -806,7 +825,6 @@ define( }); - // Listener function which will be called when user updates existing rows grid.onCellChange.subscribe(function (e, args) { // self.handler.data_store.updated will holds all the updated data @@ -816,7 +834,27 @@ define( column_data = {}, _type; - column_data[changed_column] = updated_data; + // Access to row/cell value after a cell is changed. + // The purpose is to remove row_id from temp_new_row + // if new row has primary key instead of [default_value] + // so that cell edit is enabled for that row. + var grid = args.grid, + row_data = grid.getDataItem(args.row), + p_keys_list = _.pick( + row_data, self.handler.get_row_primary_key() + ), + is_primary_key = Object.keys(p_keys_list).length ? + p_keys_list[0] : undefined; + + // temp_new_rows is available only for view data. + if (is_primary_key && self.handler.temp_new_rows) { + var index = self.handler.temp_new_rows.indexOf(args.row); + if (index > -1) { + self.handler.temp_new_rows.splice(index, 1); + } + } + + column_data[changed_column] = updated_data; if(_pk) { // Check if it is in newly added row by user? @@ -854,28 +892,6 @@ define( $("#btn-save").prop('disabled', false); }.bind(editor_data)); - - // Listener function which will be called after cell is changed - grid.onActiveCellChanged.subscribe(function (e, args) { - // Access to row/cell value after a cell is changed. - // The purpose is to remove row_id from temp_new_row - // if new row has primary key instead of [default_value] - // so that cell edit is enabled for that row. - var grid = args.grid, - row_data = grid.getDataItem(args.row), - primary_key = row_data && row_data[0]; - - // temp_new_rows is available only for view data. - if (!_.isUndefined(primary_key) && - self.handler.temp_new_rows - ) { - var index = self.handler.temp_new_rows.indexOf(args.row); - if (index > -1) { - self.handler.temp_new_rows.splice(index, 1); - } - } - }); - // Listener function which will be called when user adds new rows grid.onAddNewRow.subscribe(function (e, args) { // self.handler.data_store.added will holds all the newly added rows/data @@ -2249,22 +2265,17 @@ define( }, rows_to_delete: function(data) { - var self = this; - var tmp_keys = []; - _.each(self.primary_keys, function(p, idx) { - // For each columns search primary key position - _.each(self.columns, function(c) { - if(c.name == idx) { - tmp_keys.push(c.pos); - } - }); - }); + var self = this, + tmp_keys = self.get_row_primary_key.call(self); // re-calculate rows with no primary keys self.temp_new_rows = []; data.forEach(function(d, idx) { - var p_keys_idx = _.pick(d, tmp_keys); - if (Object.keys(p_keys_idx).length == 0) { + var p_keys_list = _.pick(d, tmp_keys), + is_primary_key = Object.keys(p_keys_list).length ? + p_keys_list[0] : undefined; + + if (!is_primary_key) { self.temp_new_rows.push(idx); } }); @@ -2381,7 +2392,7 @@ define( is_primary_error = false; if( !is_added && !is_updated && !is_deleted ) { - return; // Nothing to save here + return; // Nothing to save here } if (save_data) { @@ -2405,6 +2416,18 @@ define( var grid = self.slickgrid, data = grid.getData(); if (res.data.status) { + // Remove flag is_row_copied from copied rows + _.each(data, function(row, idx) { + if (row.is_row_copied) { + delete row.is_row_copied; + } + }); + + // Remove 2d copied_rows array + if (grid.copied_rows) { + delete grid.copied_rows; + } + // Remove deleted rows from client as well if(is_deleted) { var rows = grid.getSelectedRows(); @@ -3074,64 +3097,50 @@ define( data = grid.getData(); // Deep copy var copied_rows = $.extend(true, [], self.copied_rows), - _tmp_copied_row = {}; + count = Object.keys(data).length-1, + _tmp_copied_row = {}; + // If there are rows to paste? if(copied_rows.length > 0) { // Enable save button so that user can // save newly pasted rows on server $("#btn-save").prop('disabled', false); - // Generate Unique key for each pasted row(s) - _.each(copied_rows, function(row) { - var _pk = epicRandomString(8); - row.__temp_PK = _pk; - }); - - var temp_func = self.data_view.getItemMetadata, - count = Object.keys(data).length-1; - _.each(copied_rows, function(row, idx) { - data[count] = row; - count++; - }); + var arr_to_object = function (arr) { + var obj = {}; + for (var i = 0; i < arr.length; ++i) { + if (arr[i] !== undefined) { + if(_.isObject(arr[i])) { + obj[String(i)] = JSON.stringify(arr[i]); + } else { + obj[String(i)] = arr[i]; + } + } + } + return obj; + }; - //update data_view - data.getItemMetadata = temp_func; - grid.setData(data, true); - grid.updateRowCount(); - grid.setSelectedRows([]); - grid.invalidateAllRows(); - grid.render(); - - // Fetch column name & its data type - _.each(self.columns, function(c) { - col_info[String(c.pos)] = c.type; + // Generate Unique key for each pasted row(s) + // Convert array values to object to send to server + // Add flag is_row_copied to handle [default] and [null] + // for copied rows. + // Add index of copied row into temp_new_rows + // Trigger grid.onAddNewRow when a row is copied + // Reset selection + _.each(copied_rows, function(row) { + var _pk = epicRandomString(8), + new_row = arr_to_object(row); + new_row.__temp_PK = _pk; + new_row.is_row_copied = true; + row = new_row; + self.temp_new_rows.push(count); + grid.onAddNewRow.notify( + {item: new_row, column: self.columns[0] , grid:grid} + ) + grid.setSelectedRows([]); + count++; }); - - // insert these data in data_store as well to save them on server - for (var j = 0; j < copied_rows.length; j += 1) { - self.data_store.added[copied_rows[j].__temp_PK] = { - 'data_type': {}, - 'data': {} - }; - self.data_store.added[copied_rows[j].__temp_PK]['data_type'] = col_info; - // We need to convert it from array to dict so that server can - // understand the data properly - _.each(copied_rows[j], function(val, key) { - // If value is array then convert it to string - if(_.isArray(val)) { - _tmp_copied_row[String(key)] = val.toString(); - // If value is object then stringify it - } else if(_.isObject(val)) { - _tmp_copied_row[j][String(key)] = JSON.stringify(val); - } else { - _tmp_copied_row[String(key)] = val; - } - }); - self.data_store.added[copied_rows[j].__temp_PK]['data'] = _tmp_copied_row; - // reset the variable - _tmp_copied_row = {}; - } } },