diff --git a/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js b/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js index faad73126..a3f4469bf 100644 --- a/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js +++ b/web/pgadmin/static/js/slickgrid/slick.pgadmin.editors.js @@ -11,17 +11,61 @@ "Editors": { "pgText": pgTextEditor, "JsonText": JsonTextEditor, + "CustomNumber": CustomNumberEditor, // Below editor will read only editors, Just to display data "ReadOnlyText": ReadOnlyTextEditor, "ReadOnlyCheckbox": ReadOnlyCheckboxEditor, "Checkbox": CheckboxEditor, // Override editor to implement checkbox with three states "ReadOnlypgText": ReadOnlypgTextEditor, - "ReadOnlyJsonText": ReadOnlyJsonTextEditor, - "CustomNumber": CustomNumberEditor + "ReadOnlyJsonText": ReadOnlyJsonTextEditor } } }); + /* + * This function handles the [default] and [null] values for cells + * if row is copied, otherwise returns the editor value. + * @param {args} editor object + * @param {item} row cell values + * @param {state} entered value + * @param {column_type} type of column + */ + function setValue(args, item, state, column_type) { + // 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] + + // If a row is copied + var grid = args.grid; + if (item.is_row_copied) { + 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], + last_value = (column_type === 'number') ? + (_.isEmpty(last_value) || last_value) : last_value; + + 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; + } + } + + // Text data type editor function pgTextEditor(args) { var $input, $wrapper; @@ -113,10 +157,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 +190,7 @@ }; this.applyValue = function (item, state) { - item[args.column.pos] = state; + setValue(args, item, state, 'text'); }; this.isValueChanged = function () { @@ -290,7 +334,7 @@ }; this.applyValue = function (item, state) { - item[args.column.pos] = state; + setValue(args, item, state, 'text'); }; this.isValueChanged = function () { @@ -855,7 +899,7 @@ }; this.applyValue = function (item, state) { - item[args.column.pos] = state; + setValue(args, item, state, 'number'); }; this.isValueChanged = function () { diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index 06fe56511..b7d8a78e8 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -463,11 +463,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 d8ae73eed..7d51193d0 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js @@ -39,10 +39,27 @@ define( pgBrowser = pgAdmin.Browser, Slick = window.Slick; - /* Get the function definition from - * http://stackoverflow.com/questions/1349404/generate-a-string-of-5-random-characters-in-javascript/35302975#35302975 + /* Reference link + * http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript + * Modified as per requirement. */ - function epicRandomString(b){for(var a=(Math.random()*eval("1e"+~~(50*Math.random()+50))).toString(36).split(""),c=3;cb)return b?a.substr(a,b):a;a=a.substr(a,b);if(a.length==b)return a;for(;a.length -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,53 +910,42 @@ 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 var _key = epicRandomString(10), column = args.column, - item = args.item, data_length = this.grid.getDataLength(); + item = args.item, + data_length = this.grid.getDataLength(), + new_collection = args.grid.getData(); // Add new row in list to keep track of it if (_.isUndefined(item[0])) { self.handler.temp_new_rows.push(data_length); } + // If copied item has already primary key, use it. if(item) { item.__temp_PK = _key; } - collection.push(item); + new_collection.push(item); + self.handler.data_store.added[_key] = {'err': false, 'data': item}; self.handler.data_store.added_index[data_length] = _key; // Fetch data type & add it for the column var temp = {}; temp[column.pos] = _.where(this.columns, {pos: column.pos})[0]['type']; self.handler.data_store.added[_key]['data_type'] = temp; - grid.invalidateRows([collection.length - 1]); + grid.invalidateRows([new_collection.length - 1]); grid.updateRowCount(); grid.render(); + + // Add a blank row after add row + grid.setData(new_collection, true); + grid.updateRowCount(); + grid.invalidateAllRows(); + grid.render(); + // Enable save button $("#btn-save").prop('disabled', false); }.bind(editor_data)); @@ -2249,22 +2294,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 +2421,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 +2445,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(); @@ -3071,67 +3123,59 @@ define( _paste_row: function() { var self = this, col_info = {}, grid = self.slickgrid, - data = grid.getData(); - // Deep copy - var copied_rows = $.extend(true, [], self.copied_rows), - _tmp_copied_row = {}; + data = grid.getData(), + count = Object.keys(data).length-1; + + var rows = grid.getSelectedRows().sort( + function (a, b) { return a - b; } + ), + rows = rows.length == 0 ? self.last_copied_rows : rows, + copied_rows = rows.map(function (rowIndex) { + return data[rowIndex]; + }); + self.last_copied_rows = rows; // 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; + var arr_to_object = function (arr) { + var obj = {}, + count = typeof(arr) == 'object' ? + Object.keys(arr).length: arr.length - _.each(copied_rows, function(row, idx) { - data[count] = row; - count++; - }); - - //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; - }); - - // 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; + _.each(arr, function(val, i){ + if (arr[i] !== undefined) { + if(_.isObject(arr[i])) { + obj[String(i)] = JSON.stringify(arr[i]); + } else { + obj[String(i)] = arr[i]; + } } }); - self.data_store.added[copied_rows[j].__temp_PK]['data'] = _tmp_copied_row; - // reset the variable - _tmp_copied_row = {}; - } + return obj; + }; + + // 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 new_row = arr_to_object(row); + 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++; + }); } },