diff --git a/Documentation.html b/Documentation.html index 72387ab63b..2fdf4f5fee 100644 --- a/Documentation.html +++ b/Documentation.html @@ -1373,6 +1373,9 @@ CREATE DATABASE,ALTER DATABASE,DROP DATABASE main panel's list (except on the Export page). This limit is also enforced in the navigation panel when in Light mode. +
$cfg['ShowHint'] boolean
+
Whether to show the hints or not (for example, hints when hovering table header)
+
$cfg['MaxCharactersInDisplayedSQL'] integer
The maximum number of characters when a SQL query is displayed. The default limit of 1000 should be correct to avoid the display of tons @@ -1961,7 +1964,7 @@ $cfg['TrustedProxies'] =
$cfg['RowActionLinks'] string
-
Defines the place where table row links (Edit, Inline edit, Copy, +
Defines the place where table row links (Edit, Copy, Delete) would be put when tables contents are displayed (you may have them displayed at the left side, right side, both sides or nowhere). "left" and "right" are parsed as "top" @@ -2143,6 +2146,11 @@ setfacl -d -m "g:www-data:rwx" tmp identify what they mean.
+
$cfg['SaveCellsAtOnce'] boolean
+
+ Defines whether or not to save all edited cells at once in browse-mode. +
+
$cfg['ShowDisplayDirection'] boolean
Defines whether or not type display direction option is shown diff --git a/js/db_search.js b/js/db_search.js index 3e2eeb40a7..aef5047be2 100644 --- a/js/db_search.js +++ b/js/db_search.js @@ -32,7 +32,6 @@ function loadResult(result_path , table_name , link , ajaxEnable) // we assign it manually from #table-link window.parent.table = $('#table-link').text().trim(); - appendInlineAnchor(); $('#table_results').makegrid(); }).show(); } diff --git a/js/functions.js b/js/functions.js index b16cf80c40..4447a4b584 100644 --- a/js/functions.js +++ b/js/functions.js @@ -3078,6 +3078,9 @@ $(document).ready(function() { */ function PMA_createqTip($elements, content, options) { + if ($('#no_hint').length > 0) { + return; + } var o = { content: content, style: { @@ -3112,6 +3115,21 @@ function PMA_createqTip($elements, content, options) $elements.qtip($.extend(true, o, options)); } +/** + * Return value of a cell in a table. + */ +function PMA_getCellValue(td) { + if ($(td).is('.null')) { + return ''; + } else if (! $(td).is('.to_be_saved') && $(td).data('original_data')) { + return $(td).data('original_data'); + } else if ($(td).is(':not(.transformed, .relation, .enum, .set, .null)')) { + return unescape($(td).find('span').html()).replace(/
/g, "\n"); + } else { + return $(td).text(); + } +} + $(document).ready(function() { /** * Theme selector. diff --git a/js/jquery/jquery.qtip-1.0.0-rc3.js b/js/jquery/jquery.qtip-1.0.0-rc3.js index 5b9c7e3064..4b01d9dbca 100644 --- a/js/jquery/jquery.qtip-1.0.0-rc3.js +++ b/js/jquery/jquery.qtip-1.0.0-rc3.js @@ -633,6 +633,7 @@ { // Set width to auto initally to determine new width and hide other elements self.elements.tooltip.css({ width: 'auto' }); + self.elements.wrapper.css({ width: 'auto' }); hidden.hide(); // Set position and zoom to defaults to prevent IE hasLayout bug @@ -2146,4 +2147,4 @@ classes: { tooltip: 'qtip-blue' } } }; -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/js/makegrid.js b/js/makegrid.js index 13fcc37a4c..91834ac381 100644 --- a/js/makegrid.js +++ b/js/makegrid.js @@ -21,7 +21,27 @@ showColVisibHint: false, showAllColText: '', // string, text for "show all" button under column visibility list visibleHeadersCount: 0, // number of visible data headers - + isCellEditActive: false, // true if current focus is in edit cell + isEditCellTextEditable: false, // true if current edit cell is editable in the text input box (not textarea) + currentEditCell: null, // reference to that currently being edited + inEditMode: false, // true if grid is in edit mode + cellEditHint: '', // hint shown when doing grid edit + gotoLinkText: 'Go to link', // "Go to link" text + wasEditedCellNull: false, // true if last value of the edited cell was NULL + maxTruncatedLen: 0, // number of characters that can be displayed in a cell + saveCellsAtOnce: false, // $cfg[saveCellsAtOnce] + isCellEdited: false, // true if at least one cell has been edited + saveCellWarning: '', // string, warning text when user want to leave a page with unsaved edited data + lastXHR : null, // last XHR object used in AJAX request + isSaving: false, // true when currently saving edited data, used to handle double posting caused by pressing ENTER in grid edit text box in Chrome browser + alertNonUnique: '', // string, alert shown when saving edited nonunique table + + // common hidden inputs + token: null, + server: null, + db: null, + table: null, + // functions dragStartRsz: function(e, obj) { // start column resize var n = $(this.cRsz).find('div').index(obj); @@ -34,6 +54,9 @@ }; $('body').css('cursor', 'col-resize'); $('body').noSelect(); + if (g.isCellEditActive) { + g.hideEditCell(); + } }, dragStartMove: function(e, obj) { // start column move @@ -65,6 +88,9 @@ this.qtip.hide(); $('body').css('cursor', 'move'); $('body').noSelect(); + if (g.isCellEditActive) { + g.hideEditCell(); + } }, dragMove: function(e) { @@ -146,7 +172,6 @@ this.colMov = false; } - $('body').css('cursor', 'default'); $('body').noSelect(false); }, @@ -266,10 +291,10 @@ sendColPrefs: function() { $.post('sql.php', { ajax_request: true, - db: window.parent.db, - table: window.parent.table, - token: window.parent.token, - server: window.parent.server, + db: g.db, + table: g.table, + token: g.token, + server: g.server, set_col_prefs: true, col_order: this.colOrder.toString(), col_visib: this.colVisib.toString(), @@ -438,6 +463,784 @@ } } this.afterToggleCol(); + }, + + /** + * Show edit cell, if it can be shown or it is forced. + */ + showEditCell: function(cell, force) { + if ($(cell).is('.grid_edit') && + !g.colRsz && !g.colMov) + { + if (!g.isCellEditActive || force) { + $cell = $(cell); + // remove all edit area and hide it + $(g.cEdit).find('.edit_area').empty().hide(); + // reposition the cEdit element + $(g.cEdit).css({ + top: $cell.position().top, + left: $cell.position().left, + }) + .show() + .find('input') + .css({ + width: $cell.outerWidth(), + height: $cell.outerHeight() + }); + // fill the cell edit with text from , if it is not null + var value = $cell.is(':not(.null)') ? PMA_getCellValue(cell) : ''; + $(g.cEdit).find('input') + .val(value); + + g.currentEditCell = cell; + $(g.cEdit).find('input[type=text]').focus(); + $(g.cEdit).find('*').removeAttr('disabled'); + } + } else { + g.hideEditCell(); + } + }, + + /** + * Remove edit cell and the edit area, if it is shown. + * + * @param force Optional, force to hide edit cell without saving edited field. + * @param data Optional, data from the POST AJAX request to save the edited field + * or just specify "true", if we want to replace the edited field with the new value. + * @param field Optional, the edited . If not specified, the function will + * use currently edited from g.currentEditCell. + */ + hideEditCell: function(force, data, field) { + if (g.isCellEditActive && !force) { + // cell is being edited, post the edited data + g.saveOrPostEditedCell(); + return; + } + + // cancel any previous request + if (g.lastXHR != null) { + g.lastXHR.abort(); + g.lastXHR = null; + } + + if (data) { + if (g.currentEditCell) { // save value of currently edited cell + // replace current edited field with the new value + var $this_field = $(g.currentEditCell); + var new_html = $this_field.data('value'); + var is_null = $this_field.data('value') == null; + if (is_null) { + $this_field.find('span').html('NULL'); + $this_field.addClass('null'); + } else { + $this_field.removeClass('null'); + if ($this_field.is('.truncated')) { + if (new_html.length > g.maxTruncatedLen) { + new_html = new_html.substring(0, g.maxTruncatedLen) + '...'; + } + } + // replace '\n' with
+ new_html = new_html.replace(/\n/g, '
'); + $this_field.find('span').html(new_html); + } + } + if (data.transformations != undefined) { + $.each(data.transformations, function(cell_index, value) { + var $this_field = $(g.t).find('.to_be_saved:eq(' + cell_index + ')'); + $this_field.find('span').html(value); + }); + } + if (data.relations != undefined) { + $.each(data.relations, function(cell_index, value) { + var $this_field = $(g.t).find('.to_be_saved:eq(' + cell_index + ')'); + $this_field.find('span').html(value); + }); + } + + // refresh the grid + this.reposRsz(); + this.reposDrop(); + } + + // hide the cell editing area + $(g.cEdit).hide(); + $(g.cEdit).find('input[type=text]').blur(); + g.isCellEditActive = false; + g.currentEditCell = null; + }, + + /** + * Show drop-down edit area when edit cell is clicked. + */ + showEditArea: function() { + if (!this.isCellEditActive) { // make sure we don't have focus on other edit cell + g.isCellEditActive = true; + g.isEditCellTextEditable = false; + var $td = $(g.currentEditCell); + var $editArea = $(this.cEdit).find('.edit_area'); + var where_clause = $td.parent('tr').find('.where_clause').val(); + /** + * @var field_name String containing the name of this field. + * @see getFieldName() + */ + var field_name = getFieldName($td); + /** + * @var relation_curr_value String current value of the field (for fields that are foreign keyed). + */ + var relation_curr_value = $td.text(); + /** + * @var relation_key_or_display_column String relational key if in 'Relational display column' mode, + * relational display column if in 'Relational key' mode (for fields that are foreign keyed). + */ + var relation_key_or_display_column = $td.find('a').attr('title'); + /** + * @var curr_value String current value of the field (for fields that are of type enum or set). + */ + var curr_value = $td.find('span').text(); + + // empty all edit area, then rebuild it based on $td classes + $editArea.empty(); + + // add goto link, if this cell contains a link + if ($td.find('a').length > 0) { + var gotoLink = document.createElement('div'); + gotoLink.className = 'goto_link'; + $(gotoLink).append(g.gotoLinkText + ': ') + .append($td.find('a').clone()); + $editArea.append(gotoLink); + } + + g.wasEditedCellNull = false; + if ($td.is(':not(.not_null)')) { + // append a null checkbox + $editArea.append('
Null :
'); + var $checkbox = $editArea.find('.null_div input'); + // check if current is NULL + if ($td.is('.null')) { + $checkbox.attr('checked', true); + g.wasEditedCellNull = true; + } + + // if the select/editor is changed un-check the 'checkbox_null__'. + if ($td.is('.enum, .set')) { + $editArea.find('select').live('change', function(e) { + $checkbox.attr('checked', false); + }) + } else if ($td.is('.relation')) { + $editArea.find('select').live('change', function(e) { + $checkbox.attr('checked', false); + }) + $editArea.find('.browse_foreign').live('click', function(e) { + $checkbox.attr('checked', false); + }) + } else { + $(g.cEdit).find('input[type=text]').live('change', function(e) { + $checkbox.attr('checked', false); + }) + $editArea.find('textarea').live('keydown', function(e) { + $checkbox.attr('checked', false); + }) + } + + // if 'checkbox_null__' is clicked empty the corresponding select/editor. + $checkbox.click(function(e) { + if ($td.is('.enum')) { + $editArea.find('select').attr('value', ''); + } else if ($td.is('.set')) { + $editArea.find('select').find('option').each(function() { + var $option = $(this); + $option.attr('selected', false); + }) + } else if ($td.is('.relation')) { + // if the dropdown is there to select the foreign value + if ($editArea.find('select').length > 0) { + $editArea.find('select').attr('value', ''); + } + } else { + $editArea.find('textarea').val(''); + } + $(g.cEdit).find('input[type=text]').val(''); + }) + } + + if($td.is('.relation')) { + /** @lends jQuery */ + //handle relations + $editArea.addClass('edit_area_loading'); + + // initialize the original data + $td.data('original_data', null); + + /** + * @var post_params Object containing parameters for the POST request + */ + var post_params = { + 'ajax_request' : true, + 'get_relational_values' : true, + 'server' : g.server, + 'db' : g.db, + 'table' : g.table, + 'column' : field_name, + 'token' : g.token, + 'curr_value' : relation_curr_value, + 'relation_key_or_display_column' : relation_key_or_display_column + } + + g.lastXHR = $.post('sql.php', post_params, function(data) { + g.lastXHR = null; + $editArea.removeClass('edit_area_loading'); + // save original_data + var value = $(data.dropdown).val(); + $td.data('original_data', value); + // update the text input field, in case where the "Relational display column" is checked + $(g.cEdit).find('input[type=text]').val(value); + + $editArea.append(data.dropdown); + $editArea.append('
' + g.cellEditHint + '
'); + }) // end $.post() + + $editArea.find('select').live('change', function(e) { + $(g.cEdit).find('input[type=text]').val($(this).val()); + }) + } + else if($td.is('.enum')) { + /** @lends jQuery */ + //handle enum fields + $editArea.addClass('edit_area_loading'); + + /** + * @var post_params Object containing parameters for the POST request + */ + var post_params = { + 'ajax_request' : true, + 'get_enum_values' : true, + 'server' : g.server, + 'db' : g.db, + 'table' : g.table, + 'column' : field_name, + 'token' : g.token, + 'curr_value' : curr_value + } + g.lastXHR = $.post('sql.php', post_params, function(data) { + g.lastXHR = null; + $editArea.removeClass('edit_area_loading'); + $editArea.append(data.dropdown); + $editArea.append('
' + g.cellEditHint + '
'); + }) // end $.post() + + $editArea.find('select').live('change', function(e) { + $(g.cEdit).find('input[type=text]').val($(this).val()); + }) + } + else if($td.is('.set')) { + /** @lends jQuery */ + //handle set fields + $editArea.addClass('edit_area_loading'); + + /** + * @var post_params Object containing parameters for the POST request + */ + var post_params = { + 'ajax_request' : true, + 'get_set_values' : true, + 'server' : g.server, + 'db' : g.db, + 'table' : g.table, + 'column' : field_name, + 'token' : g.token, + 'curr_value' : curr_value + } + + g.lastXHR = $.post('sql.php', post_params, function(data) { + g.lastXHR = null; + $editArea.removeClass('edit_area_loading'); + $editArea.append(data.select); + $editArea.append('
' + g.cellEditHint + '
'); + }) // end $.post() + + $editArea.find('select').live('change', function(e) { + $(g.cEdit).find('input[type=text]').val($(this).val()); + }) + } + else if($td.is('.truncated, .transformed')) { + if ($td.is('.to_be_saved')) { // cell has been edited + var value = $td.data('value'); + $(g.cEdit).find('input[type=text]').val(value); + $editArea.append(''); + $editArea.find('textarea').live('keyup', function(e) { + $(g.cEdit).find('input[type=text]').val($(this).val()); + }); + $(g.cEdit).find('input[type=text]').live('keyup', function(e) { + $editArea.find('textarea').val($(this).val()); + }); + $editArea.append('
' + g.cellEditHint + '
'); + } else { + /** @lends jQuery */ + //handle truncated/transformed values values + $editArea.addClass('edit_area_loading'); + + // initialize the original data + $td.data('original_data', null); + + /** + * @var sql_query String containing the SQL query used to retrieve value of truncated/transformed data + */ + var sql_query = 'SELECT `' + field_name + '` FROM `' + g.table + '` WHERE ' + PMA_urldecode(where_clause); + + // Make the Ajax call and get the data, wrap it and insert it + g.lastXHR = $.post('sql.php', { + 'token' : g.token, + 'server' : g.server, + 'db' : g.db, + 'ajax_request' : true, + 'sql_query' : sql_query, + 'grid_edit' : true + }, function(data) { + g.lastXHR = null; + $editArea.removeClass('edit_area_loading'); + if(data.success == true) { + if ($td.is('.truncated')) { + // get the truncated data length + g.maxTruncatedLen = $(g.currentEditCell).text().length - 3; + } + + $td.data('original_data', data.value); + $(g.cEdit).find('input[type=text]').val(data.value); + $editArea.append(''); + $editArea.find('textarea').live('keyup', function(e) { + $(g.cEdit).find('input[type=text]').val($(this).val()); + }); + $(g.cEdit).find('input[type=text]').live('keyup', function(e) { + $editArea.find('textarea').val($(this).val()); + }); + $editArea.append('
' + g.cellEditHint + '
'); + } + else { + PMA_ajaxShowMessage(data.error); + } + }) // end $.post() + } + g.isEditCellTextEditable = true; + } else { + $editArea.append(''); + $editArea.find('textarea').live('keyup', function(e) { + $(g.cEdit).find('input[type=text]').val($(this).val()); + }); + $(g.cEdit).find('input[type=text]').live('keyup', function(e) { + $editArea.find('textarea').val($(this).val()); + }); + $editArea.append('
' + g.cellEditHint + '
'); + g.isEditCellTextEditable = true; + } + + $editArea.show(); + } + }, + + /** + * Post the content of edited cell. + */ + postEditedCell: function() { + if (g.isSaving) { + return; + } + g.isSaving = true; + + /** + * @var relation_fields Array containing the name/value pairs of relational fields + */ + var relation_fields = {}; + /** + * @var relational_display string 'K' if relational key, 'D' if relational display column + */ + var relational_display = $("#relational_display_K").attr('checked') ? 'K' : 'D'; + /** + * @var transform_fields Array containing the name/value pairs for transformed fields + */ + var transform_fields = {}; + /** + * @var transformation_fields Boolean, if there are any transformed fields in the edited cells + */ + var transformation_fields = false; + /** + * @var full_sql_query String containing the complete SQL query to update this table + */ + var full_sql_query = ''; + /** + * @var rel_fields_list String, url encoded representation of {@link relations_fields} + */ + var rel_fields_list = ''; + /** + * @var transform_fields_list String, url encoded representation of {@link transform_fields} + */ + var transform_fields_list = ''; + /** + * @var where_clause Array containing where clause for updated fields + */ + var full_where_clause = Array(); + /** + * @var is_unique Boolean, whether the rows in this table is unique or not + */ + var is_unique = $('.edit_row_anchor').is('.nonunique') ? 0 : 1; + /** + * multi edit variables + */ + var me_fields_name = Array(); + var me_fields = Array(); + var me_fields_null = Array(); + + // alert user if edited table is not unique + if (!is_unique) { + alert(g.alertNonUnique); + } + + // loop each edited row + $('.to_be_saved').parents('tr').each(function() { + var $tr = $(this); + var where_clause = $tr.find('.where_clause').val(); + full_where_clause.push(PMA_urldecode(where_clause)); + var condition_array = jQuery.parseJSON($tr.find('.condition_array').val()); + + /** + * multi edit variables, for current row + * @TODO array indices are still not correct, they should be md5 of field's name + */ + var fields_name = Array(); + var fields = Array(); + var fields_null = Array(); + + // loop each edited cell in a row + $tr.find('.to_be_saved').each(function() { + /** + * @var $this_field Object referring to the td that is being edited + */ + var $this_field = $(this); + + /** + * @var field_name String containing the name of this field. + * @see getFieldName() + */ + var field_name = getFieldName($this_field); + + /** + * @var this_field_params Array temporary storage for the name/value of current field + */ + var this_field_params = {}; + + if($this_field.is('.transformed')) { + transformation_fields = true; + } + this_field_params[field_name] = $this_field.data('value'); + + /** + * @var is_null String capturing whether 'checkbox_null__' is checked. + */ + var is_null = this_field_params[field_name] === null; + + fields_name.push(field_name); + + if (is_null) { + fields_null.push('on'); + fields.push(''); + } else { + fields_null.push(''); + fields.push($this_field.data('value')); + + var cell_index = $this_field.index('.to_be_saved'); + if($this_field.is(":not(.relation, .enum, .set, .bit)")) { + if($this_field.is('.transformed')) { + transform_fields[cell_index] = {}; + $.extend(transform_fields[cell_index], this_field_params); + } + } else if($this_field.is('.relation')) { + relation_fields[cell_index] = {}; + $.extend(relation_fields[cell_index], this_field_params); + } + } + // check if edited field appears in WHERE clause + if (where_clause.indexOf(PMA_urlencode(field_name)) > -1) { + var field_str = '`' + g.table + '`.' + '`' + field_name + '`'; + for (var field in condition_array) { + if (field.indexOf(field_str) > -1) { + condition_array[field] = is_null ? 'IS NULL' : "= '" + this_field_params[field_name].replace(/'/g,"''") + "'"; + break; + } + } + } + + }); // end of loop for every edited cells in a row + + // save new_clause + var new_clause = ''; + for (var field in condition_array) { + new_clause += field + ' ' + condition_array[field] + ' AND '; + } + new_clause = new_clause.substring(0, new_clause.length - 5); // remove the last AND + new_clause = PMA_urlencode(new_clause); + $tr.data('new_clause', new_clause); + // save condition_array + $tr.find('.condition_array').val(JSON.stringify(condition_array)); + + me_fields_name.push(fields_name); + me_fields.push(fields); + me_fields_null.push(fields_null); + + }); // end of loop for every edited rows + + rel_fields_list = $.param(relation_fields); + transform_fields_list = $.param(transform_fields); + + // Make the Ajax post after setting all parameters + /** + * @var post_params Object containing parameters for the POST request + */ + var post_params = {'ajax_request' : true, + 'sql_query' : full_sql_query, + 'token' : g.token, + 'server' : g.server, + 'db' : g.db, + 'table' : g.table, + 'clause_is_unique' : is_unique, + 'where_clause' : full_where_clause, + 'fields[multi_edit]' : me_fields, + 'fields_name[multi_edit]' : me_fields_name, + 'fields_null[multi_edit]' : me_fields_null, + 'rel_fields_list' : rel_fields_list, + 'do_transformations' : transformation_fields, + 'transform_fields_list' : transform_fields_list, + 'relational_display' : relational_display, + 'goto' : 'sql.php', + 'submit_type' : 'save' + }; + + if (!g.saveCellsAtOnce) { + $(g.cEdit).find('*').attr('disabled', 'disabled'); + var $editArea = $(g.cEdit).find('.edit_area'); + $editArea.addClass('edit_area_posting'); + } else { + $('.save_edited').addClass('saving_edited_data') + .find('input').attr('disabled', 'disabled'); // disable the save button + } + + $.ajax({ + type: 'POST', + url: 'tbl_replace.php', + data: post_params, + success: + function(data) { + g.isSaving = false; + if (!g.saveCellsAtOnce) { + $(g.cEdit).find('*').removeAttr('disabled'); + $editArea.removeClass('edit_area_posting'); + } else { + $('.save_edited').removeClass('saving_edited_data') + .find('input').removeAttr('disabled'); // enable the save button back + } + if(data.success == true) { + PMA_ajaxShowMessage(data.message); + $('.to_be_saved').each(function() { + var new_clause = $(this).parent('tr').data('new_clause'); + if (new_clause != '') { + var $where_clause = $(this).parent('tr').find('.where_clause'); + var old_clause = $where_clause.attr('value'); + var decoded_old_clause = PMA_urldecode(old_clause); + var decoded_new_clause = PMA_urldecode(new_clause); + + $where_clause.attr('value', new_clause); + // update Edit, Copy, and Delete links also + $(this).parent('tr').find('a').each(function() { + $(this).attr('href', $(this).attr('href').replace(old_clause, new_clause)); + // update delete confirmation in Delete link + if ($(this).attr('href').indexOf('DELETE') > -1) { + $(this).removeAttr('onclick') + .unbind('click') + .bind('click', function() { + return confirmLink(this, 'DELETE FROM `' + g.db + '`.`' + g.table + '` WHERE ' + + decoded_new_clause + (is_unique ? '' : ' LIMIT 1')); + }); + } + }); + // update the multi edit checkboxes + $(this).parent('tr').find('input[type=checkbox]').each(function() { + var $checkbox = $(this); + var checkbox_name = $checkbox.attr('name'); + var checkbox_value = $checkbox.attr('value'); + + $checkbox.attr('name', checkbox_name.replace(old_clause, new_clause)); + $checkbox.attr('value', checkbox_value.replace(decoded_old_clause, decoded_new_clause)); + }); + } + }); + // remove possible previous feedback message + $('#result_query').remove(); + if (typeof data.sql_query != 'undefined') { + // display feedback + $('#sqlqueryresults').prepend(data.sql_query); + } + g.hideEditCell(true, data); + + // remove the "Save edited cells" button + $('.save_edited').hide(); + // update saved fields + $(g.t).find('.to_be_saved') + .removeClass('to_be_saved') + .data('value', null) + .data('original_data', null); + + g.isCellEdited = false; + } else { + PMA_ajaxShowMessage(data.error); + } + } + }) // end $.ajax() + }, + + // save edited cell, so it can be posted later + saveEditedCell: function() { + /** + * @var $this_field Object referring to the td that is being edited + */ + var $this_field = $(g.currentEditCell); + var $test_element = ''; // to test the presence of a element + + var need_to_post = false; + + /** + * @var field_name String containing the name of this field. + * @see getFieldName() + */ + var field_name = getFieldName($this_field); + + /** + * @var this_field_params Array temporary storage for the name/value of current field + */ + var this_field_params = {}; + + /** + * @var is_null String capturing whether 'checkbox_null__' is checked. + */ + var is_null = $(g.cEdit).find('input:checkbox').is(':checked'); + var value; + + if ($(g.cEdit).find('.edit_area').is('.edit_area_loading')) { + need_to_post = false; + } else if (is_null) { + if (!g.wasEditedCellNull) { + this_field_params[field_name] = null; + need_to_post = true; + } + } else { + if($this_field.is(":not(.relation, .enum, .set, .bit)")) { + this_field_params[field_name] = $(g.cEdit).find('textarea').val(); + } else if ($this_field.is('.bit')) { + this_field_params[field_name] = '0b' + $(g.cEdit).find('textarea').val(); + } else if ($this_field.is('.set')) { + $test_element = $(g.cEdit).find('select'); + this_field_params[field_name] = $test_element.map(function(){ + return $(this).val(); + }).get().join(","); + } else { + // results from a drop-down + $test_element = $(g.cEdit).find('select'); + if ($test_element.length != 0) { + this_field_params[field_name] = $test_element.val(); + } + + // results from Browse foreign value + $test_element = $(g.cEdit).find('span.curr_value'); + if ($test_element.length != 0) { + this_field_params[field_name] = $test_element.text(); + } + } + if (g.wasEditedCellNull || this_field_params[field_name] != PMA_getCellValue(g.currentEditCell)) { + need_to_post = true; + } + } + + if (need_to_post) { + $(g.currentEditCell).addClass('to_be_saved') + .data('value', this_field_params[field_name]); + if (g.saveCellsAtOnce) { + $('.save_edited').show(); + } + g.isCellEdited = true; + } + + return need_to_post; + }, + + // save or post edited cell, depending on the configuration + saveOrPostEditedCell: function() { + var saved = g.saveEditedCell(); + if (!g.saveCellsAtOnce) { + if (saved) { + g.postEditedCell(); + } else { + g.hideEditCell(true); + } + } else { + if (saved) { + g.hideEditCell(true, true); + } else { + g.hideEditCell(true); + } + } + }, + + // initialize grid editing feature + initGridEdit: function() { + $(t).find('td.data') + .click(function(e) { + if (g.isCellEditActive) { + g.saveOrPostEditedCell(); + e.stopPropagation(); + } else { + g.showEditCell(this); + e.stopPropagation(); + } + // prevent default action when clicking on "link" in a table + if ($(e.target).is('a')) { + e.preventDefault(); + } + }); + $(g.cEdit).find('input[type=text]').focus(function(e) { + g.showEditArea(); + }); + $(g.cEdit).find('input[type=text], select').live('keydown', function(e) { + if (e.which == 13) { + // post on pressing "Enter" + e.preventDefault(); + g.saveOrPostEditedCell(); + } + }); + $(g.cEdit).keydown(function(e) { + if (!g.isEditCellTextEditable) { + // prevent text editing + e.preventDefault(); + } + }); + $('html').click(function(e) { + // hide edit cell if the click is not from g.cEdit + if ($(e.target).parents().index(g.cEdit) == -1) { + g.hideEditCell(); + } + }); + $('html').keydown(function(e) { + if (e.which == 27 && g.isCellEditActive) { + + // cancel on pressing "Esc" + g.hideEditCell(true); + } + }); + $('.save_edited').click(function() { + g.hideEditCell(); + g.postEditedCell(); + }); + $(window).bind('beforeunload', function(e) { + if (g.isCellEdited) { + return g.saveCellWarning; + } + }); } } @@ -451,7 +1254,8 @@ g.cPointer = document.createElement('div'); // column pointer, used when reordering column g.cDrop = document.createElement('div'); // column drop-down arrows g.cList = document.createElement('div'); // column visibility list - + g.cEdit = document.createElement('div'); // cell edit + // adjust g.cCpy g.cCpy.className = 'cCpy'; $(g.cCpy).hide(); @@ -466,7 +1270,12 @@ // adjust g.cList g.cList.className = 'cList'; $(g.cList).hide(); - + + // adjust g.cEdit + g.cEdit.className = 'cEdit'; + $(g.cEdit).html('
'); + $(g.cEdit).hide(); + // chain table and grid together t.grid = g; g.t = t; @@ -491,10 +1300,25 @@ // assign column reorder & column sort hint g.reorderHint = PMA_messages['strColOrderHint']; g.sortHint = PMA_messages['strSortHint']; - g.markHint = PMA_messages['strMarkHint']; - g.colVisibHint = PMA_messages['strVisibHint']; - g.showAllColText = PMA_messages['strShowAll']; - + g.markHint = PMA_messages['strColMarkHint']; + g.colVisibHint = PMA_messages['strColVisibHint']; + g.showAllColText = PMA_messages['strShowAllCol']; + + // assign cell editing hint + g.cellEditHint = PMA_messages['strCellEditHint']; + g.saveCellWarning = PMA_messages['strSaveCellWarning']; + g.alertNonUnique = PMA_messages['strAlertNonUnique']; + + // initialize cell editing configuration + g.saveCellsAtOnce = $('#save_cells_at_once').val(); + + // assign common hidden inputs + var $common_hidden_inputs = $('.common_hidden_inputs'); + g.token = $common_hidden_inputs.find('input[name=token]').val(); + g.server = $common_hidden_inputs.find('input[name=server]').val(); + g.db = $common_hidden_inputs.find('input[name=db]').val(); + g.table = $common_hidden_inputs.find('input[name=table]').val(); + // initialize column order $col_order = $('#col_order'); if ($col_order.length > 0) { @@ -663,7 +1487,11 @@ $(t).find('td, th.draggable').mouseenter(function() { g.hideColList(); }); - + // edit cell event + if ($(t).is('.ajax')) { + g.initGridEdit(); + } + // add table class $(t).addClass('pma_table'); @@ -675,6 +1503,7 @@ $(g.gDiv).append(g.cDrop); $(g.gDiv).append(g.cList); $(g.gDiv).append(g.cCpy); + $(g.gDiv).append(g.cEdit); // some adjustment g.refreshRestoreButton(); diff --git a/js/messages.php b/js/messages.php index a63184043f..5f4e12b040 100644 --- a/js/messages.php +++ b/js/messages.php @@ -226,7 +226,6 @@ $js_messages['strImportCSV'] = __('Note: If the file contains multiple tables, t /* For sql.js */ $js_messages['strHideQueryBox'] = __('Hide query box'); $js_messages['strShowQueryBox'] = __('Show query box'); -$js_messages['strInlineEdit'] = __('Inline Edit'); $js_messages['strEdit'] = __('Edit'); $js_messages['strSave'] = __('Save'); $js_messages['strHide'] = __('Hide'); @@ -254,6 +253,16 @@ $js_messages['strLeavingDesigner'] = __('You haven\'t saved the changes in the l /* Visual query builder (js/pmd/move.js) */ $js_messages['strAddOption'] = __('Add an option for column '); +/* For makegrid.js (column reordering, show/hide column, grid editing) */ +$js_messages['strCellEditHint'] = __('Press escape to cancel editing'); +$js_messages['strSaveCellWarning'] = __('You have edited some data and they have not been saved. Are you sure you want to leave this page before saving the data?'); +$js_messages['strColOrderHint'] = __('Drag to reorder'); +$js_messages['strSortHint'] = __('Click to sort'); +$js_messages['strColMarkHint'] = __('Click to mark/unmark'); +$js_messages['strColVisibHint'] = __('Click the drop-down arrow
to toggle column\'s visibility'); +$js_messages['strShowAllCol'] = __('Show all'); +$js_messages['strAlertNonUnique'] = __('This table contains no unique field. Features related to the grid edit, checkbox, Edit, Copy and Delete links may not work after saving.'); + /* password generation */ $js_messages['strGeneratePassword'] = __('Generate password'); $js_messages['strGenerate'] = __('Generate'); @@ -268,12 +277,6 @@ $js_messages['strNewerVersion'] = __('A newer version of phpMyAdmin is available $js_messages['strLatestAvailable'] = __(', latest stable version:'); $js_messages['strUpToDate'] = __('up to date'); -/* Browsing grid */ -$js_messages['strColOrderHint'] = __('Drag to reorder'); -$js_messages['strSortHint'] = __('Click to sort'); -$js_messages['strMarkHint'] = __('Click to mark/unmark'); -$js_messages['strVisibHint'] = __('Click the drop-down arrow
to toggle column\'s visibility'); -$js_messages['strShowAll'] = __('Show all'); echo "var PMA_messages = new Array();\n"; foreach ($js_messages as $name => $js_message) { diff --git a/js/navigation.js b/js/navigation.js index c70d428c18..e4d7a207b1 100644 --- a/js/navigation.js +++ b/js/navigation.js @@ -205,9 +205,9 @@ $(document).ready(function(){ /* Jump to recent table */ $('#recentTable').change(function() { if (this.value != '') { - var arr = this.value.split('.'); - window.parent.setDb(arr[0]); - window.parent.setTable(arr[1]); + var arr = jQuery.parseJSON(this.value); + window.parent.setDb(arr['db']); + window.parent.setTable(arr['table']); window.parent.refreshMain($('#LeftDefaultTabTable')[0].value); } }); diff --git a/js/sql.js b/js/sql.js index 2b77af12f0..4386dcec6f 100644 --- a/js/sql.js +++ b/js/sql.js @@ -22,12 +22,12 @@ function PMA_urldecode(str) function PMA_urlencode(str) { - return encodeURIComponent(str.replace(/\%20/g, '+')); + return encodeURIComponent(str).replace(/\%20/g, '+'); } /** * Get the field name for the current field. Required to construct the query - * for inline editing + * for grid editing * * @param $this_field jQuery object that points to the current field's tr */ @@ -37,12 +37,13 @@ function getFieldName($this_field) var this_field_index = $this_field.index(); // ltr or rtl direction does not impact how the DOM was generated // check if the action column in the left exist - var leftActionExist = !$('#table_results').find('th:first').hasClass('draggable'); - // 5 columns to account for the checkbox, edit, appended inline edit, copy and delete anchors but index is zero-based so substract 4 - var field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index - (leftActionExist ? 4 : 0)) + ') a').text(); + var left_action_exist = !$('#table_results').find('th:first').hasClass('draggable'); + // number of column span for checkbox and Actions + var left_action_skip = left_action_exist ? $('#table_results').find('th:first').attr('colspan') - 1 : 0; + var field_name = $('#table_results').find('thead').find('th:eq('+ (this_field_index - left_action_skip) + ') a').text(); // happens when just one row (headings contain no a) if ("" == field_name) { - field_name = $('#table_results').find('thead').find('th:nth('+ (this_field_index - (leftActionExist ? 4 : 0)) + ')').text(); + field_name = $('#table_results').find('thead').find('th:eq('+ (this_field_index - left_action_skip) + ')').text(); } field_name = $.trim(field_name); @@ -50,69 +51,6 @@ function getFieldName($this_field) return field_name; } -/** - * The function that iterates over each row in the table_results and appends a - * new inline edit anchor to each table row. - * - */ -function appendInlineAnchor() -{ - // TODO: remove two lines below if vertical display mode has been completely removed - var disp_mode = $("#top_direction_dropdown").val(); - - if (disp_mode != 'vertical') { - $('.edit_row_anchor').each(function() { - - var $this_td = $(this); - $this_td.removeClass('edit_row_anchor'); - - var $cloned_anchor = $this_td.clone(); - - var $img_object = $cloned_anchor.find('img').attr('title', PMA_messages['strInlineEdit']); - if ($img_object.length != 0) { - $img_object.removeClass('ic_b_edit'); - $img_object.addClass('ic_b_inline_edit'); - - $cloned_anchor.find('a').attr('href', '#'); - var $edit_span = $cloned_anchor.find('span:contains("' + PMA_messages['strEdit'] + '")'); - var $span = $cloned_anchor.find('a').find('span'); - if ($edit_span.length > 0) { - $span.text(' ' + PMA_messages['strInlineEdit']); - $span.prepend($img_object); - } else { - $span.text(''); - $span.append($img_object); - } - } else { - // Only text is displayed. See $cfg['PropertiesIconic'] - $cloned_anchor.find('a').attr('href', '#'); - $cloned_anchor.find('a span').text(PMA_messages['strInlineEdit']); - - // the link was too big so is there - $img_object = $cloned_anchor.find('input:image').attr('title', PMA_messages['strInlineEdit']); - if ($img_object.length > 0) { - $img_object.removeClass('ic_b_edit'); - $img_object.addClass('ic_b_inline_edit'); - } - $cloned_anchor - .find('.clickprevimage') - .text(' ' + PMA_messages['strInlineEdit']); - } - - $cloned_anchor - .addClass('inline_edit_anchor'); - - $this_td.after($cloned_anchor); - }); - - $('#resultsForm').find('thead, tbody').find('th').each(function() { - var $this_th = $(this); - if ($this_th.attr('colspan') == 4) { - $this_th.attr('colspan', '5'); - } - }); - } -} /**#@+ * @namespace jQuery @@ -127,7 +65,7 @@ function appendInlineAnchor() *
  • Paginate the results table
  • *
  • Sort the results table
  • *
  • Change table according to display options
  • - *
  • Inline editing of data
  • + *
  • Grid editing of data
  • * * * @name document.ready @@ -150,15 +88,6 @@ $(document).ready(function() { .toggle($(this).attr('value').length > 0); }).trigger('keyup'); - /** - * Attach the {@link appendInlineAnchor} function to a custom event, which - * will be triggered manually everytime the table of results is reloaded - * @memberOf jQuery - */ - $("#sqlqueryresults").live('appendAnchor',function() { - appendInlineAnchor(); - }) - /** * Attach the {@link makegrid} function to a custom event, which will be * triggered manually everytime the table of results is reloaded @@ -170,21 +99,13 @@ $(document).ready(function() { /** * Attach the {@link refreshgrid} function to a custom event, which will be - * triggered manually everytime the table of results is manipulated (e.g., by inline edit) + * triggered manually everytime the table of results is manipulated * @memberOf jQuery */ $("#sqlqueryresults").live('refreshgrid', function() { $('#table_results').refreshgrid(); }) - /** - * Trigger the appendAnchor event to prepare the first table for inline edit - * (see $GLOBALS['cfg']['AjaxEnable']) - * @memberOf jQuery - * @name sqlqueryresults_trigger - */ - $("#sqlqueryresults.ajax").trigger('appendAnchor'); - /** * Append the "Show/Hide query box" message to the query input form * @@ -290,7 +211,6 @@ $(document).ready(function() { $sqlqueryresults .show() .html(data) - .trigger('appendAnchor') .trigger('makegrid'); $('#togglequerybox').show(); if ($("#togglequerybox").siblings(":visible").length > 0) { @@ -332,7 +252,6 @@ $(document).ready(function() { $.post($form.attr('action'), $form.serialize(), function(data) { $("#sqlqueryresults") .html(data) - .trigger('appendAnchor') .trigger('makegrid'); PMA_init_slider(); @@ -357,7 +276,6 @@ $(document).ready(function() { $.post($form.attr('action'), $form.serialize() + '&ajax_request=true', function(data) { $("#sqlqueryresults") .html(data) - .trigger('appendAnchor') .trigger('makegrid'); PMA_init_slider(); PMA_ajaxRemoveMessage($msgbox); @@ -384,7 +302,6 @@ $(document).ready(function() { $.get($anchor.attr('href'), $anchor.serialize() + '&ajax_request=true', function(data) { $("#sqlqueryresults") .html(data) - .trigger('appendAnchor') .trigger('makegrid'); PMA_ajaxRemoveMessage($msgbox); }) // end $.get() @@ -404,535 +321,12 @@ $(document).ready(function() { $.post($form.attr('action'), $form.serialize() + '&ajax_request=true' , function(data) { $("#sqlqueryresults") .html(data) - .trigger('appendAnchor') .trigger('makegrid'); PMA_init_slider(); }) // end $.post() }) //end displayOptionsForm handler - /** - * Ajax Event handlers for Inline Editing - */ - - /** - * On click, replace the fields of current row with an input/textarea - * @memberOf jQuery - * @name inline_edit_start - * @see PMA_ajaxShowMessage() - * @see getFieldName() - */ - $(".inline_edit_anchor span a").live('click', function(event) { - /** @lends jQuery */ - event.preventDefault(); - - var $edit_td = $(this).parents('td'); - $edit_td.removeClass('inline_edit_anchor').addClass('inline_edit_active').parent('tr').addClass('noclick'); - - // Adding submit and hide buttons to inline edit . - // For "hide" button the original data to be restored is - // kept in the jQuery data element 'original_data' inside the . - // Looping through all columns or rows, to find the required data and then storing it in an array. - - var $this_children = $edit_td.children('span.nowrap').children('a').children('span.nowrap'); - // Keep the original data preserved. - $data_a = $edit_td.children('span.nowrap').children('a').clone(); - - // Change the inline edit to save. - var $img_object = $this_children.find('img'); - - // If texts are displayed. See $cfg['PropertiesIconic'] - if ($this_children.parent('a').find('span:contains("' + PMA_messages['strInlineEdit'] + '")').length > 0) { - $this_children.text(' ' + PMA_messages['strSave']); - } else { - $this_children.empty(); - } - - // If icons are displayed. See $cfg['PropertiesIconic'] - if ($img_object.length > 0) { - $img_object.attr('title', PMA_messages['strSave']); - $img_object.removeClass('ic_b_inline_edit'); - $img_object.addClass('ic_b_save'); - $this_children.prepend($img_object); - } - - // Clone the save link and change it to create the hide link. - var $hide_a = $edit_td.children('span.nowrap').children('a').clone().attr('id', 'hide'); - var $hide_span = $hide_a.find('span'); - var $img_object = $hide_a.find('span img'); - - // If texts are displayed. See $cfg['PropertiesIconic'] - if ($hide_a.find('span:contains("' + PMA_messages['strSave'] + '")').length > 0) { - $hide_span.text(' ' + PMA_messages['strHide']); - } else { - $hide_span.empty(); - } - - // If icons are displayed. See $cfg['PropertiesIconic'] - if ($img_object.length > 0) { - $img_object.attr('title', PMA_messages['strHide']); - $img_object.removeClass('ic_b_save'); - $img_object.addClass('ic_b_close'); - $hide_span.prepend($img_object); - } - - // Add hide icon and/or text. - $edit_td.children('span.nowrap').append($('

    ')).append($hide_a); - - $('#table_results tbody tr td span a#hide').click(function() { - var $this_hide = $(this).parents('td'); - - var $this_span = $this_hide.find('span'); - $this_span.find('a, br').remove(); - $this_span.append($data_a.clone()); - - $this_hide.removeClass("inline_edit_active hover").addClass("inline_edit_anchor"); - $this_hide.parent().removeClass("hover noclick"); - $this_hide.siblings().removeClass("hover"); - - var $input_siblings = $this_hide.parent('tr').find('.inline_edit'); - var txt = ''; - $input_siblings.each(function() { - var $this_hide_siblings = $(this); - txt = $this_hide_siblings.data('original_data'); - if($this_hide_siblings.children('span').children().length != 0) { - $this_hide_siblings.children('span').empty(); - $this_hide_siblings.children('span').append(txt); - } - }); - $(this).prev().prev().remove(); - $(this).prev().remove(); - $(this).remove(); - - // refresh the grid - $("#sqlqueryresults").trigger('refreshgrid'); - }); - - // Initialize some variables - var this_row_index = $edit_td.parent().index(); - var $input_siblings = $edit_td.parent('tr').find('.inline_edit'); - var where_clause = $edit_td.parent('tr').find('.where_clause').val(); - - $input_siblings.each(function() { - /** @lends jQuery */ - /** - * @var data_value Current value of this field - */ - var data_value = $(this).children('span').html(); - - // We need to retrieve the value from the server for truncated/relation fields - // Find the field name - - /** - * @var this_field Object referring to this field () - */ - var $this_field = $(this); - /** - * @var this_field_span Object referring to this field's child () - */ - var $this_field_span = $(this).children('span'); - /** - * @var field_name String containing the name of this field. - * @see getFieldName() - */ - var field_name = getFieldName($this_field); - /** - * @var relation_curr_value String current value of the field (for fields that are foreign keyed). - */ - var relation_curr_value = $this_field.find('a').text(); - /** - * @var relation_key_or_display_column String relational key if in 'Relational display column' mode, - * relational display column if in 'Relational key' mode (for fields that are foreign keyed). - */ - var relation_key_or_display_column = $this_field.find('a').attr('title'); - /** - * @var curr_value String current value of the field (for fields that are of type enum or set). - */ - var curr_value = $this_field_span.text(); - - if($this_field.is(':not(.not_null)')){ - // add a checkbox to mark null for all the field that are nullable. - $this_field_span.html('
    Null :
    '); - // check the 'checkbox_null__' if the corresponding value is null - if($this_field.is('.null')) { - $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', true); - } - - // if the select/editor is changed un-check the 'checkbox_null__'. - if ($this_field.is('.enum, .set')) { - $this_field.find('select').live('change', function(e) { - $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false); - }) - } else if ($this_field.is('.relation')) { - $this_field.find('select').live('change', function(e) { - $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false); - }) - $this_field.find('.browse_foreign').live('click', function(e) { - $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false); - }) - } else { - $this_field.find('textarea').live('keypress', function(e) { - // FF errorneously triggers for modifier keys such as tab (bug #3357837) - if (e.which != 0) { - $('.checkbox_null_' + field_name + '_' + this_row_index).attr('checked', false); - } - }) - } - - // if 'checkbox_null__' is clicked empty the corresponding select/editor. - $('.checkbox_null_' + field_name + '_' + this_row_index).bind('click', function(e) { - if ($this_field.is('.enum')) { - $this_field.find('select').attr('value', ''); - } else if ($this_field.is('.set')) { - $this_field.find('select').find('option').each(function() { - var $option = $(this); - $option.attr('selected', false); - }) - } else if ($this_field.is('.relation')) { - // if the dropdown is there to select the foreign value - if ($this_field.find('select').length > 0) { - $this_field.find('select').attr('value', ''); - // if foriegn value is selected by browsing foreing values - } else { - $this_field.find('span.curr_value').empty(); - } - } else { - $this_field.find('textarea').val(''); - } - }) - - } else { - $this_field_span.html('
    '); - } - - // In each input sibling, wrap the current value in a textarea - // and store the current value in a hidden span - if($this_field.is(':not(.truncated, .transformed, .relation, .enum, .set, .null)')) { - // handle non-truncated, non-transformed, non-relation values - - value = data_value.replace("
    ", "\n"); - // We don't need to get any more data, just wrap the value - $this_field_span.append(''); - $this_field.data('original_data', data_value); - } - else if($this_field.is('.truncated, .transformed')) { - /** @lends jQuery */ - //handle truncated/transformed values values - - /** - * @var sql_query String containing the SQL query used to retrieve value of truncated/transformed data - */ - var sql_query = 'SELECT `' + field_name + '` FROM `' + window.parent.table + '` WHERE ' + PMA_urldecode(where_clause); - - // Make the Ajax call and get the data, wrap it and insert it - $.post('sql.php', { - 'token' : window.parent.token, - 'db' : window.parent.db, - 'ajax_request' : true, - 'sql_query' : sql_query, - 'inline_edit' : true - }, function(data) { - if(data.success == true) { - $this_field_span.append(''); - $this_field.data('original_data', data_value); - $("#sqlqueryresults").trigger('refreshgrid'); - } - else { - PMA_ajaxShowMessage(data.error); - } - }) // end $.post() - } - else if($this_field.is('.relation')) { - /** @lends jQuery */ - //handle relations - - /** - * @var post_params Object containing parameters for the POST request - */ - var post_params = { - 'ajax_request' : true, - 'get_relational_values' : true, - 'db' : window.parent.db, - 'table' : window.parent.table, - 'column' : field_name, - 'token' : window.parent.token, - 'curr_value' : relation_curr_value, - 'relation_key_or_display_column' : relation_key_or_display_column - } - - $.post('sql.php', post_params, function(data) { - $this_field_span.append(data.dropdown); - $this_field.data('original_data', data_value); - $("#sqlqueryresults").trigger('refreshgrid'); - }) // end $.post() - } - else if($this_field.is('.enum')) { - /** @lends jQuery */ - //handle enum fields - - /** - * @var post_params Object containing parameters for the POST request - */ - var post_params = { - 'ajax_request' : true, - 'get_enum_values' : true, - 'db' : window.parent.db, - 'table' : window.parent.table, - 'column' : field_name, - 'token' : window.parent.token, - 'curr_value' : curr_value - } - $.post('sql.php', post_params, function(data) { - $this_field_span.append(data.dropdown); - $this_field.data('original_data', data_value); - $("#sqlqueryresults").trigger('refreshgrid'); - }) // end $.post() - } - else if($this_field.is('.set')) { - /** @lends jQuery */ - //handle set fields - - /** - * @var post_params Object containing parameters for the POST request - */ - var post_params = { - 'ajax_request' : true, - 'get_set_values' : true, - 'db' : window.parent.db, - 'table' : window.parent.table, - 'column' : field_name, - 'token' : window.parent.token, - 'curr_value' : curr_value - } - - $.post('sql.php', post_params, function(data) { - $this_field_span.append(data.select); - $this_field.data('original_data', data_value); - $("#sqlqueryresults").trigger('refreshgrid'); - }) // end $.post() - } - else if($this_field.is('.null')) { - //handle null fields - $this_field_span.append(''); - $this_field.data('original_data', 'NULL'); - } - }); - - // refresh the grid - $("#sqlqueryresults").trigger('refreshgrid'); - - }) // End On click, replace the current field with an input/textarea - - /** - * After editing, clicking again should post data - * - * @memberOf jQuery - * @name inline_edit_save - * @see PMA_ajaxShowMessage() - * @see getFieldName() - */ - $(".inline_edit_active span a").live('click', function(event) { - /** @lends jQuery */ - - event.preventDefault(); - - /** - * @var $this_td Object referring to the td containing the - * "Inline Edit" link that was clicked to save the row that is - * being edited - * - */ - var $this_td = $(this).parents('td'); - var $test_element = ''; // to test the presence of a element - - // Initialize variables - var $input_siblings = $this_td.parent('tr').find('.inline_edit'); - var where_clause = $this_td.parent('tr').find('.where_clause').val(); - - /** - * @var nonunique Boolean, whether this row is unique or not - */ - if($this_td.is('.nonunique')) { - var nonunique = 0; - } - else { - var nonunique = 1; - } - - // Collect values of all fields to submit, we don't know which changed - /** - * @var relation_fields Array containing the name/value pairs of relational fields - */ - var relation_fields = {}; - /** - * @var relational_display string 'K' if relational key, 'D' if relational display column - */ - var relational_display = $("#relational_display_K").attr('checked') ? 'K' : 'D'; - /** - * @var transform_fields Array containing the name/value pairs for transformed fields - */ - var transform_fields = {}; - /** - * @var transformation_fields Boolean, if there are any transformed fields in this row - */ - var transformation_fields = false; - - /** - * @var sql_query String containing the SQL query to update this row - */ - var sql_query = 'UPDATE `' + window.parent.table + '` SET '; - - var need_to_post = false; - - var new_clause = ''; - var prev_index = -1; - - $input_siblings.each(function() { - /** @lends jQuery */ - /** - * @var this_field Object referring to this field () - */ - var $this_field = $(this); - - /** - * @var field_name String containing the name of this field. - * @see getFieldName() - */ - var field_name = getFieldName($this_field); - - /** - * @var this_field_params Array temporary storage for the name/value of current field - */ - var this_field_params = {}; - - if($this_field.is('.transformed')) { - transformation_fields = true; - } - /** - * @var is_null String capturing whether 'checkbox_null__' is checked. - */ - var is_null = $this_field.find('input:checkbox').is(':checked'); - var value; - var addQuotes = true; - - if (is_null) { - sql_query += ' `' + field_name + "`=NULL , "; - need_to_post = true; - } else { - if($this_field.is(":not(.relation, .enum, .set, .bit)")) { - this_field_params[field_name] = $this_field.find('textarea').val(); - if($this_field.is('.transformed')) { - $.extend(transform_fields, this_field_params); - } - } else if ($this_field.is('.bit')) { - this_field_params[field_name] = '0b' + $this_field.find('textarea').val(); - addQuotes = false; - } else if ($this_field.is('.set')) { - $test_element = $this_field.find('select'); - this_field_params[field_name] = $test_element.map(function(){ - return $(this).val(); - }).get().join(","); - } else { - // results from a drop-down - $test_element = $this_field.find('select'); - if ($test_element.length != 0) { - this_field_params[field_name] = $test_element.val(); - } - - // results from Browse foreign value - $test_element = $this_field.find('span.curr_value'); - if ($test_element.length != 0) { - this_field_params[field_name] = $test_element.text(); - } - - if($this_field.is('.relation')) { - $.extend(relation_fields, this_field_params); - } - } - if(where_clause.indexOf(field_name) > prev_index){ - new_clause += '`' + window.parent.table + '`.' + '`' + field_name + "` = '" + this_field_params[field_name].replace(/'/g,"''") + "'" + ' AND '; - } - if (this_field_params[field_name] != $this_field.data('original_data')) { - if (addQuotes == true) { - sql_query += ' `' + field_name + "`='" + this_field_params[field_name].replace(/'/g, "''") + "', "; - } else { - sql_query += ' `' + field_name + "`=" + this_field_params[field_name].replace(/'/g, "''") + ", "; - } - need_to_post = true; - } - } - }) - - /* - * update the where_clause, remove the last appended ' AND ' - * */ - - //Remove the last ',' appended in the above loop - sql_query = sql_query.replace(/,\s$/, ''); - //Fix non-escaped backslashes - sql_query = sql_query.replace(/\\/g, '\\\\'); - new_clause = new_clause.substring(0, new_clause.length-5); - new_clause = PMA_urlencode(new_clause); - sql_query += ' WHERE ' + PMA_urldecode(where_clause); - // Avoid updating more than one row in case there is no primary key - // (happened only for duplicate rows) - sql_query += ' LIMIT 1'; - /** - * @var rel_fields_list String, url encoded representation of {@link relations_fields} - */ - var rel_fields_list = $.param(relation_fields); - - /** - * @var transform_fields_list String, url encoded representation of {@link transform_fields} - */ - var transform_fields_list = $.param(transform_fields); - - // if inline_edit is successful, we need to go back to default view - var $del_hide = $(this).parent(); - var $chg_submit = $(this); - - if (need_to_post) { - // Make the Ajax post after setting all parameters - /** - * @var post_params Object containing parameters for the POST request - */ - var post_params = {'ajax_request' : true, - 'sql_query' : sql_query, - 'token' : window.parent.token, - 'db' : window.parent.db, - 'table' : window.parent.table, - 'clause_is_unique' : nonunique, - 'where_clause' : where_clause, - 'rel_fields_list' : rel_fields_list, - 'do_transformations' : transformation_fields, - 'transform_fields_list' : transform_fields_list, - 'relational_display' : relational_display, - 'goto' : 'sql.php', - 'submit_type' : 'save' - }; - - $.post('tbl_replace.php', post_params, function(data) { - if(data.success == true) { - PMA_ajaxShowMessage(data.message); - $this_td.parent('tr').find('.where_clause').attr('value', new_clause); - // remove possible previous feedback message - $('#result_query').remove(); - if (typeof data.sql_query != 'undefined') { - // display feedback - $('#sqlqueryresults').prepend(data.sql_query); - } - PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data); - } else { - PMA_ajaxShowMessage(data.error); - }; - }) // end $.post() - } else { - // no posting was done but still need to display the row - // in its previous format - PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, ''); - } - }) // End After editing, clicking again should post data - /** * Ajax Event for table row change * */ @@ -1088,102 +482,6 @@ $(document).ready(function() { }, 'top.frame_content') // end $(document).ready() -/** - * Visually put back the row in the state it was before entering Inline edit - * - * (when called in the situation where no posting was done, the data - * parameter is empty) - */ -function PMA_unInlineEditRow($del_hide, $chg_submit, $this_td, $input_siblings, data) -{ - - // deleting the hide button. remove

    tags - $del_hide.find('a, br').remove(); - // append inline edit button. - $del_hide.append($data_a.clone()); - - // changing inline_edit_active to inline_edit_anchor - $this_td.removeClass('inline_edit_active').addClass('inline_edit_anchor'); - - // removing hover, marked and noclick classes - $this_td.parent('tr').removeClass('noclick'); - $this_td.parent('tr').removeClass('hover').find('td').removeClass('hover'); - - $input_siblings.each(function() { - // Inline edit post has been successful. - $this_sibling = $(this); - $this_sibling_span = $(this).children('span'); - - var is_null = $this_sibling.find('input:checkbox').is(':checked'); - if (is_null) { - $this_sibling_span.html('NULL'); - $this_sibling.addClass('null'); - } else { - $this_sibling.removeClass('null'); - if($this_sibling.is(':not(.relation, .enum, .set)')) { - /** - * @var new_html String containing value of the data field after edit - */ - var new_html = $this_sibling.find('textarea').val(); - - if($this_sibling.is('.transformed')) { - var field_name = getFieldName($this_sibling); - if (typeof data.transformations != 'undefined') { - $.each(data.transformations, function(key, value) { - if(key == field_name) { - if($this_sibling.is('.text_plain, .application_octetstream')) { - new_html = value; - return false; - } else { - var new_value = $this_sibling.find('textarea').val(); - new_html = $(value).append(new_value); - return false; - } - } - }) - } - } - } else { - var new_html = ''; - var new_value = ''; - $test_element = $this_sibling.find('select'); - if ($test_element.length != 0) { - new_value = $test_element.val(); - } - $test_element = $this_sibling.find('span.curr_value'); - if ($test_element.length != 0) { - new_value = $test_element.text(); - } - - if($this_sibling.is('.relation')) { - var field_name = getFieldName($this_sibling); - if (typeof data.relations != 'undefined') { - $.each(data.relations, function(key, value) { - if(key == field_name) { - new_html = $(value); - return false; - } - }) - } - } else if ($this_sibling.is('.enum')) { - new_html = new_value; - } else if ($this_sibling.is('.set')) { - if (new_value != null) { - $.each(new_value, function(key, value) { - new_html = new_html + value + ','; - }) - new_html = new_html.substring(0, new_html.length-1); - } - } - } - $this_sibling_span.html(new_html); - } - }) - - // refresh the grid - $("#sqlqueryresults").trigger('refreshgrid'); -} - /** * Starting from some th, change the class of all td under it. * If isAddClass is specified, it will be used to determine whether to add or remove the class. diff --git a/js/tbl_select.js b/js/tbl_select.js index cb0c67044f..3bc1137c5d 100644 --- a/js/tbl_select.js +++ b/js/tbl_select.js @@ -66,7 +66,6 @@ $(document).ready(function() { if (typeof response == 'string') { // found results $("#sqlqueryresults").html(response); - $("#sqlqueryresults").trigger('appendAnchor'); $("#sqlqueryresults").trigger('makegrid'); $('#tbl_search_form') // work around for bug #3168569 - Issue on toggling the "Hide search criteria" in chrome. diff --git a/libraries/RecentTable.class.php b/libraries/RecentTable.class.php index bfb60818fa..def9c37c03 100644 --- a/libraries/RecentTable.class.php +++ b/libraries/RecentTable.class.php @@ -82,7 +82,7 @@ class PMA_RecentTable $row = PMA_DBI_fetch_array(PMA_query_as_controluser($sql_query)); if (isset($row[0])) { - return json_decode($row[0]); + return json_decode($row[0], true); } else { return array(); } @@ -142,7 +142,8 @@ class PMA_RecentTable $html = ''; if (count($this->tables)) { foreach ($this->tables as $table) { - $html .= ''; + $html .= ''; } } else { $html .= ''; @@ -159,7 +160,7 @@ class PMA_RecentTable { $html = ''; - $html .= ''; $html .= $this->getHtmlSelectOption(); $html .= ''; @@ -176,12 +177,14 @@ class PMA_RecentTable */ public function add($db, $table) { - $table_str = $db . '.' . $table; + $table_arr = array(); + $table_arr['db'] = $db; + $table_arr['table'] = $table; // add only if this is new table - if (! isset($this->tables[0]) || $this->tables[0] != $table_str) { - array_unshift($this->tables, $table_str); - $this->tables = array_merge(array_unique($this->tables)); + if (! isset($this->tables[0]) || $this->tables[0] != $table_arr) { + array_unshift($this->tables, $table_arr); + $this->tables = array_merge(array_unique($this->tables, SORT_REGULAR)); $this->trim(); if (isset($this->pma_table)) { return $this->saveToDb(); diff --git a/libraries/common.inc.php b/libraries/common.inc.php index 5f56352aed..fd21708d0c 100644 --- a/libraries/common.inc.php +++ b/libraries/common.inc.php @@ -510,21 +510,22 @@ if (PMA_isValid($_REQUEST['db'])) { */ $GLOBALS['table'] = ''; if (PMA_isValid($_REQUEST['table'])) { - // check if specified table contain db name - if (strpos($_REQUEST['table'], '.')) { - $splitted = explode('.', $_REQUEST['table']); - if (count($splitted) == 2) { // make sure the format is "db.table" - $GLOBALS['db'] = $splitted[0]; - $GLOBALS['url_params']['db'] = $GLOBALS['db']; - $GLOBALS['table'] = $splitted[1]; - $GLOBALS['url_params']['table'] = $GLOBALS['table']; - } - } else { - // can we strip tags from this? - // only \ and / is not allowed in table names for MySQL - $GLOBALS['table'] = $_REQUEST['table']; - $GLOBALS['url_params']['table'] = $GLOBALS['table']; - } + // can we strip tags from this? + // only \ and / is not allowed in table names for MySQL + $GLOBALS['table'] = $_REQUEST['table']; + $GLOBALS['url_params']['table'] = $GLOBALS['table']; +} + +/** + * Store currently selected recent table. + * Affect $GLOBALS['db'] and $GLOBALS['table'] + */ +if (PMA_isValid($_REQUEST['selected_recent_table'])) { + $recent_table = json_decode($_REQUEST['selected_recent_table'], true); + $GLOBALS['db'] = $recent_table['db']; + $GLOBALS['url_params']['db'] = $GLOBALS['db']; + $GLOBALS['table'] = $recent_table['table']; + $GLOBALS['url_params']['table'] = $GLOBALS['table']; } /** @@ -1008,16 +1009,16 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) { } /** - * @global boolean $GLOBALS['inline_edit'] + * @global boolean $GLOBALS['grid_edit'] * - * Set to true if this is a request made during an inline edit process. This + * Set to true if this is a request made during an grid edit process. This * request is made to retrieve the non-truncated/transformed values. */ -if (isset($_REQUEST['inline_edit']) && $_REQUEST['inline_edit'] == true) { - $GLOBALS['inline_edit'] = true; +if (isset($_REQUEST['grid_edit']) && $_REQUEST['grid_edit'] == true) { + $GLOBALS['grid_edit'] = true; } else { - $GLOBALS['inline_edit'] = false; + $GLOBALS['grid_edit'] = false; } if (!empty($__redirect) && in_array($__redirect, $goto_whitelist)) { diff --git a/libraries/common.lib.php b/libraries/common.lib.php index 12917edb4a..e5c90de728 100644 --- a/libraries/common.lib.php +++ b/libraries/common.lib.php @@ -1979,9 +1979,15 @@ function PMA_getUniqueCondition($handle, $fields_cnt, $fields_meta, $row, $force $unique_key = ''; $nonprimary_condition = ''; $preferred_condition = ''; + $primary_key_array = array(); + $unique_key_array = array(); + $nonprimary_condition_array = array(); + $condition_array = array(); for ($i = 0; $i < $fields_cnt; ++$i) { $condition = ''; + $con_key = ''; + $con_val = ''; $field_flags = PMA_DBI_field_flags($handle, $i); $meta = $fields_meta[$i]; @@ -2023,20 +2029,21 @@ function PMA_getUniqueCondition($handle, $fields_cnt, $fields_meta, $row, $force // (also, the syntax "CONCAT(field) IS NULL" // that we need on the next "if" will work) if ($meta->type == 'real') { - $condition = ' CONCAT(' . PMA_backquote($meta->table) . '.' - . PMA_backquote($meta->orgname) . ') '; + $con_key = 'CONCAT(' . PMA_backquote($meta->table) . '.' + . PMA_backquote($meta->orgname) . ')'; } else { - $condition = ' ' . PMA_backquote($meta->table) . '.' - . PMA_backquote($meta->orgname) . ' '; + $con_key = PMA_backquote($meta->table) . '.' + . PMA_backquote($meta->orgname); } // end if... else... + $condition = ' ' . $con_key . ' '; if (! isset($row[$i]) || is_null($row[$i])) { - $condition .= 'IS NULL AND'; + $con_val = 'IS NULL'; } else { // timestamp is numeric on some MySQL 4.1 // for real we use CONCAT above and it should compare to string if ($meta->numeric && $meta->type != 'timestamp' && $meta->type != 'real') { - $condition .= '= ' . $row[$i] . ' AND'; + $con_val = '= ' . $row[$i]; } elseif (($meta->type == 'blob' || $meta->type == 'string') // hexify only if this is a true not empty BLOB or a BINARY && stristr($field_flags, 'BINARY') @@ -2045,25 +2052,29 @@ function PMA_getUniqueCondition($handle, $fields_cnt, $fields_meta, $row, $force if (strlen($row[$i]) < 1000) { // use a CAST if possible, to avoid problems // if the field contains wildcard characters % or _ - $condition .= '= CAST(0x' . bin2hex($row[$i]) - . ' AS BINARY) AND'; + $con_val = '= CAST(0x' . bin2hex($row[$i]) . ' AS BINARY)'; } else { // this blob won't be part of the final condition - $condition = ''; + $con_val = null; } } elseif ($meta->type == 'bit') { - $condition .= "= b'" . PMA_printable_bit_value($row[$i], $meta->length) . "' AND"; + $con_val = "= b'" . PMA_printable_bit_value($row[$i], $meta->length) . "'"; } else { - $condition .= '= \'' - . PMA_sqlAddSlashes($row[$i], false, true) . '\' AND'; + $con_val = '= \'' . PMA_sqlAddSlashes($row[$i], false, true) . '\''; } } - if ($meta->primary_key > 0) { - $primary_key .= $condition; - } elseif ($meta->unique_key > 0) { - $unique_key .= $condition; + if ($con_val != null) { + $condition .= $con_val . ' AND'; + if ($meta->primary_key > 0) { + $primary_key .= $condition; + $primary_key_array[$con_key] = $con_val; + } elseif ($meta->unique_key > 0) { + $unique_key .= $condition; + $unique_key_array[$con_key] = $con_val; + } + $nonprimary_condition .= $condition; + $nonprimary_condition_array[$con_key] = $con_val; } - $nonprimary_condition .= $condition; } // end for // Correction University of Virginia 19991216: @@ -2072,15 +2083,18 @@ function PMA_getUniqueCondition($handle, $fields_cnt, $fields_meta, $row, $force $clause_is_unique = true; if ($primary_key) { $preferred_condition = $primary_key; + $condition_array = $primary_key_array; } elseif ($unique_key) { $preferred_condition = $unique_key; + $condition_array = $unique_key_array; } elseif (! $force_unique) { $preferred_condition = $nonprimary_condition; + $condition_array = $nonprimary_condition_array; $clause_is_unique = false; } $where_clause = trim(preg_replace('|\s?AND$|', '', $preferred_condition)); - return(array($where_clause, $clause_is_unique)); + return(array($where_clause, $clause_is_unique, $condition_array)); } // end function /** diff --git a/libraries/config.default.php b/libraries/config.default.php index 6166e833c7..0568e2d259 100644 --- a/libraries/config.default.php +++ b/libraries/config.default.php @@ -543,6 +543,13 @@ $cfg['MaxDbList'] = 100; */ $cfg['MaxTableList'] = 250; +/** + * whether to show hint or not + * + * @global boolean $cfg['ShowHint'] + */ +$cfg['ShowHint'] = true; + /** * maximum number of characters when a SQL query is displayed * @@ -2258,7 +2265,7 @@ $cfg['CharTextareaRows'] = 2; $cfg['LimitChars'] = 50; /** - * Where to show the edit/inline_edit/delete links in browse mode + * Where to show the edit/copy/delete links in browse mode * Possible values are 'left', 'right', 'both' and 'none'; * which will be interpreted as 'top', 'bottom', 'both' and 'none' * respectively for vertical display mode @@ -2312,6 +2319,11 @@ $cfg['ShowBrowseComments'] = true; */ $cfg['ShowPropertyComments']= true; +/** + * save edited cell(s) in browse-mode at once. + */ +$cfg['SaveCellsAtOnce'] = false; + /** * shows table display direction. */ diff --git a/libraries/config/messages.inc.php b/libraries/config/messages.inc.php index 5d42f89c86..6cb9f20fc8 100644 --- a/libraries/config/messages.inc.php +++ b/libraries/config/messages.inc.php @@ -316,7 +316,7 @@ $strConfigMcryptDisableWarning_desc = __('Disable the default warning that is di $strConfigMcryptDisableWarning_name = __('mcrypt warning'); $strConfigMemoryLimit_desc = __('The number of bytes a script is allowed to allocate, eg. [kbd]32M[/kbd] ([kbd]0[/kbd] for no limit)'); $strConfigMemoryLimit_name = __('Memory limit'); -$strConfigRowActionLinks_desc = __('These are Edit, Inline edit, Copy and Delete links'); +$strConfigRowActionLinks_desc = __('These are Edit, Copy and Delete links'); $strConfigRowActionLinks_name = __('Where to show the table row links'); $strConfigNaturalOrder_desc = __('Use natural order for sorting table and database names'); $strConfigNaturalOrder_name = __('Natural order'); @@ -354,6 +354,7 @@ $strConfigRepeatCells_name = __('Repeat headers'); $strConfigReplaceHelpImg_desc = __('Show help button instead of Documentation text'); $strConfigReplaceHelpImg_name = __('Show help button'); $strConfigRestoreDefaultValue = __('Restore default value'); +$strConfigSaveCellsAtOnce_name = __('Save all edited cells at once'); $strConfigSaveDir_desc = __('Directory where exports can be saved on server'); $strConfigSaveDir_name = __('Save directory'); $strConfigServers_AllowDeny_order_desc = __('Leave blank if not used'); @@ -456,6 +457,8 @@ $strConfigShowFieldTypesInDataEditView_desc = __('Defines whether or not type fi $strConfigShowFieldTypesInDataEditView_name = __('Show field types'); $strConfigShowFunctionFields_desc = __('Display the function fields in edit/insert mode'); $strConfigShowFunctionFields_name = __('Show function fields'); +$strConfigShowHint_desc = __('Whether to show hint or not'); +$strConfigShowHint_name = __('Show hint'); $strConfigShowPhpInfo_desc = __('Shows link to [a@http://php.net/manual/function.phpinfo.php]phpinfo()[/a] output'); $strConfigShowPhpInfo_name = __('Show phpinfo() link'); $strConfigShowServerInfo_name = __('Show detailed MySQL server information'); diff --git a/libraries/config/setup.forms.php b/libraries/config/setup.forms.php index 5f3f729de5..8b71eff7ec 100644 --- a/libraries/config/setup.forms.php +++ b/libraries/config/setup.forms.php @@ -130,6 +130,7 @@ $forms['Features']['Other_core_settings'] = array( 'ReplaceHelpImg', 'MaxDbList', 'MaxTableList', + 'ShowHint', 'OBGzip', 'PersistentConnections', 'ExecTimeLimit', @@ -198,6 +199,7 @@ $forms['Main_frame']['Browse'] = array( 'Order', 'BrowsePointerEnable', 'BrowseMarkerEnable', + 'SaveCellsAtOnce', 'ShowDisplayDirection', 'RepeatCells', 'LimitChars', diff --git a/libraries/config/user_preferences.forms.php b/libraries/config/user_preferences.forms.php index 8dc78feaa9..7cefa4ac40 100644 --- a/libraries/config/user_preferences.forms.php +++ b/libraries/config/user_preferences.forms.php @@ -34,7 +34,8 @@ $forms['Features']['General'] = array( 'SkipLockedTables', 'DisableMultiTableMaintenance', 'MaxDbList', - 'MaxTableList'); + 'MaxTableList', + 'ShowHint'); $forms['Features']['Text_fields'] = array( 'CharEditing', 'CharTextareaCols', @@ -108,6 +109,7 @@ $forms['Main_frame']['Browse'] = array( 'DisplayBinaryAsHex', 'BrowsePointerEnable', 'BrowseMarkerEnable', + 'SaveCellsAtOnce', 'ShowDisplayDirection', 'RepeatCells', 'LimitChars', diff --git a/libraries/display_tbl.lib.php b/libraries/display_tbl.lib.php index 9ae8bb6d82..3ec67e5672 100644 --- a/libraries/display_tbl.lib.php +++ b/libraries/display_tbl.lib.php @@ -396,30 +396,17 @@ function PMA_displayTableNavigation($pos_next, $pos_prev, $sql_query, $id_for_di echo ''; } ?> + +
    + + +
    +
    - getUiProp(PMA_Table::PROP_COLUMN_ORDER); - if ($col_order) { - echo ''; - } - $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB); - if ($col_visib) { - echo ''; - } - // generate table create time - if (! PMA_Table::isView($GLOBALS['table'], $GLOBALS['db'])) { - echo ''; - } - } - ?> '; + echo '
    '; + echo PMA_generate_common_hidden_inputs($db, $table); + echo '
    '; + // Output data needed for column reordering and show/hide column + if (PMA_isSelect()) { + // generate the column order, if it is set + $pmatable = new PMA_Table($GLOBALS['table'], $GLOBALS['db']); + $col_order = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_ORDER); + if ($col_order) { + echo ''; + } + $col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB); + if ($col_visib) { + echo ''; + } + // generate table create time + echo ''; + } + + $vertical_display['emptypre'] = 0; $vertical_display['emptyafter'] = 0; $vertical_display['textbtn'] = ''; @@ -1092,7 +1102,7 @@ function PMA_buildValueDisplay($class, $condition_field, $value) */ function PMA_buildNullDisplay($class, $condition_field) { - // the null class is needed for inline editing + // the null class is needed for grid editing return 'NULL'; } @@ -1217,8 +1227,8 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) $vertical_display['delete'] = array(); $vertical_display['data'] = array(); $vertical_display['row_delete'] = array(); - // name of the class added to all inline editable elements - $inline_edit_class = 'inline_edit'; + // name of the class added to all grid editable elements + $grid_edit_class = 'grid_edit'; // prepare to get the column order, if available if (PMA_isSelect()) { @@ -1286,7 +1296,7 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) * with only one field and it's a BLOB; in this case, * avoid to display the delete and edit links */ - list($where_clause, $clause_is_unique) = PMA_getUniqueCondition($dt_result, $fields_cnt, $fields_meta, $row); + list($where_clause, $clause_is_unique, $condition_array) = PMA_getUniqueCondition($dt_result, $fields_cnt, $fields_meta, $row); $where_clause_html = urlencode($where_clause); // 1.2 Defines the URLs for the modify/delete link(s) @@ -1316,7 +1326,7 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) $edit_str = PMA_getIcon('b_edit.png', __('Edit')); $copy_str = PMA_getIcon('b_insrow.png', __('Copy')); - // Class definitions required for inline editing jQuery scripts + // Class definitions required for grid editing jQuery scripts $edit_anchor_class = "edit_row_anchor"; if ( $clause_is_unique == 0) { $edit_anchor_class .= ' nonunique'; @@ -1378,14 +1388,14 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) if (! isset($js_conf)) { $js_conf = ''; } - echo PMA_generateCheckboxAndLinks('left', $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf); + echo PMA_generateCheckboxAndLinks('left', $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $condition_array, $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf); } else if (($GLOBALS['cfg']['RowActionLinks'] == 'none') && ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' || $_SESSION['tmp_user_values']['disp_direction'] == 'horizontalflipped')) { if (! isset($js_conf)) { $js_conf = ''; } - echo PMA_generateCheckboxAndLinks('none', $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf); + echo PMA_generateCheckboxAndLinks('none', $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $condition_array, $del_query, 'l', $edit_url, $copy_url, $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf); } // end if (1.3) } // end if (1) @@ -1405,7 +1415,7 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) $is_field_truncated = false; //If the previous column had blob data, we need to reset the class // to $inline_edit_class - $class = 'data ' . $inline_edit_class . ' ' . $not_null_class . ' ' . $relation_class; //' ' . $alternating_color_class . + $class = 'data ' . $grid_edit_class . ' ' . $not_null_class . ' ' . $relation_class . ' ' . $hide_class; //' ' . $alternating_color_class . // See if this column should get highlight because it's used in the // where-query. @@ -1490,10 +1500,10 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) // TEXT fields type so we have to ensure it's really a BLOB $field_flags = PMA_DBI_field_flags($dt_result, $i); - // remove 'inline_edit' from $class as we can't edit binary data. - $class = str_replace('inline_edit', '', $class); - if (stristr($field_flags, 'BINARY')) { + // remove 'grid_edit' from $class as we can't edit binary data. + $class = str_replace('grid_edit', '', $class); + if (! isset($row[$i]) || is_null($row[$i])) { $vertical_display['data'][$row_no][$i] = PMA_buildNullDisplay($class, $condition_field); } else { @@ -1522,7 +1532,11 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) // characters for tabulations and / $row[$i] = ($default_function != $transform_function ? $transform_function($row[$i], $transform_options, $meta) : $default_function($row[$i], array(), $meta)); - $vertical_display['data'][$row_no][$i] = PMA_buildValueDisplay($class, $condition_field, $row[$i]); + if ($is_field_truncated) { + $class .= ' truncated'; + } + + $vertical_display['data'][$row_no][$i] = PMA_buildValueDisplay($class, $condition_field, $row[$i]); } else { $vertical_display['data'][$row_no][$i] = PMA_buildEmptyDisplay($class, $condition_field, $meta); } @@ -1530,8 +1544,8 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) // g e o m e t r y } elseif ($meta->type == 'geometry') { - // Remove 'inline_edit' from $class as we do not allow to inline-edit geometry data. - $class = str_replace('inline_edit', '', $class); + // Remove 'grid_edit' from $class as we do not allow to inline-edit geometry data. + $class = str_replace('grid_edit', '', $class); // Display as [GEOMETRY - (size)] if ('GEOM' == $_SESSION['tmp_user_values']['geometry_display']) { @@ -1666,7 +1680,7 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) if (! isset($js_conf)) { $js_conf = ''; } - echo PMA_generateCheckboxAndLinks('right', $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $del_query, 'r', $edit_url, $copy_url, $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf); + echo PMA_generateCheckboxAndLinks('right', $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $condition_array, $del_query, 'r', $edit_url, $copy_url, $edit_anchor_class, $edit_str, $copy_str, $del_str, $js_conf); } // end if (3) if ($_SESSION['tmp_user_values']['disp_direction'] == 'horizontal' @@ -1693,7 +1707,7 @@ function PMA_displayTableBody(&$dt_result, &$is_display, $map, $analyzed_sql) } if (!empty($del_url) && $is_display['del_lnk'] != 'kp') { - $vertical_display['row_delete'][$row_no] .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $del_query, '[%_PMA_CHECKBOX_DIR_%]', $alternating_color_class . $vertical_class); + $vertical_display['row_delete'][$row_no] .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $condition_array, $del_query, '[%_PMA_CHECKBOX_DIR_%]', $alternating_color_class . $vertical_class); } else { unset($vertical_display['row_delete'][$row_no]); } @@ -2712,7 +2726,7 @@ function PMA_prepare_row_data($class, $condition_field, $analyzed_sql, $meta, $m * @return string the generated HTML */ -function PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $del_query, $id_suffix, $class) +function PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $condition_array, $del_query, $id_suffix, $class) { $ret = ''; if (! empty($del_url) && $is_display['del_lnk'] != 'kp') { @@ -2724,6 +2738,7 @@ function PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_cla . '' + . '' . ' '; } return $ret; @@ -2747,7 +2762,7 @@ function PMA_generateEditLink($edit_url, $class, $edit_str, $where_clause, $wher . PMA_linkOrButton($edit_url, $edit_str, array(), false); /* * Where clause for selecting this row uniquely is provided as - * a hidden input. Used by jQuery scripts for handling inline editing + * a hidden input. Used by jQuery scripts for handling grid editing */ if (! empty($where_clause)) { $ret .= ''; @@ -2778,7 +2793,7 @@ function PMA_generateCopyLink($copy_url, $copy_str, $where_clause, $where_clause . PMA_linkOrButton($copy_url, $copy_str, array(), false); /* * Where clause for selecting this row uniquely is provided as - * a hidden input. Used by jQuery scripts for handling inline editing + * a hidden input. Used by jQuery scripts for handling grid editing */ if (! empty($where_clause)) { $ret .= ''; @@ -2832,12 +2847,12 @@ function PMA_generateDeleteLink($del_url, $del_str, $js_conf, $class) * @param string $js_conf * @return string the generated HTML */ -function PMA_generateCheckboxAndLinks($position, $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $del_query, $id_suffix, $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf) +function PMA_generateCheckboxAndLinks($position, $del_url, $is_display, $row_no, $where_clause, $where_clause_html, $condition_array, $del_query, $id_suffix, $edit_url, $copy_url, $class, $edit_str, $copy_str, $del_str, $js_conf) { $ret = ''; if ($position == 'left') { - $ret .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $del_query, $id_suffix='_left', '', '', ''); + $ret .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $condition_array, $del_query, $id_suffix='_left', '', '', ''); $ret .= PMA_generateEditLink($edit_url, $class, $edit_str, $where_clause, $where_clause_html, ''); @@ -2852,9 +2867,9 @@ function PMA_generateCheckboxAndLinks($position, $del_url, $is_display, $row_no, $ret .= PMA_generateEditLink($edit_url, $class, $edit_str, $where_clause, $where_clause_html, ''); - $ret .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $del_query, $id_suffix='_right', '', '', ''); + $ret .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $condition_array, $del_query, $id_suffix='_right', '', '', ''); } else { // $position == 'none' - $ret .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $del_query, $id_suffix='_left', '', '', ''); + $ret .= PMA_generateCheckboxForMulti($del_url, $is_display, $row_no, $where_clause_html, $condition_array, $del_query, $id_suffix='_left', '', '', ''); } return $ret; } diff --git a/libraries/header.inc.php b/libraries/header.inc.php index 3fbfc0d7fd..eb17e4cc98 100644 --- a/libraries/header.inc.php +++ b/libraries/header.inc.php @@ -97,6 +97,12 @@ if (isset($GLOBALS['is_ajax_request']) && !$GLOBALS['is_ajax_request']) { define('PMA_DISPLAY_HEADING', 1); } + // pass configuration for hint tooltip display + // (to be used by PMA_createqTip in js/functions.js) + if (! $GLOBALS['cfg']['ShowHint']) { + echo ''; + } + /** * Display heading if needed. Design can be set in css file. */ diff --git a/libraries/tbl_replace_fields.inc.php b/libraries/tbl_replace_fields.inc.php index 32da9c085d..f1a0b9ed02 100644 --- a/libraries/tbl_replace_fields.inc.php +++ b/libraries/tbl_replace_fields.inc.php @@ -95,7 +95,7 @@ if (false !== $possibly_uploaded_val) { // Was the Null checkbox checked for this field? // (if there is a value, we ignore the Null checkbox: this could // be possible if Javascript is disabled in the browser) - if (isset($me_fields_null[$key]) + if (! empty($me_fields_null[$key]) && ($val == "''" || $val == '')) { $val = 'NULL'; } diff --git a/sql.php b/sql.php index 2ce10ea0db..9b733ccce4 100644 --- a/sql.php +++ b/sql.php @@ -58,7 +58,7 @@ if (isset($fields['dbase'])) { } /** - * During inline edit, if we have a relational field, show the dropdown for it + * During grid edit, if we have a relational field, show the dropdown for it * * Logic taken from libraries/display_tbl_lib.php * @@ -104,7 +104,7 @@ if (isset($_REQUEST['get_relational_values']) && $_REQUEST['get_relational_value } /** - * Just like above, find possible values for enum fields during inline edit. + * Just like above, find possible values for enum fields during grid edit. * * Logic taken from libraries/display_tbl_lib.php */ @@ -133,7 +133,7 @@ if (isset($_REQUEST['get_enum_values']) && $_REQUEST['get_enum_values'] == true) } /** - * Find possible values for set fields during inline edit. + * Find possible values for set fields during grid edit. */ if (isset($_REQUEST['get_set_values']) && $_REQUEST['get_set_values'] == true) { $field_info_query = 'SHOW FIELDS FROM `' . $db . '`.`' . $table . '` LIKE \'' . $_REQUEST['column'] . '\' ;'; @@ -696,7 +696,7 @@ if (0 == $num_rows || $is_affected) { if ($GLOBALS['is_ajax_request'] == true) { /** - * If we are in inline editing, we need to process the relational and + * If we are in grid editing, we need to process the relational and * transformed fields, if they were edited. After that, output the correct * link/transformed value and exit * @@ -859,7 +859,7 @@ if (0 == $num_rows || $is_affected) { else { //If we are retrieving the full value of a truncated field or the original // value of a transformed field, show it here and exit - if ($GLOBALS['inline_edit'] == true && $GLOBALS['cfg']['AjaxEnable']) { + if ($GLOBALS['grid_edit'] == true && $GLOBALS['cfg']['AjaxEnable']) { $row = PMA_DBI_fetch_row($result); $extra_data = array(); $extra_data['value'] = $row[0]; diff --git a/tbl_replace.php b/tbl_replace.php index 48d01fc6de..08750e43d8 100644 --- a/tbl_replace.php +++ b/tbl_replace.php @@ -271,8 +271,8 @@ foreach ($loop_array as $rownumber => $where_clause) { // avoid setting a field to NULL when it's already NULL // (field had the null checkbox before the update // field still has the null checkbox) - if (!(! empty($me_fields_null_prev[$key]) - && isset($me_fields_null[$key]))) { + if (empty($me_fields_null_prev[$key]) + || empty($me_fields_null[$key])) { $query_values[] = PMA_backquote($me_fields_name[$key]) . ' = ' . $cur_value; } } @@ -405,9 +405,130 @@ if (! empty($error_messages)) { unset($error_messages, $warning_messages, $total_affected_rows, $last_messages, $last_message); if ($GLOBALS['is_ajax_request'] == true) { + /** + * If we are in grid editing, we need to process the relational and + * transformed fields, if they were edited. After that, output the correct + * link/transformed value and exit + * + * Logic taken from libraries/display_tbl.lib.php + */ + + if (isset($_REQUEST['rel_fields_list']) && $_REQUEST['rel_fields_list'] != '') { + //handle relations work here for updated row. + require_once './libraries/relation.lib.php'; + + $map = PMA_getForeigners($db, $table, '', 'both'); + + $rel_fields = array(); + parse_str($_REQUEST['rel_fields_list'], $rel_fields); + + // loop for each relation cell + foreach ( $rel_fields as $cell_index => $curr_cell_rel_field) { + + foreach ( $curr_cell_rel_field as $rel_field => $rel_field_value) { + + $where_comparison = "='" . $rel_field_value . "'"; + $display_field = PMA_getDisplayField($map[$rel_field]['foreign_db'], $map[$rel_field]['foreign_table']); + + // Field to display from the foreign table? + if (isset($display_field) && strlen($display_field)) { + $dispsql = 'SELECT ' . PMA_backquote($display_field) + . ' FROM ' . PMA_backquote($map[$rel_field]['foreign_db']) + . '.' . PMA_backquote($map[$rel_field]['foreign_table']) + . ' WHERE ' . PMA_backquote($map[$rel_field]['foreign_field']) + . $where_comparison; + $dispresult = PMA_DBI_try_query($dispsql, null, PMA_DBI_QUERY_STORE); + if ($dispresult && PMA_DBI_num_rows($dispresult) > 0) { + list($dispval) = PMA_DBI_fetch_row($dispresult, 0); + } else { + //$dispval = __('Link not found'); + } + @PMA_DBI_free_result($dispresult); + } else { + $dispval = ''; + } // end if... else... + + if ('K' == $_SESSION['tmp_user_values']['relational_display']) { + // user chose "relational key" in the display options, so + // the title contains the display field + $title = (! empty($dispval))? ' title="' . htmlspecialchars($dispval) . '"' : ''; + } else { + $title = ' title="' . htmlspecialchars($rel_field_value) . '"'; + } + + $_url_params = array( + 'db' => $map[$rel_field]['foreign_db'], + 'table' => $map[$rel_field]['foreign_table'], + 'pos' => '0', + 'sql_query' => 'SELECT * FROM ' + . PMA_backquote($map[$rel_field]['foreign_db']) . '.' . PMA_backquote($map[$rel_field]['foreign_table']) + . ' WHERE ' . PMA_backquote($map[$rel_field]['foreign_field']) + . $where_comparison + ); + $output = '
    '; + + if ('D' == $_SESSION['tmp_user_values']['relational_display']) { + // user chose "relational display field" in the + // display options, so show display field in the cell + $output .= (!empty($dispval)) ? htmlspecialchars($dispval) : ''; + } else { + // otherwise display data in the cell + $output .= htmlspecialchars($rel_field_value); + } + $output .= ''; + $extra_data['relations'][$cell_index] = $output; + } + } // end of loop for each relation cell + } + + if (isset($_REQUEST['do_transformations']) && $_REQUEST['do_transformations'] == true ) { + require_once './libraries/transformations.lib.php'; + //if some posted fields need to be transformed, generate them here. + $mime_map = PMA_getMIME($db, $table); + + if ($mime_map === false) { + $mime_map = array(); + } + + $edited_values = array(); + parse_str($_REQUEST['transform_fields_list'], $edited_values); + + foreach($mime_map as $transformation) { + $include_file = PMA_securePath($transformation['transformation']); + $column_name = $transformation['column_name']; + + foreach ($edited_values as $cell_index => $curr_cell_edited_values) { + if (isset($curr_cell_edited_values[$column_name])) { + $column_data = $curr_cell_edited_values[$column_name]; + + $_url_params = array( + 'db' => $db, + 'table' => $table, + 'where_clause' => $_REQUEST['where_clause'], + 'transform_key' => $column_name, + ); + + if (file_exists('./libraries/transformations/' . $include_file)) { + $transformfunction_name = str_replace('.inc.php', '', $transformation['transformation']); + + require_once './libraries/transformations/' . $include_file; + + if (function_exists('PMA_transformation_' . $transformfunction_name)) { + $transform_function = 'PMA_transformation_' . $transformfunction_name; + $transform_options = PMA_transformation_getOptions((isset($transformation['transformation_options']) ? $transformation['transformation_options'] : '')); + $transform_options['wrapper_link'] = PMA_generate_common_url($_url_params); + } + } + + $extra_data['transformations'][$cell_index] = $transform_function($column_data, $transform_options); + } + } // end of loop for each transformation cell + } // end of loop for each $mime_map + } + /**Get the total row count of the table*/ $extra_data['row_count'] = PMA_Table::countRecords($_REQUEST['db'],$_REQUEST['table']); - $extra_data['sql_query'] = PMA_showMessage(NULL, $GLOBALS['display_query']); + $extra_data['sql_query'] = PMA_showMessage($message, $GLOBALS['display_query']); PMA_ajaxResponse($message, $message->isSuccess(), $extra_data); } diff --git a/themes/original/css/theme_right.css.php b/themes/original/css/theme_right.css.php index 862f85f2d1..257378b320 100644 --- a/themes/original/css/theme_right.css.php +++ b/themes/original/css/theme_right.css.php @@ -2456,7 +2456,7 @@ span.cm-number { font-weight: bold; } -.navigation input[type=submit]:hover { +.navigation input[type=submit]:hover, .navigation input.edit_mode_active { background: #333; color: white; cursor: pointer; @@ -2465,3 +2465,56 @@ span.cm-number { .navigation select { margin: 0 0.8em; } + +.cEdit { + margin: 0; + padding: 0; + position: absolute; +} + +.cEdit input[type=text] { + background: #FFF; + height: 100%; + margin: 0; + padding: 0; +} + +.cEdit .edit_area { + background: #FFF; + border: 1px solid #999; + min-width: 10em; + padding: 0.3em 0.5em; +} + +.cEdit .edit_area select, .cEdit .edit_area textarea { + width: 97%; +} + +.cEdit .cell_edit_hint { + color: #555; + font-size: 0.8em; + margin: 0.3em 0.2em; +} + +.cEdit .edit_area_loading { + background: #FFF url(getImgPath(); ?>ajax_clock_small.gif) no-repeat center; + height: 10em; +} + + +.cEdit .edit_area_posting { + background: #FFF url(getImgPath(); ?>ajax_clock_small.gif) no-repeat center top; + padding-top: 1.5em; +} + +.cEdit .goto_link { + background: #EEE; + color: #555; + padding: 0.2em 0.3em; +} + +.saving_edited_data { + background: url(./themes/pmahomme/img/ajax_clock_small.gif) no-repeat left; + padding-left: 20px; +} + diff --git a/themes/pmahomme/css/theme_right.css.php b/themes/pmahomme/css/theme_right.css.php index fc114ce358..39744450b6 100644 --- a/themes/pmahomme/css/theme_right.css.php +++ b/themes/pmahomme/css/theme_right.css.php @@ -2890,7 +2890,7 @@ span.cm-number { -moz-border-radius: 0; } -.navigation input[type=submit]:hover { +.navigation input[type=submit]:hover, .navigation input.edit_mode_active { color: white; cursor: pointer; text-shadow: none; @@ -2906,3 +2906,55 @@ span.cm-number { .navigation select { margin: 0 0.8em; } + +.cEdit { + margin: 0; + padding: 0; + position: absolute; +} + +.cEdit input[type=text] { + background: #FFF; + height: 100%; + margin: 0; + padding: 0; +} + +.cEdit .edit_area { + background: #FFF; + border: 1px solid #999; + min-width: 10em; + padding: 0.3em 0.5em; +} + +.cEdit .edit_area select, .cEdit .edit_area textarea { + width: 97%; +} + +.cEdit .cell_edit_hint { + color: #555; + font-size: 0.8em; + margin: 0.3em 0.2em; +} + +.cEdit .edit_area_loading { + background: #FFF url(./themes/pmahomme/img/ajax_clock_small.gif) no-repeat center; + height: 10em; +} + +.cEdit .edit_area_posting { + background: #FFF url(./themes/pmahomme/img/ajax_clock_small.gif) no-repeat center top; + padding-top: 1.5em; +} + +.cEdit .goto_link { + background: #EEE; + color: #555; + padding: 0.2em 0.3em; +} + +.saving_edited_data { + background: url(./themes/pmahomme/img/ajax_clock_small.gif) no-repeat left; + padding-left: 20px; +} +