Merge commit 'a9cd487f0f48ab9b070823545860f384cfb8a4e0'

This commit is contained in:
Marc Delisle 2011-08-10 09:37:26 -04:00
commit 9d28b4b1b0
23 changed files with 1278 additions and 839 deletions

View File

@ -1373,6 +1373,9 @@ CREATE DATABASE,ALTER DATABASE,DROP DATABASE</pre>
main panel's list (except on the Export page). This limit is also enforced in the navigation panel
when in Light mode.</dd>
<dt id="cfg_ShowHint">$cfg['ShowHint'] boolean</dt>
<dd>Whether to show the hints or not (for example, hints when hovering table header)</dd>
<dt id="cfg_MaxCharactersInDisplayedSQL">$cfg['MaxCharactersInDisplayedSQL'] integer</dt>
<dd>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'] =
<dt><span id="cfg_RowActionLinks">$cfg['RowActionLinks'] </span>string
</dt>
<dd>Defines the place where table row links (Edit, Inline edit, Copy,
<dd>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).
&quot;left&quot; and &quot;right&quot; are parsed as &quot;top&quot;
@ -2143,6 +2146,11 @@ setfacl -d -m "g:www-data:rwx" tmp
identify what they mean.
</dd>
<dt id="cfg_SaveCellsAtOnce">$cfg['SaveCellsAtOnce'] boolean</dt>
<dd>
Defines whether or not to save all edited cells at once in browse-mode.
</dd>
<dt id="cfg_ShowDisplayDirection">$cfg['ShowDisplayDirection'] boolean</dt>
<dd>
Defines whether or not type display direction option is shown

View File

@ -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();
}

View File

@ -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(/<br>/g, "\n");
} else {
return $(td).text();
}
}
$(document).ready(function() {
/**
* Theme selector.

View File

@ -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);
})(jQuery);

View File

@ -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 <td> 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 <td>, 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 <td>. If not specified, the function will
* use currently edited <td> 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 <br>
new_html = new_html.replace(/\n/g, '<br />');
$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('<div class="null_div">Null :<input type="checkbox"></div>');
var $checkbox = $editArea.find('.null_div input');
// check if current <td> is NULL
if ($td.is('.null')) {
$checkbox.attr('checked', true);
g.wasEditedCellNull = true;
}
// if the select/editor is changed un-check the 'checkbox_null_<field_name>_<row_index>'.
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_<field_name>_<row_index>' 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('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
}) // 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('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
}) // 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('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
}) // 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('<textarea>'+value+'</textarea>');
$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('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
} 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('<textarea>'+data.value+'</textarea>');
$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('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
}
else {
PMA_ajaxShowMessage(data.error);
}
}) // end $.post()
}
g.isEditCellTextEditable = true;
} else {
$editArea.append('<textarea>' + PMA_getCellValue(g.currentEditCell) + '</textarea>');
$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('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
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_<field_name>_<row_index>' 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_<field_name>_<row_index>' 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('<input type="text" /><div class="edit_area" />');
$(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();

View File

@ -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<br />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<br />to toggle column\'s visibility');
$js_messages['strShowAll'] = __('Show all');
echo "var PMA_messages = new Array();\n";
foreach ($js_messages as $name => $js_message) {

View File

@ -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);
}
});

720
js/sql.js
View File

@ -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 <input type="image"> 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()
* <li>Paginate the results table</li>
* <li>Sort the results table</li>
* <li>Change table according to display options</li>
* <li>Inline editing of data</li>
* <li>Grid editing of data</li>
* </ul>
*
* @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 <td>.
// For "hide" button the original data to be restored is
// kept in the jQuery data element 'original_data' inside the <td>.
// 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($('<br /><br />')).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 (<td>)
*/
var $this_field = $(this);
/**
* @var this_field_span Object referring to this field's child (<span>)
*/
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('<div class="null_div">Null :<input type="checkbox" class="checkbox_null_'+ field_name + '_' + this_row_index +'"></div>');
// check the 'checkbox_null_<field_name>_<row_index>' 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_<field_name>_<row_index>'.
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_<field_name>_<row_index>' 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('<div class="null_div"></div>');
}
// 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("<br>", "\n");
// We don't need to get any more data, just wrap the value
$this_field_span.append('<textarea>' + value + '</textarea>');
$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('<textarea>'+data.value+'</textarea>');
$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('<textarea></textarea>');
$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 (<td>)
*/
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_<field_name>_<row_index>' 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 <br><br><a> 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.

View File

@ -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.

View File

@ -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 = '<option value="">(' . __('Recent tables') . ') ...</option>';
if (count($this->tables)) {
foreach ($this->tables as $table) {
$html .= '<option value="' . htmlspecialchars($table) . '">' . htmlspecialchars($table) . '</option>';
$html .= '<option value="' . htmlspecialchars(json_encode($table)) . '">' .
htmlspecialchars('`' . $table['db'] . '`.`' . $table['table'] . '`') . '</option>';
}
} else {
$html .= '<option value="">' . __('There are no recent tables') . '</option>';
@ -159,7 +160,7 @@ class PMA_RecentTable
{
$html = '<input type="hidden" name="goto" id="LeftDefaultTabTable" value="' .
htmlspecialchars($GLOBALS['cfg']['LeftDefaultTabTable']) . '" />';
$html .= '<select name="table" id="recentTable">';
$html .= '<select name="selected_recent_table" id="recentTable">';
$html .= $this->getHtmlSelectOption();
$html .= '</select>';
@ -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();

View File

@ -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)) {

View File

@ -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
/**

View File

@ -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.
*/

View File

@ -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');

View File

@ -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',

View File

@ -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',

View File

@ -396,30 +396,17 @@ function PMA_displayTableNavigation($pos_next, $pos_prev, $sql_query, $id_for_di
echo '<td><div class="navigation_separator">|</div></td>';
}
?>
<td>
<div class="save_edited hide">
<input type="submit" value="<?php echo __('Save edited data'); ?>" />
<div class="navigation_separator">|</div>
</div>
</td>
<td>
<div class="restore_column hide">
<input type="submit" value="<?php echo __('Restore column order'); ?>" />
<div class="navigation_separator">|</div>
</div>
<?php
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 '<input id="col_order" type="hidden" value="' . implode(',', $col_order) . '" />';
}
$col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB);
if ($col_visib) {
echo '<input id="col_visib" type="hidden" value="' . implode(',', $col_visib) . '" />';
}
// generate table create time
if (! PMA_Table::isView($GLOBALS['table'], $GLOBALS['db'])) {
echo '<input id="table_create_time" type="hidden" value="' .
PMA_Table::sGetStatusInfo($GLOBALS['db'], $GLOBALS['table'], 'Create_time') . '" />';
}
}
?>
</td>
<?php // if displaying a VIEW, $unlim_num_rows could be zero because
@ -573,6 +560,29 @@ function PMA_displayTableHeaders(&$is_display, &$fields_meta, $fields_cnt = 0, $
}
// Output data needed for grid editing
echo '<input id="save_cells_at_once" type="hidden" value="' . $GLOBALS['cfg']['SaveCellsAtOnce'] . '" />';
echo '<div class="common_hidden_inputs">';
echo PMA_generate_common_hidden_inputs($db, $table);
echo '</div>';
// 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 '<input id="col_order" type="hidden" value="' . implode(',', $col_order) . '" />';
}
$col_visib = $pmatable->getUiProp(PMA_Table::PROP_COLUMN_VISIB);
if ($col_visib) {
echo '<input id="col_visib" type="hidden" value="' . implode(',', $col_visib) . '" />';
}
// generate table create time
echo '<input id="table_create_time" type="hidden" value="' .
PMA_Table::sGetStatusInfo($GLOBALS['db'], $GLOBALS['table'], 'Create_time') . '" />';
}
$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 '<td align="right"' . ' class="' . $class . ($condition_field ? ' condition' : '') . ' null"><i>NULL</i></td>';
}
@ -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 <cr>/<lf>
$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
. '<input type="checkbox" id="id_rows_to_delete' . $row_no . $id_suffix . '" name="rows_to_delete[' . $where_clause_html . ']"'
. ' class="multi_checkbox"'
. ' value="' . htmlspecialchars($del_query) . '" ' . (isset($GLOBALS['checkall']) ? 'checked="checked"' : '') . ' />'
. '<input type="hidden" class="condition_array" value="' . htmlspecialchars(json_encode($condition_array)) . '" />'
. ' </td>';
}
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 .= '<input type="hidden" class="where_clause" value ="' . $where_clause_html . '" />';
@ -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 .= '<input type="hidden" class="where_clause" value ="' . $where_clause_html . '" />';
@ -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;
}

View File

@ -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 '<span id="no_hint" class="hide"></span>';
}
/**
* Display heading if needed. Design can be set in css file.
*/

View File

@ -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';
}

10
sql.php
View File

@ -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];

View File

@ -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 = '<a href="sql.php' . PMA_generate_common_url($_url_params) . '"' . $title . '>';
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 .= '</a>';
$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);
}

View File

@ -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(<?php echo $_SESSION['PMA_Theme']->getImgPath(); ?>ajax_clock_small.gif) no-repeat center;
height: 10em;
}
.cEdit .edit_area_posting {
background: #FFF url(<?php echo $_SESSION['PMA_Theme']->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;
}

View File

@ -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;
}