diff --git a/.gitignore b/.gitignore index 3e12f7d6f..a773f8e3e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ phpmyadmin.wpj .settings .buildpath .cache +.idea *.sw[op] # Locales locale diff --git a/Documentation.html b/Documentation.html index a78284794..7077d2ad2 100644 --- a/Documentation.html +++ b/Documentation.html @@ -1139,6 +1139,26 @@ CREATE DATABASE,ALTER DATABASE,DROP DATABASE

+
+ $cfg['Servers'][$i]['userconfig'] string +
+
+ Since release 3.4.x phpMyAdmin allows users to set most preferences by themselves + and store them in the database. +

+ + If you don't allow for storing preferences in pmadb, users can + still personalize phpMyAdmin, but settings will be saved in browser's local storage, + or, it is is unavailable, until the end of session. +

+ + To allow the usage of this functionality: + + +
$cfg['Servers'][$i]['designer_coords'] string @@ -1183,7 +1203,7 @@ CREATE DATABASE,ALTER DATABASE,DROP DATABASE $cfg['Servers'][$i]['AllowDeny']['order'] string
If your rule order is empty, then IP - authentication is disabled.

+ authorization is disabled.

If your rule order is set to 'deny,allow' then the system applies all deny rules followed by allow rules. Access is allowed by default. Any @@ -1195,7 +1215,7 @@ CREATE DATABASE,ALTER DATABASE,DROP DATABASE default. Any client which does not match an Allow directive or does match a Deny directive will be denied access to the server.

- If your rule order is set to 'explicit', the authentication is + If your rule order is set to 'explicit', authorization is performed in a similar fashion to rule order 'deny,allow', with the added restriction that your host/username combination must be listed in the allow rules, and not listed in the deny @@ -2153,6 +2173,11 @@ setfacl -d -m "g:www-data:rwx" tmp closed state. A value of 'open' does the reverse. To completely disable all visual sliders, use 'disabled'.
+
$cfg['UserprefsDisallow'] array
+
Contains names of configuration options (keys in $cfg array) + that users can't set through user preferences. For possible values, refer + to libraries/config/user_preferences.forms.php.
+
$cfg['TitleTable'] string
$cfg['TitleDatabase'] string
$cfg['TitleServer'] string
@@ -3483,7 +3508,7 @@ have either the APC extension

3.15 When I import an Excel spreadsheet, some cells with calculations do not display correctly.

- phpMyAdmin uses the PHPExcel library to parse Excel XLS and XLSX spreadsheets. + phpMyAdmin uses the PHPExcel library to parse Excel XLS and XLSX spreadsheets. Therefore, any limitations that are listed on their page regarding Excel calculations will also apply here.

PHPExcel will be kept up to date so as to make all improvements available to phpMyAdmin users. diff --git a/config.sample.inc.php b/config.sample.inc.php index da403fd2b..41f6466a4 100644 --- a/config.sample.inc.php +++ b/config.sample.inc.php @@ -49,6 +49,7 @@ $cfg['Servers'][$i]['AllowNoPassword'] = false; // $cfg['Servers'][$i]['history'] = 'pma_history'; // $cfg['Servers'][$i]['tracking'] = 'pma_tracking'; // $cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords'; +// $cfg['Servers'][$i]['userconfig'] = 'pma_userconfig'; /* Contrib / Swekey authentication */ // $cfg['Servers'][$i]['auth_swekey_config'] = '/etc/swekey-pma.conf'; diff --git a/export.php b/export.php index 1a5d6e645..26154962e 100644 --- a/export.php +++ b/export.php @@ -256,15 +256,18 @@ if ($asfile) { $pma_uri_parts = parse_url($cfg['PmaAbsoluteUri']); if ($export_type == 'server') { if (isset($remember_template)) { - $GLOBALS['PMA_Config']->setCookie('pma_server_filename_template', $filename_template); + $GLOBALS['PMA_Config']->setUserValue('pma_server_filename_template', + 'Export/file_template_server', $filename_template); } } elseif ($export_type == 'database') { if (isset($remember_template)) { - $GLOBALS['PMA_Config']->setCookie('pma_db_filename_template', $filename_template); + $GLOBALS['PMA_Config']->setUserValue('pma_db_filename_template', + 'Export/file_template_database', $filename_template); } } else { if (isset($remember_template)) { - $GLOBALS['PMA_Config']->setCookie('pma_table_filename_template', $filename_template); + $GLOBALS['PMA_Config']->setUserValue('pma_table_filename_template', + 'Export/file_template_table', $filename_template); } } $filename = PMA_expandUserString($filename_template); diff --git a/js/config.js b/js/config.js new file mode 100644 index 000000000..1b0ceddd4 --- /dev/null +++ b/js/config.js @@ -0,0 +1,748 @@ +/** + * Functions used in configuration forms and on user preferences pages + */ + +// default values for fields +var defaultValues = {}; + +// language strings +var PMA_messages = {}; + +/** + * Returns field type + * + * @param {Element} field + */ +function getFieldType(field) { + field = $(field); + var tagName = field.attr('tagName'); + if (tagName == 'INPUT') { + return field.attr('type'); + } else if (tagName == 'SELECT') { + return 'select'; + } else if (tagName == 'TEXTAREA') { + return 'text'; + } + return ''; +} + +/** + * Sets field value + * + * value must be of type: + * o undefined (omitted) - restore default value (form default, not PMA default) + * o String - if field_type is 'text' + * o boolean - if field_type is 'checkbox' + * o Array of values - if field_type is 'select' + * + * @param {Element} field + * @param {String} field_type see {@link #getFieldType} + * @param {String|Boolean} [value] + */ +function setFieldValue(field, field_type, value) { + field = $(field); + switch (field_type) { + case 'text': + field.attr('value', (value != undefined ? value : field.attr('defaultValue'))); + break; + case 'checkbox': + field.attr('checked', (value != undefined ? value : field.attr('defaultChecked'))); + break; + case 'select': + var options = field.attr('options'); + var i, imax = options.length; + if (value == undefined) { + for (i = 0; i < imax; i++) { + options[i].selected = options[i].defaultSelected; + } + } else { + for (i = 0; i < imax; i++) { + options[i].selected = (value.indexOf(options[i].value) != -1); + } + } + break; + } + markField(field); +} + +/** + * Gets field value + * + * Will return one of: + * o String - if type is 'text' + * o boolean - if type is 'checkbox' + * o Array of values - if type is 'select' + * + * @param {Element} field + * @param {String} field_type returned by {@link #getFieldType} + * @type Boolean|String|String[] + */ +function getFieldValue(field, field_type) { + field = $(field); + switch (field_type) { + case 'text': + return field.attr('value'); + case 'checkbox': + return field.attr('checked'); + case 'select': + var options = field.attr('options'); + var i, imax = options.length, items = []; + for (i = 0; i < imax; i++) { + if (options[i].selected) { + items.push(options[i].value); + } + } + return items; + } + return null; +} + +/** + * Returns values for all fields in fieldsets + */ +function getAllValues() { + var elements = $('fieldset input, fieldset select, fieldset textarea'); + var values = {}; + var type, value; + for (var i = 0; i < elements.length; i++) { + type = getFieldType(elements[i]); + value = getFieldValue(elements[i], type); + if (typeof value != 'undefined') { + // we only have single selects, fatten array + if (type == 'select') { + value = value[0]; + } + values[elements[i].name] = value; + } + } + return values; +} + +/** + * Checks whether field has its default value + * + * @param {Element} field + * @param {String} type + * @return boolean + */ +function checkFieldDefault(field, type) { + field = $(field); + var field_id = field.attr('id'); + if (typeof defaultValues[field_id] == 'undefined') { + return true; + } + var isDefault = true; + var currentValue = getFieldValue(field, type); + if (type != 'select') { + isDefault = currentValue == defaultValues[field_id]; + } else { + // compare arrays, will work for our representation of select values + if (currentValue.length != defaultValues[field_id].length) { + isDefault = false; + } + else { + for (var i = 0; i < currentValue.length; i++) { + if (currentValue[i] != defaultValues[field_id][i]) { + isDefault = false; + break; + } + } + } + } + return isDefault; +} + +/** + * Returns element's id prefix + * @param {Element} element + */ +function getIdPrefix(element) { + return $(element).attr('id').replace(/[^-]+$/, ''); +} + +// ------------------------------------------------------------------ +// Form validation and field operations +// + +// form validator assignments +var validate = {}; + +// form validator list +var validators = { + // regexp: numeric value + _regexp_numeric: /^[0-9]+$/, + // regexp: extract parts from PCRE expression + _regexp_pcre_extract: /(.)(.*)\1(.*)?/, + /** + * Validates positive number + * + * @param {boolean} isKeyUp + */ + validate_positive_number: function (isKeyUp) { + if (isKeyUp && this.value == '') { + return true; + } + var result = this.value != '0' && validators._regexp_numeric.test(this.value); + return result ? true : PMA_messages['error_nan_p']; + }, + /** + * Validates non-negative number + * + * @param {boolean} isKeyUp + */ + validate_non_negative_number: function (isKeyUp) { + if (isKeyUp && this.value == '') { + return true; + } + var result = validators._regexp_numeric.test(this.value); + return result ? true : PMA_messages['error_nan_nneg']; + }, + /** + * Validates port number + * + * @param {boolean} isKeyUp + */ + validate_port_number: function(isKeyUp) { + if (this.value == '') { + return true; + } + var result = validators._regexp_numeric.test(this.value) && this.value != '0'; + return result && this.value <= 65535 ? true : PMA_messages['error_incorrect_port']; + }, + /** + * Validates value according to given regular expression + * + * @param {boolean} isKeyUp + * @param {string} regexp + */ + validate_by_regex: function(isKeyUp, regexp) { + if (isKeyUp && this.value == '') { + return true; + } + // convert PCRE regexp + var parts = regexp.match(validators._regexp_pcre_extract); + var valid = this.value.match(new RegExp(parts[2], parts[3])) != null; + return valid ? true : PMA_messages['error_invalid_value']; + }, + /** + * Validates upper bound for numeric inputs + * + * @param {boolean} isKeyUp + * @param {int} max_value + */ + validate_upper_bound: function(isKeyUp, max_value) { + var val = parseInt(this.value); + if (isNaN(val)) { + return true; + } + return val <= max_value ? true : PMA_messages['error_value_lte'].replace('%s', max_value); + }, + // field validators + _field: { + }, + // fieldset validators + _fieldset: { + } +}; + +/** + * Registers validator for given field + * + * @param {String} id field id + * @param {String} type validator (key in validators object) + * @param {boolean} onKeyUp whether fire on key up + * @param {Array} params validation function parameters + */ +function validateField(id, type, onKeyUp, params) { + if (typeof validators[type] == 'undefined') { + return; + } + if (typeof validate[id] == 'undefined') { + validate[id] = []; + } + validate[id].push([type, params, onKeyUp]); +} + +/** + * Returns valdiation functions associated with form field + * + * @param {String} field_id form field id + * @param {boolean} onKeyUpOnly see validateField + * @type Array + * @return array of [function, paramseters to be passed to function] + */ +function getFieldValidators(field_id, onKeyUpOnly) { + // look for field bound validator + var name = field_id.match(/[^-]+$/)[0]; + if (typeof validators._field[name] != 'undefined') { + return [[validators._field[name], null]]; + } + + // look for registered validators + var functions = []; + if (typeof validate[field_id] != 'undefined') { + // validate[field_id]: array of [type, params, onKeyUp] + for (var i = 0, imax = validate[field_id].length; i < imax; i++) { + if (onKeyUpOnly && !validate[field_id][i][2]) { + continue; + } + functions.push([validators[validate[field_id][i][0]], validate[field_id][i][1]]); + } + } + + return functions; +} + +/** + * Displays errors for given form fields + * + * WARNING: created DOM elements must be identical with the ones made by + * display_input() in FormDisplay.tpl.php! + * + * @param {Object} error_list list of errors in the form {field id: error array} + */ +function displayErrors(error_list) { + for (var field_id in error_list) { + var errors = error_list[field_id]; + var field = $('#'+field_id); + var isFieldset = field.attr('tagName') == 'FIELDSET'; + var errorCnt = isFieldset + ? field.find('dl.errors') + : field.siblings('.inline_errors'); + + // remove empty errors (used to clear error list) + errors = $.grep(errors, function(item) { + return item != ''; + }); + + // CSS error class + if (!isFieldset) { + // checkboxes uses parent for marking + var fieldMarker = (field.attr('type') == 'checkbox') ? field.parent() : field; + fieldMarker[errors.length ? 'addClass' : 'removeClass']('field-error'); + } + + if (errors.length) { + // if error container doesn't exist, create it + if (errorCnt.length == 0) { + if (isFieldset) { + errorCnt = $('

'); + field.find('table').before(errorCnt); + } else { + errorCnt = $('
'); + field.closest('td').append(errorCnt); + } + } + + var html = ''; + for (var i = 0, imax = errors.length; i < imax; i++) { + html += '
' + errors[i] + '
'; + } + errorCnt.html(html); + } else if (errorCnt !== null) { + // remove useless error container + errorCnt.remove(); + } + } +} + +/** + * Validates fieldset and puts errors in 'errors' object + * + * @param {Element} fieldset + * @param {boolean} isKeyUp + * @param {Object} errors + */ +function validate_fieldset(fieldset, isKeyUp, errors) { + fieldset = $(fieldset); + if (fieldset.length && typeof validators._fieldset[fieldset.attr('id')] != 'undefined') { + var fieldset_errors = validators._fieldset[fieldset.attr('id')].apply(fieldset[0], [isKeyUp]); + for (var field_id in fieldset_errors) { + if (typeof errors[field_id] == 'undefined') { + errors[field_id] = []; + } + if (typeof fieldset_errors[field_id] == 'string') { + fieldset_errors[field_id] = [fieldset_errors[field_id]]; + } + $.merge(errors[field_id], fieldset_errors[field_id]); + } + } +} + +/** + * Validates form field and puts errors in 'errors' object + * + * @param {Element} field + * @param {boolean} isKeyUp + * @param {Object} errors + */ +function validate_field(field, isKeyUp, errors) { + field = $(field); + var field_id = field.attr('id'); + errors[field_id] = []; + var functions = getFieldValidators(field_id, isKeyUp); + for (var i = 0; i < functions.length; i++) { + var args = functions[i][1] != null + ? functions[i][1].slice(0) + : []; + args.unshift(isKeyUp); + var result = functions[i][0].apply(field[0], args); + if (result !== true) { + if (typeof result == 'string') { + result = [result]; + } + $.merge(errors[field_id], result); + } + } +} + +/** + * Validates form field and parent fieldset + * + * @param {Element} field + * @param {boolean} isKeyUp + */ +function validate_field_and_fieldset(field, isKeyUp) { + field = $(field); + var errors = {}; + validate_field(field, isKeyUp, errors); + validate_fieldset(field.closest('fieldset'), isKeyUp, errors); + displayErrors(errors); +} + +/** + * Marks field depending on its value (system default or custom) + * + * @param {Element} field + */ +function markField(field) { + field = $(field); + var type = getFieldType(field); + var isDefault = checkFieldDefault(field, type); + + // checkboxes uses parent for marking + var fieldMarker = (type == 'checkbox') ? field.parent() : field; + setRestoreDefaultBtn(field, !isDefault); + fieldMarker[isDefault ? 'removeClass' : 'addClass']('custom'); +} + +/** + * Enables or disables the "restore default value" button + * + * @param {Element} field + * @param {boolean} display + */ +function setRestoreDefaultBtn(field, display) { + var el = $(field).closest('td').find('.restore-default img'); + el[display ? 'show' : 'hide'](); +} + +$(function() { + // register validators and mark custom values + var elements = $('input[id], select[id], textarea[id]'); + $('input[id], select[id], textarea[id]').each(function(){ + markField(this); + var el = $(this); + el.bind('change', function() { + validate_field_and_fieldset(this, false); + markField(this); + }); + var tagName = el.attr('tagName'); + // text fields can be validated after each change + if (tagName == 'INPUT' && el.attr('type') == 'text') { + el.keyup(function() { + validate_field_and_fieldset(el, true); + markField(el); + }); + } + // disable textarea spellcheck + if (tagName == 'TEXTAREA') { + el.attr('spellcheck', false); + } + }); + + // check whether we've refreshed a page and browser remembered modified + // form values + var check_page_refresh = $('#check_page_refresh'); + if (check_page_refresh.length == 0 || check_page_refresh.val() == '1') { + // run all field validators + var errors = {}; + for (var i = 0; i < elements.length; i++) { + validate_field(elements[i], false, errors); + } + // run all fieldset validators + $('fieldset').each(function(){ + validate_fieldset(this, false, errors); + }); + + displayErrors(errors); + } else if (check_page_refresh) { + check_page_refresh.val('1'); + } +}); + +// +// END: Form validation and field operations +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// Tabbed forms +// + +/** + * Sets active tab + * + * @param {String} tab_id + */ +function setTab(tab_id) { + $('.tabs a').removeClass('active').filter('[href=' + tab_id + ']').addClass('active'); + $('.tabs_contents fieldset').hide().filter(tab_id).show(); + location.hash = 'tab_' + tab_id.substr(1); + $('.config-form input[name=tab_hash]').val(location.hash); +} + +$(function() { + var tabs = $('.tabs'); + if (!tabs.length) { + return; + } + // add tabs events and activate one tab (the first one or indicated by location hash) + tabs.find('a') + .click(function(e) { + e.preventDefault(); + setTab($(this).attr('href')); + }) + .filter(':first') + .addClass('active'); + $('.tabs_contents fieldset').hide().filter(':first').show(); + + // tab links handling, check each 200ms + // (works with history in FF, further browser support here would be an overkill) + var prev_hash; + var tab_check_fnc = function() { + if (location.hash != prev_hash) { + prev_hash = location.hash; + if (location.hash.match(/^#tab_.+/) && $('#' + location.hash.substr(5)).length) { + setTab('#' + location.hash.substr(5)); + } + } + }; + tab_check_fnc(); + setInterval(tab_check_fnc, 200); +}); + +// +// END: Tabbed forms +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// Form reset buttons +// + +$(function() { + $('input[type=button][name=submit_reset]').click(function() { + var fields = $(this).closest('fieldset').find('input, select, textarea'); + for (var i = 0, imax = fields.length; i < imax; i++) { + setFieldValue(fields[i], getFieldType(fields[i])); + } + }); +}); + +// +// END: Form reset buttons +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// "Restore default" and "set value" buttons +// + +/** + * Restores field's default value + * + * @param {String} field_id + */ +function restoreField(field_id) { + var field = $('#'+field_id); + if (field.length == 0 || defaultValues[field_id] == undefined) { + return; + } + setFieldValue(field, getFieldType(field), defaultValues[field_id]); +} + +$(function() { + $('.tabs_contents') + .delegate('.restore-default, .set-value', 'mouseenter', function(){$(this).css('opacity', 1)}) + .delegate('.restore-default, .set-value', 'mouseleave', function(){$(this).css('opacity', 0.25)}) + .delegate('.restore-default, .set-value', 'click', function(e) { + e.preventDefault(); + var href = $(this).attr('href'); + var field_sel; + if ($(this).hasClass('restore-default')) { + field_sel = href; + restoreField(field_sel.substr(1)); + } else { + field_sel = href.match(/^[^=]+/)[0]; + var value = href.match(/=(.+)$/)[1]; + setFieldValue($(field_sel), 'text', value); + } + $(field_sel).trigger('change'); + }) + .find('.restore-default, .set-value') + // inline-block for IE so opacity inheritance works + .css({display: 'inline-block', opacity: 0.25}); +}); + +// +// END: "Restore default" and "set value" buttons +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// User preferences import/export +// + +$(function() { + offerPrefsAutoimport(); + var radios = $('#import_local_storage, #export_local_storage'); + if (!radios.length) { + return; + } + + // enable JavaScript dependent fields + radios + .attr('disabled', false) + .add('#export_text_file, #import_text_file') + .click(function(){ + var enable_id = $(this).attr('id'); + var disable_id = enable_id.match(/local_storage$/) + ? enable_id.replace(/local_storage$/, 'text_file') + : enable_id.replace(/text_file$/, 'local_storage'); + $('#opts_'+disable_id).addClass('disabled').find('input').attr('disabled', true); + $('#opts_'+enable_id).removeClass('disabled').find('input').attr('disabled', false); + }); + + // detect localStorage state + var ls_supported = window.localStorage || false; + var ls_exists = ls_supported ? (window.localStorage['config'] || false) : false; + $('.localStorage-'+(ls_supported ? 'un' : '')+'supported').hide(); + $('.localStorage-'+(ls_exists ? 'empty' : 'exists')).hide(); + if (ls_exists) { + updatePrefsDate(); + } + $('form.prefs-form').change(function(){ + var form = $(this); + var disabled = false; + if (!ls_supported) { + disabled = form.find('input[type=radio][value$=local_storage]').attr('checked'); + } else if (!ls_exists && form.attr('name') == 'prefs_import' + && $('#import_local_storage')[0].checked) { + disabled = true; + } + form.find('input[type=submit]').attr('disabled', disabled); + }).submit(function(e) { + var form = $(this); + if (form.attr('name') == 'prefs_export' && $('#export_local_storage')[0].checked) { + e.preventDefault(); + // use AJAX to read JSON settings and save them + savePrefsToLocalStorage(form); + } else if (form.attr('name') == 'prefs_import' && $('#import_local_storage')[0].checked) { + // set 'json' input and submit form + form.find('input[name=json]').val(window.localStorage['config']); + } + }); + + $('.click-hide-message').live('click', function(){ + var div = $(this); + div.hide().parent('.group').css('height', ''); + div.next('form').show(); + }); +}); + +/** + * Saves user preferences to localStorage + * + * @param {Element} form + */ +function savePrefsToLocalStorage(form) +{ + form = $(form); + var submit = form.find('input[type=submit]'); + submit.attr('disabled', true); + $.ajax({ + url: 'prefs_manage.php', + cache: false, + type: 'POST', + data: { + token: form.find('input[name=token]').val(), + submit_get_json: true + }, + success: function(response) { + window.localStorage['config'] = response.prefs; + window.localStorage['config_mtime'] = response.mtime; + window.localStorage['config_mtime_local'] = (new Date()).toUTCString(); + updatePrefsDate(); + $('.localStorage-empty').hide(); + $('.localStorage-exists').show(); + var group = form.parent('.group'); + group.css('height', group.height() + 'px'); + form.hide('fast'); + form.prev('.click-hide-message').show('fast'); + }, + complete: function() { + submit.attr('disabled', false); + } + }); +} + +/** + * Updates preferences timestamp in Import form + */ +function updatePrefsDate() +{ + var d = new Date(window.localStorage['config_mtime_local']); + var msg = PMA_messages['strSavedOn'].replace('__DATE__', formatDate(d)); + $('#opts_import_local_storage .localStorage-exists').html(msg); +} + +/** + * Returns date formatted as YYYY-MM-DD HH:II + * + * @param {Date} d + */ +function formatDate(d) +{ + return d.getFullYear() + '-' + + (d.getMonth() < 10 ? '0'+d.getMonth() : d.getMonth()) + + '-' + (d.getDate() < 10 ? '0'+d.getDate() : d.getDate()) + + ' ' + (d.getHours() < 10 ? '0'+d.getHours() : d.getHours()) + + ':' + (d.getMinutes() < 10 ? '0'+d.getMinutes() : d.getMinutes()); +} + +/** + * Prepares message which informs that localStorage preferences are available and can be imported + */ +function offerPrefsAutoimport() +{ + var has_config = (window.localStorage || false) && (window.localStorage['config'] || false); + var cnt = $('#prefs_autoload'); + if (!cnt.length || !has_config) { + return; + } + cnt.find('a').click(function(e) { + e.preventDefault(); + var a = $(this); + if (a.attr('href') == '#no') { + cnt.remove(); + $.post('main.php', { + token: cnt.find('input[name=token]').val(), + prefs_autoload: 'hide'}); + return; + } + cnt.find('input[name=json]').val(window.localStorage['config']); + cnt.find('form').submit(); + }); + cnt.show(); +} + +// +// END: User preferences import/export +// ------------------------------------------------------------------ \ No newline at end of file diff --git a/js/functions.js b/js/functions.js index 2d57f3b48..75ffee59c 100644 --- a/js/functions.js +++ b/js/functions.js @@ -1893,4 +1893,100 @@ $(document).ready(function() { style: { background: '#ffffcc' } }); }); -}); \ No newline at end of file +}); + +function menuResize() +{ + var cnt = $('#topmenu'); + var wmax = cnt.width() - 5; // 5 px margin for jumping menu in Chrome + var submenu = cnt.find('.submenu'); + var submenu_w = submenu.outerWidth(true); + var submenu_ul = submenu.find('ul'); + var li = cnt.find('> li'); + var li2 = submenu_ul.find('li'); + var more_shown = li2.length > 0; + var w = more_shown ? submenu_w : 0; + + // hide menu items + var hide_start = 0; + for (var i = 0; i < li.length-1; i++) { // li.length-1: skip .submenu element + var el = $(li[i]); + var el_width = el.outerWidth(true); + el.data('width', el_width); + w += el_width; + if (w > wmax) { + w -= el_width; + if (w + submenu_w < wmax) { + hide_start = i; + } else { + hide_start = i-1; + w -= $(li[i-1]).data('width'); + } + break; + } + } + + if (hide_start > 0) { + for (var i = hide_start; i < li.length-1; i++) { + $(li[i])[more_shown ? 'prependTo' : 'appendTo'](submenu_ul); + } + submenu.show(); + } else if (more_shown) { + w -= submenu_w; + // nothing hidden, maybe something can be restored + for (var i = 0; i < li2.length; i++) { + //console.log(li2[i], submenu_w); + w += $(li2[i]).data('width'); + // item fits or (it is the last item and it would fit if More got removed) + if (w+submenu_w < wmax || (i == li2.length-1 && w < wmax)) { + $(li2[i]).insertBefore(submenu); + if (i == li2.length-1) { + submenu.hide(); + } + continue; + } + break; + } + } + if (submenu.find('.tabactive').length) { + submenu.addClass('active').find('> a').removeClass('tab').addClass('tabactive'); + } else { + submenu.removeClass('active').find('> a').addClass('tab').removeClass('tabactive'); + } +} + +$(function() { + var topmenu = $('#topmenu'); + if (topmenu.length == 0) { + return; + } + // create submenu container + var link = $('', {href: '#', 'class': 'tab'}) + .text(PMA_messages['strMore']) + .click(function(e) { + e.preventDefault(); + }); + var img = topmenu.find('li:first-child img'); + if (img.length) { + img.clone().attr('src', img.attr('src').replace(/\/[^\/]+$/, '/b_more.png')).prependTo(link); + } + var submenu = $('
  • ', {'class': 'submenu'}) + .append(link) + .append($('
  • '; +} +?> \ No newline at end of file diff --git a/libraries/config/config_functions.lib.php b/libraries/config/config_functions.lib.php new file mode 100644 index 000000000..6f9aabb34 --- /dev/null +++ b/libraries/config/config_functions.lib.php @@ -0,0 +1,100 @@ + '<', + '>' => '>', + '[em]' => '', + '[/em]' => '', + '[strong]' => '', + '[/strong]' => '', + '[code]' => '', + '[/code]' => '', + '[kbd]' => '', + '[/kbd]' => '', + '[br]' => '
    ', + '[sup]' => '', + '[/sup]' => ''); + if (defined('PMA_SETUP')) { + $replace_pairs['[a@Documentation.html'] = '[a@../Documentation.html'; + } + $search = array_keys($replace_pairs); + $replace = array_values($replace_pairs); + } + $message = isset($GLOBALS["strConfig$lang_key"]) ? $GLOBALS["strConfig$lang_key"] : $lang_key; + $message = str_replace($search, $replace, $message); + // replace [a@"$1"]$2[/a] with $2 + $message = preg_replace('#\[a@("?)([^\]]+)\1\]([^\[]+)\[/a\]#e', + "PMA_lang_link_replace('$2', '$3')", $message); + + if (func_num_args() == 1) { + return $message; + } else { + $args = func_get_args(); + array_shift($args); + return vsprintf($message, $args); + } +} + +/** + * Returns translated field name/description or comment + * + * @param string $canonical_path + * @param string $type 'name', 'desc' or 'cmt' + * @param mixed $default + * @return string + */ +function PMA_lang_name($canonical_path, $type = 'name', $default = 'key') +{ + $lang_key = str_replace( + array('Servers/1/', '/'), + array('Servers/', '_'), + $canonical_path) . '_' . $type; + return isset($GLOBALS["strConfig$lang_key"]) + ? ($type == 'desc' ? PMA_lang($lang_key) : $GLOBALS["strConfig$lang_key"]) + : ($default == 'key' ? $lang_key : $default); +} + +/** + * Wraps link in <a> tags and replaces argument separator in internal links + * to the one returned by PMA_get_arg_separator() + * + * @param string $link + * @param string $text + * @return string + */ +function PMA_lang_link_replace($link, $text) +{ + static $separator; + + if (!isset($separator)) { + $separator = PMA_get_arg_separator('html'); + } + + if (!preg_match('#^http://#', $link)) { + $link = str_replace('&', $separator, $link); + } + + return '' . $text . ''; +} +?> \ No newline at end of file diff --git a/libraries/config/messages.inc.php b/libraries/config/messages.inc.php new file mode 100644 index 000000000..7dc5c1483 --- /dev/null +++ b/libraries/config/messages.inc.php @@ -0,0 +1,498 @@ + \ No newline at end of file diff --git a/libraries/config/setup.forms.php b/libraries/config/setup.forms.php new file mode 100644 index 000000000..af8d28415 --- /dev/null +++ b/libraries/config/setup.forms.php @@ -0,0 +1,368 @@ + array(1 => array('host'))); + * can be written as + * $forms['Form group']['Form name'] = array('Servers/1/host'); + * + * You can assign default values set by special button ("set value: ..."), eg.: + * 'Servers/1/pmadb' => 'phpmyadmin' + * + * To group options, use: + * ':group:' . __('group name') // just define a group + * or + * 'option' => ':group' // group starting from this option + * End group blocks with: + * ':group:end' + * + * @package phpMyAdmin-setup + */ + +$forms = array(); +$forms['_config.php'] = array( + 'DefaultLang', + 'ServerDefault'); +$forms['Servers']['Server'] = array('Servers' => array(1 => array( + 'verbose', + 'host', + 'port', + 'socket', + 'ssl', + 'connect_type', + 'extension', + 'compress', + 'nopassword'))); +$forms['Servers']['Server_auth'] = array('Servers' => array(1 => array( + 'auth_type', + ':group:' . __('Config authentication'), + 'user', + 'password', + ':group:end', + ':group:' . __('Cookie authentication'), + 'auth_swekey_config' => './swekey.conf', + ':group:end', + ':group:' . __('HTTP authentication'), + 'auth_http_realm', + ':group:end', + ':group:' . __('Signon authentication'), + 'SignonSession', + 'SignonURL', + 'LogoutURL'))); +$forms['Servers']['Server_config'] = array('Servers' => array(1 => array( + 'only_db', + 'hide_db', + 'AllowRoot', + 'AllowNoPassword', + 'DisableIS', + 'AllowDeny/order', + 'AllowDeny/rules', + 'ShowDatabasesCommand', + 'CountTables'))); +$forms['Servers']['Server_pmadb'] = array('Servers' => array(1 => array( + 'pmadb' => 'phpmyadmin', + 'controluser', + 'controlpass', + 'verbose_check', + 'bookmarktable' => 'pma_bookmark', + 'relation' => 'pma_relation', + 'userconfig' => 'pma_userconfig', + 'table_info' => 'pma_table_info', + 'column_info' => 'pma_column_info', + 'history' => 'pma_history', + 'tracking' => 'pma_tracking', + 'table_coords' => 'pma_table_coords', + 'pdf_pages' => 'pma_pdf_pages', + 'designer_coords' => 'pma_designer_coords'))); +$forms['Servers']['Server_tracking'] = array('Servers' => array(1 => array( + 'tracking_version_auto_create', + 'tracking_default_statements', + 'tracking_add_drop_view', + 'tracking_add_drop_table', + 'tracking_add_drop_database', +))); +$forms['Features']['Import_export'] = array( + 'UploadDir', + 'SaveDir', + 'RecodingEngine' => ':group', + 'IconvExtraParams', + ':group:end', + 'ZipDump', + 'GZipDump', + 'BZipDump', + 'CompressOnFly'); +$forms['Features']['Security'] = array( + 'blowfish_secret', + 'ForceSSL', + 'CheckConfigurationPermissions', + 'TrustedProxies', + 'AllowUserDropDatabase', + 'AllowArbitraryServer', + 'LoginCookieRecall', + 'LoginCookieValidity', + 'LoginCookieStore', + 'LoginCookieDeleteAll'); +$forms['Features']['Page_titles'] = array( + 'TitleDefault', + 'TitleTable', + 'TitleDatabase', + 'TitleServer'); +$forms['Features']['Warnings'] = array( + 'PmaNoRelation_DisableWarning', + 'SuhosinDisableWarning', + 'McryptDisableWarning'); +$forms['Features']['Developer'] = array( + 'UserprefsDeveloperTab', + 'Error_Handler/display', + 'Error_Handler/gather', + 'DBG/sql', + 'DBG/php'); +$forms['Features']['Other_core_settings'] = array( + 'NaturalOrder', + 'InitialSlidersState', + 'ErrorIconic', + 'ReplaceHelpImg', + 'MaxDbList', + 'MaxTableList', + 'OBGzip', + 'PersistentConnections', + 'ExecTimeLimit', + 'MemoryLimit', + 'SkipLockedTables', + 'UseDbSearch', + 'AllowThirdPartyFraming'); +$forms['Sql_queries']['Sql_queries'] = array( + 'ShowSQL', + 'Confirm', + 'QueryHistoryDB', + 'QueryHistoryMax', + 'IgnoreMultiSubmitErrors', + 'VerboseMultiSubmit', + 'MaxCharactersInDisplayedSQL', + 'EditInWindow', + //'QueryWindowWidth', // overridden in theme + //'QueryWindowHeight', + 'QueryWindowDefTab'); +$forms['Sql_queries']['Sql_box'] = array('SQLQuery' => array( + 'Edit', + 'Explain', + 'ShowAsPHP', + 'Validate', + 'Refresh')); +$forms['Sql_queries']['Sql_validator'] = array('SQLValidator' => array( + 'use', + 'username', + 'password')); +$forms['Left_frame']['Left_frame'] = array( + 'LeftFrameLight', + 'LeftDisplayLogo', + 'LeftLogoLink', + 'LeftLogoLinkWindow', + 'LeftPointerEnable'); +$forms['Left_frame']['Left_servers'] = array( + 'LeftDisplayServers', + 'DisplayServersList'); +$forms['Left_frame']['Left_databases'] = array( + 'DisplayDatabasesList', + 'LeftFrameDBTree', + 'LeftFrameDBSeparator', + 'ShowTooltipAliasDB'); +$forms['Left_frame']['Left_tables'] = array( + 'LeftDefaultTabTable', + 'LeftFrameTableSeparator', + 'LeftFrameTableLevel', + 'ShowTooltip', + 'ShowTooltipAliasTB'); +$forms['Main_frame']['Startup'] = array( + 'MainPageIconic', + 'ShowCreateDb' => ':group', + 'SuggestDBName', + ':group:end', + 'ShowStats', + 'ShowServerInfo', + 'ShowPhpInfo', + 'ShowChgPassword'); +$forms['Main_frame']['Browse'] = array( + 'NavigationBarIconic', + 'ShowAll', + 'MaxRows', + 'Order', + 'BrowsePointerEnable', + 'BrowseMarkerEnable', + 'RepeatCells', + 'LimitChars', + 'ModifyDeleteAtLeft', + 'ModifyDeleteAtRight', + 'DefaultDisplay'); +$forms['Main_frame']['Edit'] = array( + 'ProtectBinary', + 'ShowFunctionFields', + 'ShowFieldTypesInDataEditView', + 'CharEditing', + 'CharTextareaCols', + 'CharTextareaRows', + 'TextareaCols', + 'TextareaRows', + 'LongtextDoubleTextarea', + 'InsertRows', + 'ForeignKeyDropdownOrder', + 'ForeignKeyMaxLimit', + 'DefaultPropDisplay'); +$forms['Main_frame']['Tabs'] = array( + 'LightTabs', + 'PropertiesIconic', + 'DefaultTabServer', + 'DefaultTabDatabase', + 'DefaultTabTable', + 'QueryWindowDefTab'); +$forms['Import']['Import_defaults'] = array('Import' => array( + 'format', + 'charset', + 'allow_interrupt', + 'skip_queries')); +$forms['Import']['Sql'] = array('Import' => array( + 'sql_compatibility', + 'sql_no_auto_value_on_zero')); +$forms['Import']['Csv'] = array('Import' => array( + ':group:' . __('CSV'), + 'csv_replace', + 'csv_ignore', + 'csv_terminated', + 'csv_enclosed', + 'csv_escaped', + 'csv_col_names', + ':group:end', + ':group:' . __('CSV using LOAD DATA'), + 'ldi_replace', + 'ldi_ignore', + 'ldi_terminated', + 'ldi_enclosed', + 'ldi_escaped', + 'ldi_local_option', + ':group:end')); +$forms['Import']['Microsoft_Office'] = array('Import' => array( + ':group:' . __('Excel 97-2003 XLS Workbook'), + 'xls_col_names', + ':group:end', + ':group:' . __('Excel 2007 XLSX Workbook'), + 'xlsx_col_names')); +$forms['Import']['Open_Document'] = array('Import' => array( + ':group:' . __('Open Document Spreadsheet'), + 'ods_col_names', + 'ods_empty_rows', + 'ods_recognize_percentages', + 'ods_recognize_currency')); +$forms['Export']['Export_defaults'] = array('Export' => array( + 'method', + 'format', + 'compression', + 'charset', + 'asfile' => ':group', + 'onserver', + 'onserver_overwrite', + ':group:end', + 'remember_file_template', + 'file_template_table', + 'file_template_database', + 'file_template_server')); +$forms['Export']['Sql'] = array('Export' => array( + 'sql_include_comments' => ':group', + 'sql_dates', + 'sql_relation', + 'sql_mime', + ':group:end', + 'sql_use_transaction', + 'sql_disable_fk', + 'sql_compatibility', + ':group:' . __('Database export options'), + 'sql_drop_database', + 'sql_structure_or_data', + ':group:end', + ':group:' . __('Structure'), + 'sql_drop_table', + 'sql_procedure_function', + 'sql_create_table_statements' => ':group', + 'sql_if_not_exists', + 'sql_auto_increment', + ':group:end', + 'sql_backquotes', + ':group:end', + ':group:' . __('Data'), + 'sql_delayed', + 'sql_ignore', + 'sql_type', + 'sql_insert_syntax', + 'sql_max_query_size', + 'sql_hex_for_blob', + 'sql_utc_time')); +$forms['Export']['CodeGen'] = array('Export' => array( + 'codegen_format')); +$forms['Export']['Csv'] = array('Export' => array( + ':group:' . __('CSV'), + 'csv_separator', + 'csv_enclosed', + 'csv_escaped', + 'csv_terminated', + 'csv_null', + 'csv_removeCRLF', + 'csv_columns', + ':group:end', + ':group:' . __('CSV for MS Excel'), + 'excel_null', + 'excel_removeCRLF', + 'excel_columns', + 'excel_edition')); +$forms['Export']['Latex'] = array('Export' => array( + 'latex_caption', + 'latex_structure_or_data', + ':group:' . __('Structure'), + 'latex_structure_caption', + 'latex_structure_continued_caption', + 'latex_structure_label', + 'latex_relation', + 'latex_comments', + 'latex_mime', + ':group:end', + ':group:' . __('Data'), + 'latex_columns', + 'latex_data_caption', + 'latex_data_continued_caption', + 'latex_data_label', + 'latex_null')); +$forms['Export']['Microsoft_Office'] = array('Export' => array( + ':group:' . __('Excel 97-2003 XLS Workbook'), + 'xls_null', + 'xls_columns', + ':group:end', + ':group:' . __('Excel 2007 XLSX Workbook'), + 'xlsx_null', + 'xlsx_columns', + ':group:end', + ':group:' . __('Microsoft Word 2000'), + 'htmlword_structure_or_data', + 'htmlword_null', + 'htmlword_columns')); +$forms['Export']['Open_Document'] = array('Export' => array( + ':group:' . __('Open Document Spreadsheet'), + 'ods_columns', + 'ods_null', + ':group:end', + ':group:' . __('Open Document Text'), + 'odt_structure_or_data', + ':group:' . __('Structure'), + 'odt_relation', + 'odt_comments', + 'odt_mime', + ':group:end', + ':group:' . __('Data'), + 'odt_columns', + 'odt_null')); +$forms['Export']['Texy'] = array('Export' => array( + 'texytext_structure_or_data', + ':group:' . __('Data'), + 'texytext_null', + 'texytext_columns')); +?> \ No newline at end of file diff --git a/libraries/config/user_preferences.forms.php b/libraries/config/user_preferences.forms.php new file mode 100644 index 000000000..564f09338 --- /dev/null +++ b/libraries/config/user_preferences.forms.php @@ -0,0 +1,268 @@ + 'phpmyadmin' + * + * To group options, use: + * ':group:' . __('group name') // just define a group + * or + * 'option' => ':group' // group starting from this option + * End group blocks with: + * ':group:end' + * + * @package phpMyAdmin + */ + +$forms = array(); +$forms['Features']['General'] = array( + 'NaturalOrder', + 'InitialSlidersState', + 'ErrorIconic', + 'ReplaceHelpImg', + 'Servers/1/only_db', // saves to Server/only_db + 'Servers/1/hide_db', // saves to Server/hide_db + 'SkipLockedTables', + 'MaxDbList', + 'MaxTableList'); +$forms['Features']['Text_fields'] = array( + 'CharEditing', + 'CharTextareaCols', + 'CharTextareaRows', + 'TextareaCols', + 'TextareaRows', + 'LongtextDoubleTextarea'); +$forms['Features']['Page_titles'] = array( + 'TitleDefault', + 'TitleTable', + 'TitleDatabase', + 'TitleServer'); +$forms['Features']['Warnings'] = array( + 'PmaNoRelation_DisableWarning', + 'SuhosinDisableWarning', + 'McryptDisableWarning'); +// settings from this form are treated specially, see prefs_forms.php and user_preferences.lib.php +$forms['Features']['Developer'] = array( + 'Error_Handler/display', + 'Error_Handler/gather', + 'DBG/sql', + 'DBG/php'); +$forms['Sql_queries']['Sql_queries'] = array( + 'ShowSQL', + 'Confirm', + 'QueryHistoryMax', + 'IgnoreMultiSubmitErrors', + 'VerboseMultiSubmit', + 'MaxCharactersInDisplayedSQL', + 'EditInWindow', + //'QueryWindowWidth', // overridden in theme + //'QueryWindowHeight', + 'QueryWindowDefTab'); +$forms['Sql_queries']['Sql_box'] = array( + 'SQLQuery/Edit', + 'SQLQuery/Explain', + 'SQLQuery/ShowAsPHP', + 'SQLQuery/Validate', + 'SQLQuery/Refresh'); +$forms['Left_frame']['Left_frame'] = array( + 'LeftFrameLight', + 'LeftDisplayLogo', + 'LeftLogoLink', + 'LeftLogoLinkWindow', + 'LeftPointerEnable'); +$forms['Left_frame']['Left_databases'] = array( + 'DisplayDatabasesList', + 'LeftFrameDBTree', + 'LeftFrameDBSeparator', + 'ShowTooltipAliasDB'); +$forms['Left_frame']['Left_tables'] = array( + 'LeftDefaultTabTable', + 'LeftFrameTableSeparator', + 'LeftFrameTableLevel', + 'ShowTooltip', + 'ShowTooltipAliasTB'); +$forms['Main_frame']['Startup'] = array( + 'MainPageIconic', + 'ShowCreateDb' => ':group', + 'SuggestDBName', + ':group:end', + 'ShowStats', + 'ShowServerInfo'); +$forms['Main_frame']['Browse'] = array( + 'NavigationBarIconic', + 'ShowAll', + 'MaxRows', + 'Order', + 'DisplayBinaryAsHex', + 'BrowsePointerEnable', + 'BrowseMarkerEnable', + 'RepeatCells', + 'LimitChars', + 'ModifyDeleteAtLeft', + 'ModifyDeleteAtRight', + 'DefaultDisplay'); +$forms['Main_frame']['Edit'] = array( + 'ProtectBinary', + 'ShowFunctionFields', + 'ShowFieldTypesInDataEditView', + 'InsertRows', + 'ForeignKeyDropdownOrder', + 'ForeignKeyMaxLimit', + 'CtrlArrowsMoving', + 'DefaultPropDisplay'); +$forms['Main_frame']['Tabs'] = array( + 'LightTabs', + 'PropertiesIconic', + 'DefaultTabServer', + 'DefaultTabDatabase', + 'DefaultTabTable'); +$forms['Import']['Import_defaults'] = array( + 'Import/format', + 'Import/charset', + 'Import/allow_interrupt', + 'Import/skip_queries'); +$forms['Import']['Sql'] = array( + 'Import/sql_compatibility', + 'Import/sql_no_auto_value_on_zero'); +$forms['Import']['Csv'] = array( + ':group:' . __('CSV'), + 'Import/csv_replace', + 'Import/csv_ignore', + 'Import/csv_terminated', + 'Import/csv_enclosed', + 'Import/csv_escaped', + 'Import/csv_col_names', + ':group:end', + ':group:' . __('CSV using LOAD DATA'), + 'Import/ldi_replace', + 'Import/ldi_ignore', + 'Import/ldi_terminated', + 'Import/ldi_enclosed', + 'Import/ldi_escaped', + 'Import/ldi_local_option'); +$forms['Import']['Microsoft_Office'] = array( + ':group:' . __('Excel 97-2003 XLS Workbook'), + 'Import/xls_col_names', + ':group:end', + ':group:' . __('Excel 2007 XLSX Workbook'), + 'Import/xlsx_col_names'); +$forms['Import']['Open_Document'] = array( + ':group:' . __('Open Document Spreadsheet'), + 'Import/ods_col_names', + 'Import/ods_empty_rows', + 'Import/ods_recognize_percentages', + 'Import/ods_recognize_currency'); +$forms['Export']['Export_defaults'] = array( + 'Export/method', + 'Export/format', + 'Export/compression', + 'Export/charset', + 'Export/asfile', + 'Export/remember_file_template', + 'Export/file_template_table', + 'Export/file_template_database', + 'Export/file_template_server'); +$forms['Export']['Sql'] = array( + 'Export/sql_include_comments' => ':group', + 'Export/sql_dates', + 'Export/sql_relation', + 'Export/sql_mime', + ':group:end', + 'Export/sql_use_transaction', + 'Export/sql_disable_fk', + 'Export/sql_compatibility', + ':group:' . __('Database export options'), + 'Export/sql_drop_database', + 'Export/sql_structure_or_data', + ':group:end', + ':group:' . __('Structure'), + 'Export/sql_drop_table', + 'Export/sql_procedure_function', + 'Export/sql_create_table_statements' => ':group', + 'Export/sql_if_not_exists', + 'Export/sql_auto_increment', + ':group:end', + 'Export/sql_backquotes', + ':group:end', + ':group:' . __('Data'), + 'Export/sql_delayed', + 'Export/sql_ignore', + 'Export/sql_type', + 'Export/sql_insert_syntax', + 'Export/sql_max_query_size', + 'Export/sql_hex_for_blob', + 'Export/sql_utc_time'); +$forms['Export']['CodeGen'] = array( + 'Export/codegen_format'); +$forms['Export']['Csv'] = array( + ':group:' . __('CSV'), + 'Export/csv_separator', + 'Export/csv_enclosed', + 'Export/csv_escaped', + 'Export/csv_terminated', + 'Export/csv_null', + 'Export/csv_removeCRLF', + 'Export/csv_columns', + ':group:end', + ':group:' . __('CSV for MS Excel'), + 'Export/excel_null', + 'Export/excel_removeCRLF', + 'Export/excel_columns', + 'Export/excel_edition'); +$forms['Export']['Latex'] = array( + 'Export/latex_caption', + 'Export/latex_structure_or_data', + ':group:' . __('Structure'), + 'Export/latex_structure_caption', + 'Export/latex_structure_continued_caption', + 'Export/latex_structure_label', + 'Export/latex_relation', + 'Export/latex_comments', + 'Export/latex_mime', + ':group:end', + ':group:' . __('Data'), + 'Export/latex_columns', + 'Export/latex_data_caption', + 'Export/latex_data_continued_caption', + 'Export/latex_data_label', + 'Export/latex_null'); +$forms['Export']['Microsoft_Office'] = array( + ':group:' . __('Excel 97-2003 XLS Workbook'), + 'Export/xls_null', + 'Export/xls_columns', + ':group:end', + ':group:' . __('Excel 2007 XLSX Workbook'), + 'Export/xlsx_null', + 'Export/xlsx_columns', + ':group:end', + ':group:' . __('Microsoft Word 2000'), + 'Export/htmlword_structure_or_data', + 'Export/htmlword_null', + 'Export/htmlword_columns'); +$forms['Export']['Open_Document'] = array( + ':group:' . __('Open Document Spreadsheet'), + 'Export/ods_columns', + 'Export/ods_null', + ':group:end', + ':group:' . __('Open Document Text'), + 'Export/odt_structure_or_data', + ':group:' . __('Structure'), + 'Export/odt_relation', + 'Export/odt_comments', + 'Export/odt_mime', + ':group:end', + ':group:' . __('Data'), + 'Export/odt_columns', + 'Export/odt_null'); +$forms['Export']['Texy'] = array( + 'Export/texytext_structure_or_data', + ':group:' . __('Data'), + 'Export/texytext_null', + 'Export/texytext_columns'); +?> \ No newline at end of file diff --git a/setup/lib/validate.lib.php b/libraries/config/validate.lib.php similarity index 62% rename from setup/lib/validate.lib.php rename to libraries/config/validate.lib.php index c69b9f42c..7c35fffa7 100644 --- a/setup/lib/validate.lib.php +++ b/libraries/config/validate.lib.php @@ -1,4 +1,5 @@ getDbEntry('_validators', array()); + if (!defined('PMA_SETUP') || !PMA_SETUP) { + $uvs = $cf->getDbEntry('_userValidators', array()); + foreach ($uvs as $field => $uv_list) { + $uv_list = (array)$uv_list; + foreach ($uv_list as &$uv) { + if (!is_array($uv)) { + continue; + } + for ($i = 1; $i < count($uv); $i++) { + if (substr($uv[$i], 0, 6) == 'value:') { + $uv[$i] = PMA_array_read(substr($uv[$i], 6), $GLOBALS['cfg']); + } + } + } + $validators[$field] = isset($validators[$field]) + ? array_merge((array)$validators[$field], $uv_list) + : $uv_list; + } + } + } + return $validators; +} + /** * Runs validation $validator_id on values $values and returns error list. * @@ -25,18 +61,21 @@ * cleanup in HTML documen * o false - when no validators match name(s) given by $validator_id * + * @uses ConfigFile::getCanonicalPath() + * @uses ConfigFile::getInstance() + * @uses PMA_config_get_validators() * @param string|array $validator_id * @param array $values * @param bool $isPostSource tells whether $values are directly from POST request * @return bool|array */ -function validate($validator_id, &$values, $isPostSource) +function PMA_config_validate($validator_id, &$values, $isPostSource) { // find validators - $cf = ConfigFile::getInstance(); $validator_id = (array) $validator_id; - $validators = $cf->getDbEntry('_validators'); + $validators = PMA_config_get_validators(); $vids = array(); + $cf = ConfigFile::getInstance(); foreach ($validator_id as &$vid) { $vid = $cf->getCanonicalPath($vid); if (isset($validators[$vid])) { @@ -60,18 +99,25 @@ function validate($validator_id, &$values, $isPostSource) // validate $result = array(); foreach ($vids as $vid) { - $r = call_user_func($validators[$vid], $vid, $arguments); - // merge results - if (is_array($r)) { - foreach ($r as $key => $error_list) { - // skip empty values if $isPostSource is false - if (!$isPostSource && empty($error_list)) { - continue; + // call appropriate validation functions + foreach ((array)$validators[$vid] as $validator) { + $vdef = (array) $validator; + $vname = array_shift($vdef); + $args = array_merge(array($vid, &$arguments), $vdef); + $r = call_user_func_array($vname, $args); + + // merge results + if (is_array($r)) { + foreach ($r as $key => $error_list) { + // skip empty values if $isPostSource is false + if (!$isPostSource && empty($error_list)) { + continue; + } + if (!isset($result[$key])) { + $result[$key] = array(); + } + $result[$key] = array_merge($result[$key], (array)$error_list); } - if (!isset($result[$key])) { - $result[$key] = array(); - } - $result[$key] = array_merge($result[$key], (array)$error_list); } } } @@ -85,6 +131,16 @@ function validate($validator_id, &$values, $isPostSource) return empty($new_result) ? true : $new_result; } +/** + * Empty error handler, used to temporarily restore PHP internal error handler + * + * @return bool + */ +function PMA_null_error_handler() +{ + return false; +} + /** * Ensures that $php_errormsg variable will be registered in case of an error * and enables output buffering (when $start = true). @@ -95,17 +151,25 @@ function validate($validator_id, &$values, $isPostSource) */ function test_php_errormsg($start = true) { - static $old_html_errors, $old_track_errors; + static $old_html_errors, $old_track_errors, $old_error_reporting; + static $old_display_errors; if ($start) { $old_html_errors = ini_get('html_errors'); $old_track_errors = ini_get('track_errors'); + $old_display_errors = ini_get('display_errors'); + $old_error_reporting = error_reporting(E_ALL); ini_set('html_errors', false); ini_set('track_errors', true); + ini_set('display_errors', true); + set_error_handler("PMA_null_error_handler"); ob_start(); } else { ob_end_clean(); + restore_error_handler(); + error_reporting($old_error_reporting); ini_set('html_errors', $old_html_errors); ini_set('track_errors', $old_track_errors); + ini_set('display_errors', $old_display_errors); } } @@ -131,14 +195,14 @@ function test_db_connection($extension, $connect_type, $host, $port, $socket, $u if ($extension == 'mysql') { $conn = @mysql_connect($host . $socket . $port, $user, $pass); if (!$conn) { - $error = PMA_lang('error_connection'); + $error = __('Could not connect to MySQL server'); } else { mysql_close($conn); } } else { $conn = @mysqli_connect($host, $user, $pass, null, $port, $socket); if (!$conn) { - $error = PMA_lang('error_connection'); + $error = __('Could not connect to MySQL server'); } else { mysqli_close($conn); } @@ -153,6 +217,7 @@ function test_db_connection($extension, $connect_type, $host, $port, $socket, $u /** * Validate server config * + * @uses test_db_connection() * @param string $path * @param array $values * @return array @@ -162,15 +227,15 @@ function validate_server($path, $values) $result = array('Server' => '', 'Servers/1/user' => '', 'Servers/1/SignonSession' => '', 'Servers/1/SignonURL' => ''); $error = false; if ($values['Servers/1/auth_type'] == 'config' && empty($values['Servers/1/user'])) { - $result['Servers/1/user'] = PMA_lang('error_empty_user_for_config_auth'); + $result['Servers/1/user'] = __('Empty username while using config authentication method'); $error = true; } if ($values['Servers/1/auth_type'] == 'signon' && empty($values['Servers/1/SignonSession'])) { - $result['Servers/1/SignonSession'] = PMA_lang('error_empty_signon_session'); + $result['Servers/1/SignonSession'] = __('Empty signon session name while using signon authentication method'); $error = true; } if ($values['Servers/1/auth_type'] == 'signon' && empty($values['Servers/1/SignonURL'])) { - $result['Servers/1/SignonURL'] = PMA_lang('error_empty_signon_url'); + $result['Servers/1/SignonURL'] = __('Empty signon URL while using signon authentication method'); $error = true; } @@ -187,13 +252,14 @@ function validate_server($path, $values) /** * Validate pmadb config * + * @uses test_db_connection() * @param string $path * @param array $values * @return array */ function validate_pmadb($path, $values) { - $tables = array('Servers/1/bookmarktable', 'Servers/1/relation', 'Servers/1/table_info', 'Servers/1/table_coords', 'Servers/1/pdf_pages', 'Servers/1/column_info', 'Servers/1/history', 'Servers/1/designer_coords'); + //$tables = array('Servers/1/bookmarktable', 'Servers/1/relation', 'Servers/1/table_info', 'Servers/1/table_coords', 'Servers/1/pdf_pages', 'Servers/1/column_info', 'Servers/1/history', 'Servers/1/designer_coords'); $result = array('Server_pmadb' => '', 'Servers/1/controluser' => '', 'Servers/1/controlpass' => ''); $error = false; @@ -203,15 +269,17 @@ function validate_pmadb($path, $values) $result = array(); if ($values['Servers/1/controluser'] == '') { - $result['Servers/1/controluser'] = PMA_lang('error_empty_pmadb_user'); + $result['Servers/1/controluser'] = __('Empty phpMyAdmin control user while using pmadb'); $error = true; } if ($values['Servers/1/controlpass'] == '') { - $result['Servers/1/controlpass'] = PMA_lang('error_empty_pmadb_password'); + $result['Servers/1/controlpass'] = __('Empty phpMyAdmin control user password while using pmadb'); $error = true; } if (!$error) { - $test = test_db_connection($values['Servers/1/extension'], $values['Servers/1/connect_type'], $values['Servers/1/host'], $values['Servers/1/port'], $values['Servers/1/socket'], $values['Servers/1/controluser'], $values['Servers/1/controlpass'], 'Server_pmadb'); + $test = test_db_connection($values['Servers/1/extension'], $values['Servers/1/connect_type'], + $values['Servers/1/host'], $values['Servers/1/port'], $values['Servers/1/socket'], + $values['Servers/1/controluser'], $values['Servers/1/controlpass'], 'Server_pmadb'); if ($test !== true) { $result = array_merge($result, $test); } @@ -223,6 +291,7 @@ function validate_pmadb($path, $values) /** * Validates regular expression * + * @uses test_php_errormsg() * @param string $path * @param array $values * @return array @@ -239,7 +308,6 @@ function validate_regex($path, $values) $matches = array(); preg_match($values[$path], '', $matches); - ob_end_clean(); test_php_errormsg(false); @@ -283,14 +351,14 @@ function validate_trusted_proxies($path, $values) $matches = array(); // we catch anything that may (or may not) be an IP if (!preg_match("/^(.+):(?:[ ]?)\\w+$/", $line, $matches)) { - $result[$path][] = PMA_lang('error_incorrect_value') . ': ' . $line; + $result[$path][] = __('Incorrect value') . ': ' . $line; continue; } // now let's check whether we really have an IP address if (filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { $ip = htmlspecialchars(trim($matches[1])); - $result[$path][] = PMA_lang('error_incorrect_ip_address', $ip); + $result[$path][] = sprintf(__('Incorrect IP address: %s'), $ip); continue; } } @@ -298,7 +366,6 @@ function validate_trusted_proxies($path, $values) return $result; } - /** * Tests integer value * @@ -307,17 +374,17 @@ function validate_trusted_proxies($path, $values) * @param bool $allow_neg allow negative values * @param bool $allow_zero allow zero * @param int $max_value max allowed value - * @param string $error_lang_key error message key: $GLOBALS["strSetup$error_lang_key"] + * @param string $error_string error message key: $GLOBALS["strConfig$error_lang_key"] * @return string empty string if test is successful */ -function test_number($path, $values, $allow_neg, $allow_zero, $max_value, $error_lang_key) +function test_number($path, $values, $allow_neg, $allow_zero, $max_value, $error_string) { if ($values[$path] === '') { return ''; } if (intval($values[$path]) != $values[$path] || (!$allow_neg && $values[$path] < 0) || (!$allow_zero && $values[$path] == 0) || $values[$path] > $max_value) { - return PMA_lang($error_lang_key); + return $error_string; } return ''; @@ -326,36 +393,68 @@ function test_number($path, $values, $allow_neg, $allow_zero, $max_value, $error /** * Validates port number * + * @uses test_number() * @param string $path * @param array $values * @return array */ function validate_port_number($path, $values) { - return array($path => test_number($path, $values, false, false, 65536, 'error_incorrect_port')); + return array($path => test_number($path, $values, false, false, 65535, __('Not a valid port number'))); } /** * Validates positive number * + * @uses test_number() * @param string $path * @param array $values * @return array */ function validate_positive_number($path, $values) { - return array($path => test_number($path, $values, false, false, PHP_INT_MAX, 'error_nan_p')); + return array($path => test_number($path, $values, false, false, PHP_INT_MAX, __('Not a positive number'))); } /** * Validates non-negative number * + * @uses test_number() * @param string $path * @param array $values * @return array */ function validate_non_negative_number($path, $values) { - return array($path => test_number($path, $values, false, true, PHP_INT_MAX, 'error_nan_nneg')); + return array($path => test_number($path, $values, false, true, PHP_INT_MAX, __('Not a non-negative number'))); } -?> + +/** + * Validates value according to given regular expression + * Pattern and modifiers must be a valid for PCRE and JavaScript RegExp + * + * @param string $path + * @param array $values + * @param string $regex + * @return void + */ +function validate_by_regex($path, $values, $regex) +{ + $result = preg_match($regex, $values[$path]); + return array($path => ($result ? '' : __('Incorrect value'))); +} + +/** + * Validates upper bound for numeric inputs + * + * @param string $path + * @param array $values + * @param int $max_value + * @return array + */ +function validate_upper_bound($path, $values, $max_value) +{ + $result = $values[$path] <= $max_value; + return array($path => ($result ? '' : sprintf(__('Value must be equal or lower than %s'), $max_value))); +} +?> \ No newline at end of file diff --git a/libraries/core.lib.php b/libraries/core.lib.php index 8bfc035c7..97d443af1 100644 --- a/libraries/core.lib.php +++ b/libraries/core.lib.php @@ -574,4 +574,89 @@ function PMA_sendHeaderLocation($uri) } } } + +/** + * Returns value of an element in $array given by $path. + * $path is a string describing position of an element in an associative array, + * eg. Servers/1/host refers to $array[Servers][1][host] + * + * @param string $path + * @param array $array + * @param mixed $default + * @return mixed array element or $default + */ +function PMA_array_read($path, $array, $default = null) +{ + $keys = explode('/', $path); + $value =& $array; + foreach ($keys as $key) { + if (!isset($value[$key])) { + return $default; + } + $value =& $value[$key]; + } + return $value; +} + +/** + * Stores value in an array + * + * @param string $path + * @param array &$array + * @param mixed $value + */ +function PMA_array_write($path, &$array, $value) +{ + $keys = explode('/', $path); + $last_key = array_pop($keys); + $a =& $array; + foreach ($keys as $key) { + if (!isset($a[$key])) { + $a[$key] = array(); + } + $a =& $a[$key]; + } + $a[$last_key] = $value; +} + +/** + * Removes value from an array + * + * @param string $path + * @param array &$array + * @param mixed $value + */ +function PMA_array_remove($path, &$array) +{ + $keys = explode('/', $path); + $keys_last = array_pop($keys); + $path = array(); + $depth = 0; + + $path[0] =& $array; + $found = true; + // go as deep as required or possible + foreach ($keys as $key) { + if (!isset($path[$depth][$key])) { + $found = false; + break; + } + $depth++; + $path[$depth] =& $path[$depth-1][$key]; + } + // if element found, remove it + if ($found) { + unset($path[$depth][$keys_last]); + $depth--; + } + + // remove empty nested arrays + for (; $depth >= 0; $depth--) { + if (!isset($path[$depth+1]) || count($path[$depth+1]) == 0) { + unset($path[$depth][$keys[$depth]]); + } else { + break; + } + } +} ?> diff --git a/libraries/db_links.inc.php b/libraries/db_links.inc.php index 0dfa5a8ec..7eaf343a4 100644 --- a/libraries/db_links.inc.php +++ b/libraries/db_links.inc.php @@ -102,21 +102,23 @@ $tabs = array(); $tabs[] =& $tab_structure; $tabs[] =& $tab_sql; $tabs[] =& $tab_search; -if (PMA_Tracker::isActive()) { - $tabs[] =& $tab_tracking; -} $tabs[] =& $tab_qbe; $tabs[] =& $tab_export; if (! $db_is_information_schema) { $tabs[] =& $tab_import; - if ($cfgRelation['designerwork']) { - $tabs[] =& $tab_designer; - } $tabs[] =& $tab_operation; if ($is_superuser) { $tabs[] =& $tab_privileges; } } +if (PMA_Tracker::isActive()) { + $tabs[] =& $tab_tracking; +} +if (! $db_is_information_schema) { + if ($cfgRelation['designerwork']) { + $tabs[] =& $tab_designer; + } +} $url_params['db'] = $db; diff --git a/libraries/display_export.lib.php b/libraries/display_export.lib.php index 62641f33b..ad52bacf4 100644 --- a/libraries/display_export.lib.php +++ b/libraries/display_export.lib.php @@ -69,7 +69,7 @@ if(isset($_GET['export_method'])) { $cfg['Export']['method'] = 'quick'; } // The export method (quick, custom or custom-no-form) -echo ''; +echo ''; if(isset($_GET['sql_query'])) { @@ -255,23 +255,17 @@ if(isset($_GET['sql_query'])) { echo $_GET['filename_template']; } else { if ($export_type == 'database') { - if (isset($_COOKIE) && !empty($_COOKIE['pma_db_filename_template'])) { - echo htmlspecialchars($_COOKIE['pma_db_filename_template']); - } else { - echo $GLOBALS['cfg']['Export']['file_template_database']; - } + echo htmlspecialchars($GLOBALS['PMA_Config']->getUserValue( + 'pma_db_filename_template', + $GLOBALS['cfg']['Export']['file_template_database'])); } elseif ($export_type == 'table') { - if (isset($_COOKIE) && !empty($_COOKIE['pma_table_filename_template'])) { - echo htmlspecialchars($_COOKIE['pma_table_filename_template']); - } else { - echo $GLOBALS['cfg']['Export']['file_template_table']; - } + echo htmlspecialchars($GLOBALS['PMA_Config']->getUserValue( + 'pma_table_filename_template', + $GLOBALS['cfg']['Export']['file_template_table'])); } else { - if (isset($_COOKIE) && !empty($_COOKIE['pma_server_filename_template'])) { - echo htmlspecialchars($_COOKIE['pma_server_filename_template']); - } else { - echo $GLOBALS['cfg']['Export']['file_template_server']; - } + echo htmlspecialchars($GLOBALS['PMA_Config']->getUserValue( + 'pma_server_filename_template', + $GLOBALS['cfg']['Export']['file_template_server'])); } } echo '"'; diff --git a/libraries/export/sql.php b/libraries/export/sql.php index 2d6708dba..ade857f59 100644 --- a/libraries/export/sql.php +++ b/libraries/export/sql.php @@ -109,7 +109,7 @@ if (isset($plugin_list)) { /* begin CREATE TABLE statements*/ $plugin_list['sql']['options'][] = array('type' => 'begin_subgroup', 'subgroup_header' => array('type' => 'bool', 'name' => 'create_table_statements', 'text' => __('CREATE TABLE options:'))); - $plugin_list['sql']['options'][] = + $plugin_list['sql']['options'][] = array('type' => 'bool', 'name' => 'if_not_exists', 'text' => 'IF NOT EXISTS'); $plugin_list['sql']['options'][] = array('type' => 'bool', 'name' => 'auto_increment', 'text' => 'AUTO_INCREMENT'); diff --git a/libraries/footer.inc.php b/libraries/footer.inc.php index 715cf3b82..dfa7ab9d8 100644 --- a/libraries/footer.inc.php +++ b/libraries/footer.inc.php @@ -115,7 +115,15 @@ if (window.parent.refreshNavigation) { window.parent.refreshNavigation(); } + if (window.parent && window.parent.frame_navigation) { + window.parent.frame_navigation.location.reload(); + } + // set current db, table and sql query in the querywindow if (window.parent.reload_querywindow) { diff --git a/libraries/header.inc.php b/libraries/header.inc.php index c6f71079e..ea159a3c8 100644 --- a/libraries/header.inc.php +++ b/libraries/header.inc.php @@ -21,6 +21,14 @@ if (empty($GLOBALS['is_header_sent'])) { require_once './libraries/ob.lib.php'; PMA_outBufferPre(); + // if database storage for user preferences is transient, offer to load + // exported settings from localStorage (detection will be done in JavaScript) + $userprefs_offer_import = $GLOBALS['PMA_Config']->get('user_preferences') == 'session' + && !isset($_SESSION['userprefs_autoload']); + if ($userprefs_offer_import) { + $GLOBALS['js_include'][] = 'config.js'; + } + // For re-usability, moved http-headers and stylesheets // to a seperate file. It can now be included by header.inc.php, // querywindow.php. @@ -58,6 +66,12 @@ if (empty($GLOBALS['is_header_sent'])) { PMA_Message::notice(__('Cookies must be enabled past this point.'))->display(); } + // offer to load user preferences from localStorage + if ($userprefs_offer_import) { + require_once './libraries/user_preferences.lib.php'; + PMA_userprefs_autoload_header(); + } + if (!defined('PMA_DISPLAY_HEADING')) { define('PMA_DISPLAY_HEADING', 1); } diff --git a/libraries/header_meta_style.inc.php b/libraries/header_meta_style.inc.php index 55e799131..46cca27b3 100644 --- a/libraries/header_meta_style.inc.php +++ b/libraries/header_meta_style.inc.php @@ -53,7 +53,7 @@ if ($GLOBALS['text_dir'] == 'ltr') { - + diff --git a/libraries/header_scripts.inc.php b/libraries/header_scripts.inc.php index 94aff1a71..8126dd39d 100644 --- a/libraries/header_scripts.inc.php +++ b/libraries/header_scripts.inc.php @@ -59,7 +59,7 @@ foreach ($GLOBALS['js_include'] as $js_script_file) { // Updates the title of the frameset if possible (ns4 does not allow this) if (typeof(parent.document) != 'undefined' && typeof(parent.document) != 'unknown' && typeof(parent.document.title) == 'string') { - parent.document.title = ''; + parent.document.title = ''; } '; } - echo '' . "\n"; + echo '' . "\n"; } // end of display logo ?> '; diff --git a/prefs_forms.php b/prefs_forms.php new file mode 100644 index 000000000..40ea1719f --- /dev/null +++ b/prefs_forms.php @@ -0,0 +1,89 @@ + $form) { + // skip Developer form if no setting is available + if ($form_name == 'Developer' && !$GLOBALS['cfg']['UserprefsDeveloperTab']) { + continue; + } + $form_display->registerForm($form_name, $form, 1); +} + +if (isset($_POST['revert'])) { + // revert erroneous fields to their default values + $form_display->fixErrors(); + // redirect + $url_params = array('form' => $form_param); + PMA_sendHeaderLocation($cfg['PmaAbsoluteUri'] . 'prefs_forms.php' + . PMA_generate_common_url($url_params, '&')); + exit; +} + +$error = null; +if ($form_display->process(false) && !$form_display->hasErrors()) { + // save settings + $old_settings = PMA_load_userprefs(); + $result = PMA_save_userprefs(ConfigFile::getInstance()->getConfigArray()); + if ($result === true) { + // reload config + $GLOBALS['PMA_Config']->loadUserPreferences(); + $hash = ltrim(filter_input(INPUT_POST, 'tab_hash'), '#'); + PMA_userprefs_redirect($forms, $old_settings, 'prefs_forms.php', array( + 'form' => $form_param), $hash); + exit; + } else { + $error = $result; + } +} + +// display forms +$GLOBALS['js_include'][] = 'config.js'; +require './libraries/header.inc.php'; +require './libraries/user_preferences.inc.php'; +if ($error) { + $error->display(); +} +if ($form_display->hasErrors()) { + // form has errors + ?> +
    + + displayErrors(); ?> +
    + display(true, true); + +/** + * Displays the footer + */ +require './libraries/footer.inc.php'; +?> \ No newline at end of file diff --git a/prefs_manage.php b/prefs_manage.php new file mode 100644 index 000000000..d678f8a88 --- /dev/null +++ b/prefs_manage.php @@ -0,0 +1,337 @@ + json_encode($settings['config_data']), + 'mtime' => $settings['mtime'])); + return; +} else if (isset($_POST['submit_import'])) { + // load from JSON file + $json = ''; + if (filter_input(INPUT_POST, 'import_type') == 'text_file' + && isset($_FILES['import_file']) + && $_FILES['import_file']['error'] == UPLOAD_ERR_OK + && is_uploaded_file($_FILES['import_file']['tmp_name'])) { + // read JSON from uploaded file + $open_basedir = @ini_get('open_basedir'); + $file_to_unlink = ''; + $import_file = $_FILES['import_file']['tmp_name']; + + // If we are on a server with open_basedir, we must move the file + // before opening it. The doc explains how to create the "./tmp" + // directory + if (!empty($open_basedir)) { + $tmp_subdir = (PMA_IS_WINDOWS ? '.\\tmp\\' : './tmp/'); + if (is_writable($tmp_subdir)) { + $import_file_new = tempnam($tmp_subdir, 'prefs'); + if (move_uploaded_file($import_file, $import_file_new)) { + $import_file = $import_file_new; + $file_to_unlink = $import_file_new; + } + } + } + $json = file_get_contents($import_file); + if ($file_to_unlink) { + unlink($file_to_unlink); + } + } else { + // read from POST value (json) + $json = filter_input(INPUT_POST, 'json'); + } + + // hide header message + $_SESSION['userprefs_autoload'] = true; + + $config = json_decode($json, true); + $return_url = filter_input(INPUT_POST, 'return_url'); + if (!is_array($config)) { + $error = __('Could not import configuration'); + } else { + // sanitize input values: treat them as though they came from HTTP POST request + $form_display = new FormDisplay(); + foreach ($forms as $formset_id => $formset) { + foreach ($formset as $form_name => $form) { + $form_display->registerForm($formset_id . ': ' . $form_name, $form); + } + } + $cf = ConfigFile::getInstance(); + $new_config = $cf->getFlatDefaultConfig(); + if (!empty($_POST['import_merge'])) { + $new_config = array_merge($new_config, $cf->getConfigArray()); + } + $new_config = array_merge($new_config, $config); + $_POST_bak = $_POST; + foreach ($new_config as $k => $v) { + $_POST[str_replace('/', '-', $k)] = $v; + } + $cf->resetConfigData(); + $all_ok = $form_display->process(true, false); + $all_ok = $all_ok && !$form_display->hasErrors(); + $_POST = $_POST_bak; + + if (!$all_ok && isset($_POST['fix_errors'])) { + $form_display->fixErrors(); + $all_ok = true; + } + if (!$all_ok) { + // mimic original form and post json in a hidden field + require './libraries/header.inc.php'; + require './libraries/user_preferences.inc.php'; + $msg = PMA_Message::warning(__('Configuration contains incorrect data for some fields.')); + $msg->display(); + echo '
    '; + $form_display->displayErrors(); + echo '
    '; + ?> +
    + + + + + + + + + +

    + + +
    + theme->getId() != $config['ThemeDefault'] + && $_SESSION['PMA_Theme_Manager']->checkTheme($config['ThemeDefault'])) { + $_SESSION['PMA_Theme_Manager']->setActiveTheme($config['ThemeDefault']); + $_SESSION['PMA_Theme_Manager']->setThemeCookie(); + $params['reload_left_frame'] = true; + } + if (isset($config['fontsize']) + && $config['fontsize'] != $GLOBALS['PMA_Config']->get('fontsize')) { + $params['set_fontsize'] = $config['fontsize']; + $params['reload_left_frame'] = true; + } + if (isset($config['lang']) + && $config['lang'] != $GLOBALS['lang']) { + $params['lang'] = $config['lang']; + $params['reload_left_frame'] = true; + } + if (isset($config['collation_connection']) + && $config['collation_connection'] != $GLOBALS['collation_connection']) { + $params['collation_connection'] = $config['collation_connection']; + $params['reload_left_frame'] = true; + } + + // save settings + $old_settings = PMA_load_userprefs(); + $result = PMA_save_userprefs($cf->getConfigArray()); + if ($result === true) { + if ($return_url) { + $query = explode('&', parse_url($return_url, PHP_URL_QUERY)); + $return_url = parse_url($return_url, PHP_URL_PATH); + foreach ($query as $q) { + $pos = strpos($q, '='); + $k = substr($q, 0, $pos); + if ($k == 'token') { + continue; + } + $params[$k] = substr($q, $pos+1); + } + } else { + $return_url = 'prefs_manage.php'; + } + // reload config + $GLOBALS['PMA_Config']->loadUserPreferences(); + PMA_userprefs_redirect($forms, $old_settings, $return_url, $params); + exit; + } else { + $error = $result; + } + } +} else if (isset($_POST['submit_clear'])) { + $old_settings = PMA_load_userprefs(); + $result = PMA_save_userprefs(array()); + if ($result === true) { + $params = array(); + if ($_SESSION['PMA_Theme_Manager']->theme->getId() != 'original') { + $GLOBALS['PMA_Config']->removeCookie($_SESSION['PMA_Theme_Manager']->getThemeCookieName()); + unset($_SESSION['PMA_Theme_Manager']); + unset($_SESSION['PMA_Theme']); + $params['reload_left_frame'] = true; + } + if ($GLOBALS['PMA_Config']->get('fontsize') != '82%') { + $GLOBALS['PMA_Config']->removeCookie('pma_fontsize'); + $params['reload_left_frame'] = true; + } + $GLOBALS['PMA_Config']->removeCookie('pma_collaction_connection'); + $GLOBALS['PMA_Config']->removeCookie('pma_lang'); + PMA_userprefs_redirect($forms, $old_settings, 'prefs_manage.php', $params); + exit; + } else { + $error = $result; + } + exit; +} + +$GLOBALS['js_include'][] = 'config.js'; +require './libraries/header.inc.php'; +require './libraries/user_preferences.inc.php'; +if ($error) { + if (!$error instanceof PMA_Message) { + $error = PMA_Message::error($error); + } + $error->display(); +} +?> + +
    +
    +
    +

    +
    + + + + +
    + + +
    + + +
    +
    + +
    + + + + + display() ?> + +
    + + display() ?> + +
    + + + +

    + +
    +
    + +
    +

    +
    + ', ''); + echo PMA_showDocu('setup_script'); + ?> +
    +
    + +
    +
    +
    +

    + +
    + +
    + + +
    + + +
    +
    + + + +
    +
    +
    + + display() ?> + +
    +
    + +
    +
    +
    +

    +
    + + +

    + +
    + +
    +
    +
    +
    + \ No newline at end of file diff --git a/scripts/create_tables.sql b/scripts/create_tables.sql index e4d0fc811..ca7325a54 100644 --- a/scripts/create_tables.sql +++ b/scripts/create_tables.sql @@ -192,3 +192,18 @@ CREATE TABLE IF NOT EXISTS `pma_tracking` ( `tracking_active` int(1) unsigned NOT NULL default '1', PRIMARY KEY (`db_name`,`table_name`,`version`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=COMPACT; + +-- -------------------------------------------------------- + +-- +-- Table structure for table `pma_userconfig` +-- + +CREATE TABLE IF NOT EXISTS `pma_userconfig` ( + `username` varchar(64) NOT NULL, + `timevalue` timestamp NOT NULL, + `config_data` text NOT NULL, + PRIMARY KEY (`username`) +) + ENGINE=MyISAM COMMENT='User preferences storage for phpMyAdmin' + DEFAULT CHARACTER SET utf8 COLLATE utf8_bin; diff --git a/scripts/find_unused_messages.sh b/scripts/find_unused_messages.sh index ea8394b4b..bf01ffc65 100755 --- a/scripts/find_unused_messages.sh +++ b/scripts/find_unused_messages.sh @@ -29,7 +29,7 @@ echo echo Not used messages present in messages file: echo -diff $tmp1 $tmp2 | awk '/^registerForm('_config.php'); +$form_display->registerForm('_config.php', $forms['_config.php']); $form_display->save('_config.php'); $config_file_path = ConfigFile::getInstance()->getFilePath(); @@ -27,7 +29,7 @@ if (PMA_ifSetOr($_POST['submit_clear'], '')) { // // Clear current config and return to main page // - $_SESSION['ConfigFile'] = array(); + ConfigFile::getInstance()->resetConfigData(); // drop post data header('HTTP/1.1 303 See Other'); header('Location: index.php'); @@ -38,13 +40,13 @@ if (PMA_ifSetOr($_POST['submit_clear'], '')) { // header('Content-Type: text/plain'); header('Content-Disposition: attachment; filename="config.inc.php"'); - echo ConfigFile::getInstance()->getConfigFile(); + echo ConfigGenerator::getConfigFile(); exit; } elseif (PMA_ifSetOr($_POST['submit_save'], '')) { // // Save generated config file on the server // - file_put_contents($config_file_path, ConfigFile::getInstance()->getConfigFile()); + file_put_contents($config_file_path, ConfigGenerator::getConfigFile()); header('HTTP/1.1 303 See Other'); header('Location: index.php'); exit; @@ -54,7 +56,7 @@ if (PMA_ifSetOr($_POST['submit_clear'], '')) { // $cfg = array(); require_once $config_file_path; - $_SESSION['ConfigFile'] = $cfg; + ConfigFile::getInstance()->setConfigData($cfg); header('HTTP/1.1 303 See Other'); header('Location: index.php'); exit; diff --git a/setup/frames/.htaccess b/setup/frames/.htaccess new file mode 100644 index 000000000..56baee608 --- /dev/null +++ b/setup/frames/.htaccess @@ -0,0 +1,3 @@ +# This folder does not require access over HTTP +# (the following directive denies access by default) +Order allow,deny diff --git a/setup/frames/config.inc.php b/setup/frames/config.inc.php index 4e925d912..e56898fa0 100644 --- a/setup/frames/config.inc.php +++ b/setup/frames/config.inc.php @@ -1,10 +1,9 @@ diff --git a/setup/frames/form.inc.php b/setup/frames/form.inc.php index 019b110be..27429b44b 100644 --- a/setup/frames/form.inc.php +++ b/setup/frames/form.inc.php @@ -1,10 +1,9 @@ array( - 'forms' => array('Import_export', 'Security', 'Sql_queries', 'Other_core_settings')), - 'left_frame' => array( - 'forms' => array('Left_frame', 'Left_servers', 'Left_databases', 'Left_tables')), - 'main_frame' => array( - 'forms' => array('Startup', 'Browse', 'Edit', 'Tabs', 'Sql_box')), - 'import' => array( - 'forms' => array('Import_defaults')), - 'export' => array( - 'forms' => array('Export_defaults')) -); +require './libraries/config/setup.forms.php'; $formset_id = filter_input(INPUT_GET, 'formset'); $mode = filter_input(INPUT_GET, 'mode'); -if (!isset($formsets[$formset_id])) { +if (!isset($forms[$formset_id])) { die('Incorrect formset, check $formsets array in setup/frames/form.inc.php'); } -$formset = $formsets[$formset_id]; -if (isset($GLOBALS['strSetupFormset_' . $formset_id])) { - echo '

    ' . $GLOBALS['strSetupFormset_' . $formset_id] . '

    '; +if (isset($GLOBALS['strConfigFormset_' . $formset_id])) { + echo '

    ' . $GLOBALS['strConfigFormset_' . $formset_id] . '

    '; } $form_display = new FormDisplay(); -foreach ($formset['forms'] as $form_name) { - $form_display->registerForm($form_name); +foreach ($forms[$formset_id] as $form_name => $form) { + $form_display->registerForm($form_name, $form); } process_formset($form_display); -?> +?> \ No newline at end of file diff --git a/setup/frames/index.inc.php b/setup/frames/index.inc.php index 43b657466..1b1285cee 100644 --- a/setup/frames/index.inc.php +++ b/setup/frames/index.inc.php @@ -2,9 +2,7 @@ /** * Overview (main page) * - * @package phpMyAdmin-setup - * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ + * @package phpMyAdmin-setup */ if (!defined('PHPMYADMIN')) { @@ -15,7 +13,7 @@ if (!defined('PHPMYADMIN')) { * Core libraries. */ require_once './libraries/display_select_lang.lib.php'; -require_once './setup/lib/FormDisplay.class.php'; +require_once './libraries/config/FormDisplay.class.php'; require_once './setup/lib/index.lib.php'; // prepare unfiltered language list @@ -48,7 +46,8 @@ $config_writable = false; $config_exists = false; check_config_rw($config_readable, $config_writable, $config_exists); if (!$config_writable || !$config_readable) { - messages_set('error', 'config_rw', 'CannotLoadConfig', PMA_lang('CannotLoadConfigMsg')); + messages_set('error', 'config_rw', __('Cannot load or save configuration'), + PMA_lang(__('Please create web server writable folder [em]config[/em] in phpMyAdmin top level directory as described in [a@Documentation.html#setup_script]documentation[/a]. Otherwise you will be only able to download or display it.'))); } // // Check https connection @@ -56,11 +55,13 @@ if (!$config_writable || !$config_readable) { $is_https = !empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; if (!$is_https) { $text = __('You are not using a secure connection; all data (including potentially sensitive information, like passwords) is transferred unencrypted!'); + if (!empty($_SERVER['REQUEST_URI']) && !empty($_SERVER['HTTP_HOST'])) { - $text .= ' ' . PMA_lang('InsecureConnectionMsg2', + $strInsecureConnectionMsg2 = __('If your server is also configured to accept HTTPS requests follow [a@%s]this link[/a] to use a secure connection.'); + $text .= ' ' . PMA_lang($strInsecureConnectionMsg2, 'https://' . htmlspecialchars($_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'])); } - messages_set('warning', 'no_https', 'InsecureConnection', $text); + messages_set('warning', 'no_https', __('Insecure connection'), $text); } ?> @@ -110,10 +111,10 @@ display_form_top('index.php', 'get', array( # - Authentication type + DSN - $server): ?> +getServers() as $id => $server): ?> getServerName($id) ?> @@ -186,7 +187,7 @@ if ($cf->getServerCount() > 0) { } $opts['values_disabled'][] = '-'; - foreach ($_SESSION['ConfigFile']['Servers'] as $id => $server) { + foreach ($cf->getServers() as $id => $server) { $opts['values'][(string)$id] = $cf->getServerName($id) . " [$id]"; } } else { diff --git a/setup/frames/menu.inc.php b/setup/frames/menu.inc.php index 1da38512b..4a1ae9c2b 100644 --- a/setup/frames/menu.inc.php +++ b/setup/frames/menu.inc.php @@ -2,9 +2,7 @@ /** * Menu items * - * @package phpMyAdmin-setup - * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ + * @package phpMyAdmin-setup */ if (!defined('PHPMYADMIN')) { @@ -15,9 +13,10 @@ $separator = PMA_get_arg_separator('html'); ?> diff --git a/setup/frames/servers.inc.php b/setup/frames/servers.inc.php index 17ff2f9e4..081c416ec 100644 --- a/setup/frames/servers.inc.php +++ b/setup/frames/servers.inc.php @@ -2,9 +2,7 @@ /** * Server create and edit view * - * @package phpMyAdmin-setup - * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ + * @package phpMyAdmin-setup */ if (!defined('PHPMYADMIN')) { @@ -14,10 +12,12 @@ if (!defined('PHPMYADMIN')) { /** * Core libraries. */ -require_once './setup/lib/Form.class.php'; -require_once './setup/lib/FormDisplay.class.php'; +require_once './libraries/config/Form.class.php'; +require_once './libraries/config/FormDisplay.class.php'; require_once './setup/lib/form_processing.lib.php'; +require './libraries/config/setup.forms.php'; + $mode = filter_input(INPUT_GET, 'mode'); $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT); @@ -41,10 +41,8 @@ if (isset($page_title)) { echo '

    ' . $page_title . '

    '; } $form_display = new FormDisplay(); -$form_display->registerForm('Server', $id); -$form_display->registerForm('Server_login_options', $id); -$form_display->registerForm('Server_config', $id); -$form_display->registerForm('Server_pmadb', $id); -$form_display->registerForm('Server_tracking', $id); +foreach ($forms['Servers'] as $form_name => $form) { + $form_display->registerForm($form_name, $form, $id); +} process_formset($form_display); -?> +?> \ No newline at end of file diff --git a/setup/index.php b/setup/index.php index 0d16e5f06..7d7892819 100644 --- a/setup/index.php +++ b/setup/index.php @@ -5,7 +5,6 @@ * @package phpMyAdmin-setup * @copyright Copyright (c) 2008, Piotr Przybylski * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ */ /** @@ -37,6 +36,7 @@ require './libraries/header_http.inc.php'; + diff --git a/setup/lib/ConfigFile.class.php b/setup/lib/ConfigFile.class.php deleted file mode 100644 index e7e7b288f..000000000 --- a/setup/lib/ConfigFile.class.php +++ /dev/null @@ -1,322 +0,0 @@ -cfg; - require './libraries/config.default.php'; - - // load additionsl config information - $cfg_db = &$this->cfgDb; - $persist_keys = array(); - require './setup/lib/config_info.inc.php'; - - // apply default values overrides - if (count($cfg_db['_overrides'])) { - foreach ($cfg_db['_overrides'] as $path => $value) { - array_write($path, $cfg, $value); - } - } - - // checking key presence is much faster than searching so move values to keys - $this->persistKeys = array_flip($persist_keys); - } - - /** - * Returns class instance - * - * @return ConfigFile - */ - public static function getInstance() - { - if (is_null(self::$_instance)) { - self::$_instance = new ConfigFile(); - } - return self::$_instance; - } - - /** - * Sets config value - * - * @param string $path - * @param mixed $value - * @param string $canonical_path - */ - public function set($path, $value, $canonical_path = null) - { - if ($canonical_path === null) { - $canonical_path = $this->getCanonicalPath($path); - } - // remove if the path isn't protected and it's empty or has a default value - $default_value = $this->getDefault($canonical_path); - if (!isset($this->persistKeys[$canonical_path]) - && (($value == $default_value) || (empty($value) && empty($default_value)))) { - array_remove($path, $_SESSION['ConfigFile']); - } else { - array_write($path, $_SESSION['ConfigFile'], $value); - } - } - - /** - * Returns config value or $default if it's not set - * - * @param string $path - * @param mixed $default - * @return mixed - */ - public function get($path, $default = null) - { - return array_read($path, $_SESSION['ConfigFile'], $default); - } - - /** - * Returns default config value or $default it it's not set ie. it doesn't - * exist in config.default.php ($cfg) and config_info.inc.php - * ($_cfg_db['_overrides']) - * - * @param string $canonical_path - * @param mixed $default - * @return mixed - */ - public function getDefault($canonical_path, $default = null) - { - return array_read($canonical_path, $this->cfg, $default); - } - - /** - * Returns config value, if it's not set uses the default one; returns - * $default if the path isn't set and doesn't contain a default value - * - * @param string $path - * @param mixed $default - * @return mixed - */ - public function getValue($path, $default = null) - { - $v = array_read($path, $_SESSION['ConfigFile'], null); - if ($v !== null) { - return $v; - } - $path = $this->getCanonicalPath($path); - return $this->getDefault($path, $default); - } - - /** - * Returns canonical path - * - * @param string $path - * @return string - */ - public function getCanonicalPath($path) { - return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path); - } - - /** - * Returns config database entry for $path ($cfg_db in config_info.php) - * - * @param string $path - * @param mixed $default - * @return mixed - */ - public function getDbEntry($path, $default = null) - { - return array_read($path, $this->cfgDb, $default); - } - - /** - * Returns server count - * - * @return int - */ - public function getServerCount() - { - return isset($_SESSION['ConfigFile']['Servers']) - ? count($_SESSION['ConfigFile']['Servers']) - : 0; - } - - /** - * Returns DSN of given server - * - * @param integer $server - * @return string - */ - function getServerDSN($server) - { - if (!isset($_SESSION['ConfigFile']['Servers'][$server])) { - return ''; - } - - $path = 'Servers/' . $server; - $dsn = $this->getValue("$path/extension") . '://'; - if ($this->getValue("$path/auth_type") == 'config') { - $dsn .= $this->getValue("$path/user"); - if (!$this->getValue("$path/nopassword")) { - $dsn .= ':***'; - } - $dsn .= '@'; - } - if ($this->getValue("$path/connect_type") == 'tcp') { - $dsn .= $this->getValue("$path/host"); - $port = $this->getValue("$path/port"); - if ($port) { - $dsn .= ':' . $port; - } - } else { - $dsn .= $this->getValue("$path/socket"); - } - return $dsn; - } - - /** - * Returns server name - * - * @param int $id - * @return string - */ - public function getServerName($id) - { - if (!isset($_SESSION['ConfigFile']['Servers'][$id])) { - return ''; - } - $verbose = $this->get("Servers/$id/verbose"); - if (!empty($verbose)) { - return $verbose; - } - $host = $this->get("Servers/$id/host"); - return empty($host) ? 'localhost' : $host; - } - - /** - * Removes server - * - * @param int $server - */ - public function removeServer($server) - { - if (!isset($_SESSION['ConfigFile']['Servers'][$server])) { - return; - } - $last_server = $this->getServerCount(); - - for ($i = $server; $i < $last_server; $i++) { - $_SESSION['ConfigFile']['Servers'][$i] = $_SESSION['ConfigFile']['Servers'][$i+1]; - } - unset($_SESSION['ConfigFile']['Servers'][$last_server]); - - if (isset($_SESSION['ConfigFile']['ServerDefault']) - && $_SESSION['ConfigFile']['ServerDefault'] >= 0) { - unset($_SESSION['ConfigFile']['ServerDefault']); - } - } - - /** - * Returns config file path - * - * @return unknown - */ - public function getFilePath() - { - return $this->getDbEntry('_config_file_path'); - } - - /** - * Creates config file - * - * @return string - */ - public function getConfigFile() - { - $crlf = (isset($_SESSION['eol']) && $_SESSION['eol'] == 'win') ? "\r\n" : "\n"; - $c = $_SESSION['ConfigFile']; - - // header - $ret = 'get('PMA_VERSION') - . ' setup script by Piotr Przybylski ' . $crlf - . ' * Date: ' . date(DATE_RFC1123) . $crlf - . ' */' . $crlf . $crlf; - - // servers - if ($this->getServerCount() > 0) { - $ret .= "/* Servers configuration */$crlf\$i = 0;" . $crlf . $crlf; - foreach ($c['Servers'] as $id => $server) { - $ret .= '/* Server: ' . strtr($this->getServerName($id), '*/', '-') . " [$id] */" . $crlf - . '$i++;' . $crlf; - foreach ($server as $k => $v) { - $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); - $ret .= "\$cfg['Servers'][\$i]['$k'] = " - . var_export($v, true) . ';' . $crlf; - } - $ret .= $crlf; - } - $ret .= '/* End of servers configuration */' . $crlf . $crlf; - } - unset($c['Servers']); - - // other settings - $persistKeys = $this->persistKeys; - foreach ($c as $k => $v) { - $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); - $ret .= "\$cfg['$k'] = " . var_export($v, true) . ';' . $crlf; - if (isset($persistKeys[$k])) { - unset($persistKeys[$k]); - } - } - // keep 1d array keys which are present in $persist_keys (config_info.inc.php) - foreach (array_keys($persistKeys) as $k) { - if (strpos($k, '/') === false) { - $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); - $ret .= "\$cfg['$k'] = " . var_export($this->getDefault($k), true) . ';' . $crlf; - } - } - $ret .= '?>'; - - return $ret; - } -} -?> diff --git a/setup/lib/ConfigGenerator.class.php b/setup/lib/ConfigGenerator.class.php new file mode 100644 index 000000000..ddad7f124 --- /dev/null +++ b/setup/lib/ConfigGenerator.class.php @@ -0,0 +1,151 @@ +getConfig(); + + // header + $ret = 'get('PMA_VERSION') + . ' setup script by Piotr Przybylski ' . $crlf + . ' * Date: ' . date(DATE_RFC1123) . $crlf + . ' */' . $crlf . $crlf; + + // servers + if ($cf->getServerCount() > 0) { + $ret .= "/* Servers configuration */$crlf\$i = 0;" . $crlf . $crlf; + foreach ($c['Servers'] as $id => $server) { + $ret .= '/* Server: ' . strtr($cf->getServerName($id), '*/', '-') . " [$id] */" . $crlf + . '$i++;' . $crlf; + foreach ($server as $k => $v) { + $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); + $ret .= "\$cfg['Servers'][\$i]['$k'] = " + . (is_array($v) && self::_isZeroBasedArray($v) + ? self::_exportZeroBasedArray($v, $crlf) + : var_export($v, true)) + . ';' . $crlf; + } + $ret .= $crlf; + } + $ret .= '/* End of servers configuration */' . $crlf . $crlf; + } + unset($c['Servers']); + + // other settings + $persistKeys = $cf->getPersistKeysMap(); + + foreach ($c as $k => $v) { + $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); + $ret .= self::_getVarExport($k, $v, $crlf); + if (isset($persistKeys[$k])) { + unset($persistKeys[$k]); + } + } + // keep 1d array keys which are present in $persist_keys (config.values.php) + foreach (array_keys($persistKeys) as $k) { + if (strpos($k, '/') === false) { + $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); + $ret .= self::_getVarExport($k, $cf->getDefault($k), $crlf); + } + } + $ret .= '?>'; + + return $ret; + } + + /** + * Returns exported configuration variable + * + * @param string $var_name + * @param mixed $var_value + * @param string $crlf + * @return string + */ + private static function _getVarExport($var_name, $var_value, $crlf) + { + if (!is_array($var_value) || empty($var_value)) { + return "\$cfg['$var_name'] = " . var_export($var_value, true) . ';' . $crlf; + } + $ret = ''; + if (self::_isZeroBasedArray($var_value)) { + $ret = "\$cfg['$var_name'] = " . self::_exportZeroBasedArray($var_value, $crlf) + . ';' . $crlf; + } else { + // string keys: $cfg[key][subkey] = value + foreach ($var_value as $k => $v) { + $k = preg_replace('/[^A-Za-z0-9_]/', '_', $k); + $ret .= "\$cfg['$var_name']['$k'] = " . var_export($v, true) . ';' . $crlf; + } + } + return $ret; + } + + /** + * Check whether $array is a continuous 0-based array + * + * @param array $array + * @return boolean + */ + private static function _isZeroBasedArray(array $array) + { + for ($i = 0; $i < count($array); $i++) { + if (!isset($array[$i])) { + return false; + } + } + return true; + } + + /** + * Exports continuous 0-based array + * + * @param array $array + * @param string $crlf + * @return string + */ + private static function _exportZeroBasedArray(array $array, $crlf) + { + $retv = array(); + foreach ($array as $v) { + $retv[] = var_export($v, true); + } + $ret = "array("; + if (count($retv) <= 4) { + // up to 4 values - one line + $ret .= implode(', ', $retv); + } else { + // more than 4 values - value per line + $imax = count($retv)-1; + for ($i = 0; $i <= $imax; $i++) { + $ret .= ($i < $imax ? ($i > 0 ? ',' : '') : '') . $crlf . ' ' . $retv[$i]; + } + } + $ret .= ')'; + return $ret; + } +} +?> \ No newline at end of file diff --git a/setup/lib/FormDisplay.tpl.php b/setup/lib/FormDisplay.tpl.php deleted file mode 100644 index f6cb5a53f..000000000 --- a/setup/lib/FormDisplay.tpl.php +++ /dev/null @@ -1,293 +0,0 @@ - -
    -' . "\n"; - } - echo PMA_generate_common_hidden_inputs() . "\n"; - echo PMA_getHiddenFields((array)$hidden_fields); -} - -/** - * Displays form tabs which are given by an array indexed by fieldset id - * ({@link display_fieldset_top}), with values being tab titles. - * - * @param array $tabs - */ -function display_tabs_top($tabs) { -?> -
      - $tab_name): ?> -
    • - -
    -
    -
    - 'optbox'), $attributes); - foreach ($attributes as $k => &$attr) { - $attr = $k . '="' . htmlspecialchars($attr) . '"'; - } - - echo '
    '; - echo '' . $title . ''; - if (!empty($description)) { - echo '

    ' . $description . '

    '; - } - // this must match with displayErrors() in scripts.js - if (is_array($errors) && count($errors) > 0) { - echo '
    '; - foreach ($errors as $error) { - echo '
    ' . $error . '
    '; - } - echo '
    '; - } -?> - - fields - * o values_escaped - (boolean) tells whether values array is already escaped (defaults to false) - * o values_disabled - (array)list of disabled values (keys from values) - * o wiki - (string) wiki link - * - * @param string $path - * @param string $name - * @param string $description - * @param string $type - * @param mixed $value - * @param bool $value_is_default - * @param array $opts - */ -function display_input($path, $name, $description = '', $type, $value, $value_is_default = true, $opts = null) -{ - $field_class = $value_is_default ? '' : ' class="custom"'; - $name_id = 'name="' . $path . '" id="' . $path . '"'; -?> - - - - - - - - -
    - - - - Doc - Wiki - - - - - - '; - break; - case 'checkbox': - echo ''; - break; - case 'select': - echo ''; - break; - case 'list': - echo ''; - break; - } - if (isset($opts['setvalue']) && $opts['setvalue']) { - ?> - " title="" style="display:none">set-value - - - "; - foreach ($opts['errors'] as $error) { - echo '
    ' . htmlspecialchars($error) . '
    '; - } - echo ''; - } - ?> - -
    - - -
    -
    - - - - - -\n"; -} - -/** - * Displays bottom part of the form - */ -function display_form_bottom() -{ - echo "\n"; -} - -/** - * Appends JS validation code to $js_array - * - * @param string $field_id - * @param string $validator - * @param array $js_array - */ -function js_validate($field_id, $validator, &$js_array) { - $js_array[] = "validateField('$field_id', '$validator', true)"; -} - -/** - * Displays JavaScript code - * - * @param array $js_array - */ -function display_js($js_array) { - if (empty($js_array)) { - return; - } -?> - -'; - echo '
    ' . htmlspecialchars($name) . '
    '; - foreach ($error_list as $error) { - echo '
    ' . htmlspecialchars($error) . '
    '; - } - echo '
    '; -} -?> diff --git a/setup/lib/common.inc.php b/setup/lib/common.inc.php index 2d1f1f691..8b43caff9 100644 --- a/setup/lib/common.inc.php +++ b/setup/lib/common.inc.php @@ -1,10 +1,9 @@ setCookie('pma_lang', $GLOBALS['lang']); -if (!isset($_SESSION['ConfigFile'])) { - $_SESSION['ConfigFile'] = array(); -} +ConfigFile::getInstance()->setPersistKeys(array( + 'DefaultLang', + 'ServerDefault', + 'UploadDir', + 'SaveDir', + 'Servers/1/verbose', + 'Servers/1/host', + 'Servers/1/port', + 'Servers/1/socket', + 'Servers/1/extension', + 'Servers/1/connect_type', + 'Servers/1/auth_type', + 'Servers/1/user', + 'Servers/1/password')); // allows for redirection even after sending some data ob_start(); -/** - * Returns value of an element in $array given by $path. - * $path is a string describing position of an element in an associative array, - * eg. Servers/1/host refers to $array[Servers][1][host] - * - * @param string $path - * @param array $array - * @param mixed $default - * @return mixed array element or $default - */ -function array_read($path, $array, $default = null) -{ - $keys = explode('/', $path); - $value =& $array; - foreach ($keys as $key) { - if (!isset($value[$key])) { - return $default; - } - $value =& $value[$key]; - } - return $value; -} - -/** - * Stores value in an array - * - * @param string $path - * @param array &$array - * @param mixed $value - */ -function array_write($path, &$array, $value) -{ - $keys = explode('/', $path); - $last_key = array_pop($keys); - $a =& $array; - foreach ($keys as $key) { - if (!isset($a[$key])) { - $a[$key] = array(); - } - $a =& $a[$key]; - } - $a[$last_key] = $value; -} - -/** - * Removes value from an array - * - * @param string $path - * @param array &$array - * @param mixed $value - */ -function array_remove($path, &$array) -{ - $keys = explode('/', $path); - $keys_last = array_pop($keys); - $path = array(); - $depth = 0; - - $path[0] =& $array; - $found = true; - // go as deep as required or possible - foreach ($keys as $key) { - if (!isset($path[$depth][$key])) { - $found = false; - break; - } - $depth++; - $path[$depth] =& $path[$depth-1][$key]; - } - // if element found, remove it - if ($found) { - unset($path[$depth][$keys_last]); - $depth--; - } - - // remove empty nested arrays - for (; $depth >= 0; $depth--) { - if (!isset($path[$depth+1]) || count($path[$depth+1]) == 0) { - unset($path[$depth][$keys[$depth]]); - } else { - break; - } - } -} - -/** - * Returns sanitized language string, taking into account our special codes - * for formatting. Takes variable number of arguments. - * Based on PMA_sanitize from sanitize.lib.php. - * - * @param string $lang_key key in $GLOBALS WITHOUT 'strSetup' prefix - * @param mixed $args arguments for sprintf - * @return string - */ -function PMA_lang($lang_key) -{ - static $search, $replace; - - // some quick cache'ing - if ($search === null) { - $replace_pairs = array( - '<' => '<', - '>' => '>', - '[em]' => '', - '[/em]' => '', - '[strong]' => '', - '[/strong]' => '', - '[code]' => '', - '[/code]' => '', - '[kbd]' => '', - '[/kbd]' => '', - '[br]' => '
    ', - '[sup]' => '', - '[/sup]' => ''); - $search = array_keys($replace_pairs); - $replace = array_values($replace_pairs); - } - if (!isset($GLOBALS["strSetup$lang_key"])) { - return $lang_key; - } - $message = str_replace($search, $replace, $GLOBALS["strSetup$lang_key"]); - // replace [a@"$1"]$2[/a] with $2 - $message = preg_replace('#\[a@("?)([^\]]+)\1\]([^\[]+)\[/a\]#e', - "PMA_lang_link_replace('$2', '$3')", $message); - - if (func_num_args() == 1) { - return $message; - } else { - $args = func_get_args(); - array_shift($args); - return vsprintf($message, $args); - } -} - -/** - * Returns translated field name - * - * @param string $canonical_path - * @return string - */ -function PMA_lang_name($canonical_path) -{ - $lang_key = str_replace( - array('Servers/1/', '/'), - array('Servers/', '_'), - $canonical_path) . '_name'; - return isset($GLOBALS["strSetup$lang_key"]) - ? $GLOBALS["strSetup$lang_key"] - : $lang_key; -} - -/** - * Returns translated field description - * - * @param string $canonical_path - * @return string - */ -function PMA_lang_desc($canonical_path) -{ - $lang_key = str_replace( - array('Servers/1/', '/'), - array('Servers/', '_'), - $canonical_path) . '_desc'; - return isset($GLOBALS["strSetup$lang_key"]) - ? PMA_lang($lang_key) - : ''; -} - -/** - * Wraps link in <a> tags and replaces argument separator in internal links - * to the one returned by PMA_get_arg_separator() - * - * @param string $link - * @param string $text - * @return string - */ -function PMA_lang_link_replace($link, $text) -{ - static $separator; - - if (!isset($separator)) { - $separator = PMA_get_arg_separator('html'); - } - - if (!preg_match('#^http://#', $link)) { - $link = str_replace('&', $separator, $link); - } - - return '' . $text . ''; -} -?> +?> \ No newline at end of file diff --git a/setup/lib/config_info.inc.php b/setup/lib/config_info.inc.php deleted file mode 100644 index 71f2e233a..000000000 --- a/setup/lib/config_info.inc.php +++ /dev/null @@ -1,144 +0,0 @@ - array( - 'port' => 'integer', - 'connect_type' => array('tcp', 'socket'), - 'extension' => array('mysql', 'mysqli'), - 'auth_type' => array('config', 'http', 'signon', 'cookie'), - 'AllowDeny' => array( - 'order' => array('', 'deny,allow', 'allow,deny', 'explicit')), - 'only_db' => 'array')); -$cfg_db['RecodingEngine'] = array('auto', 'iconv', 'recode', 'none'); -$cfg_db['OBGzip'] = array('auto', true, false); -$cfg_db['ShowTooltipAliasTB'] = array('nested', true, false); -$cfg_db['DisplayDatabasesList'] = array('auto', true, false); -$cfg_db['LeftLogoLinkWindow'] = array('main', 'new'); -$cfg_db['LeftDefaultTabTable'] = array( - 'tbl_structure.php', // fields list - 'tbl_sql.php', // SQL form - 'tbl_select.php', // search page - 'tbl_change.php', // insert row page - 'sql.php'); // browse page -$cfg_db['NavigationBarIconic'] = array(true, false, 'both'); -$cfg_db['Order'] = array('ASC', 'DESC', 'SMART'); -$cfg_db['ProtectBinary'] = array(false, 'blob', 'all'); -$cfg_db['CharEditing'] = array('input', 'textarea'); -$cfg_db['PropertiesIconic'] = array(true, false, 'both'); -$cfg_db['DefaultTabServer'] = array( - 'main.php', // the welcome page (recommended for multiuser setups) - 'server_databases.php', // list of databases - 'server_status.php', // runtime information - 'server_variables.php', // MySQL server variables - 'server_privileges.php', // user management - 'server_processlist.php'); // process list -$cfg_db['DefaultTabDatabase'] = array( - 'db_structure.php', // tables list - 'db_sql.php', // SQL form - 'db_search.php', // search query - 'db_operations.php'); // operations on database -$cfg_db['DefaultTabTable'] = array( - 'tbl_structure.php', // fields list - 'tbl_sql.php', // SQL form - 'tbl_select.php', // search page - 'tbl_change.php', // insert row page - 'sql.php'); // browse page -$cfg_db['QueryWindowDefTab'] = array( - 'sql', // SQL - 'files', // Import files - 'history', // SQL history - 'full'); // All (SQL and SQL history) -$cfg_db['Import']['format'] = array( - 'csv', // CSV - 'docsql', // DocSQL - 'ldi', // CSV using LOAD DATA - 'sql'); // SQL -$cfg_db['Import']['sql_compatibility'] = array( - 'NONE', 'ANSI', 'DB2', 'MAXDB', 'MYSQL323', 'MYSQL40', 'MSSQL', 'ORACLE', - // removed; in MySQL 5.0.33, this produces exports that - // can't be read by POSTGRESQL (see our bug #1596328) - //'POSTGRESQL', - 'TRADITIONAL'); -$cfg_db['Import']['ldi_local_option'] = array('auto', true, false); -$cfg_db['Export']['format'] = array('codegen', 'csv', 'excel', 'htmlexcel', - 'htmlword', 'latex', 'ods', 'odt', 'pdf', 'sql', 'texytext', 'xls', 'xml', - 'yaml'); -$cfg_db['Export']['compression'] = array('none', 'zip', 'gzip', 'bzip2'); -$cfg_db['Export']['charset'] = array_merge(array(''), $GLOBALS['cfg']['AvailableCharsets']); -$cfg_db['Export']['method'] = array('quick', 'custom', 'custom-no-form'); - -/** - * Config options which will be placed in config file even if they are set - * to their default values (use only full paths) - */ -$persist_keys = array( - 'DefaultLang', - 'ServerDefault', - 'UploadDir', - 'SaveDir', - 'Servers/1/verbose', - 'Servers/1/host', - 'Servers/1/port', - 'Servers/1/socket', - 'Servers/1/extension', - 'Servers/1/connect_type', - 'Servers/1/auth_type', - 'Servers/1/user', - 'Servers/1/password'); - -/** - * Default values overrides - * Use only full paths - */ -$cfg_db['_overrides'] = array(); -$cfg_db['_overrides']['Servers/1/extension'] = extension_loaded('mysqli') - ? 'mysqli' : 'mysql'; - -/** - * Validator assignments (functions from validate.lib.php and 'validators' - * object in scripts.js) - * Use only full paths and form ids - */ -$cfg_db['_validators'] = array( - 'Server' => 'validate_server', - 'Server_pmadb' => 'validate_pmadb', - 'Servers/1/port' => 'validate_port_number', - 'Servers/1/hide_db' => 'validate_regex', - 'TrustedProxies' => 'validate_trusted_proxies', - 'LoginCookieValidity' => 'validate_positive_number', - 'LoginCookieStore' => 'validate_non_negative_number', - 'QueryHistoryMax' => 'validate_positive_number', - 'LeftFrameTableLevel' => 'validate_positive_number', - 'MaxRows' => 'validate_positive_number', - 'CharTextareaCols' => 'validate_positive_number', - 'CharTextareaRows' => 'validate_positive_number', - 'InsertRows' => 'validate_positive_number', - 'ForeignKeyMaxLimit' => 'validate_positive_number', - 'Import/skip_queries' => 'validate_non_negative_number'); -?> diff --git a/setup/lib/form_processing.lib.php b/setup/lib/form_processing.lib.php index 7128a6119..7bad38c59 100644 --- a/setup/lib/form_processing.lib.php +++ b/setup/lib/form_processing.lib.php @@ -1,10 +1,9 @@

    -
    - +
    +
    displayErrors() ?> - +   - + array(1 => array('host'))); - * can be written as - * $forms['Form name'] = array('Servers/1/host'); - * - * You can assign default values set by special button ("set value: ..."), eg.: - * $forms['Server_pmadb'] = array('Servers' => array(1 => array( - * 'pmadb' => 'phpmyadmin'))); - * - * @package phpMyAdmin-setup - * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ - */ - -$forms = array(); -$forms['_config.php'] = array( - 'DefaultLang', - 'ServerDefault'); -$forms['Server'] = array('Servers' => array(1 => array( - 'verbose', - 'host', - 'port', - 'socket', - 'ssl', - 'connect_type', - 'extension', - 'compress', - 'auth_type', - 'auth_http_realm', - 'user', - 'password', - 'nopassword', - 'auth_swekey_config' => './swekey.conf'))); -$forms['Server_login_options'] = array('Servers' => array(1 => array( - 'SignonSession', - 'SignonURL', - 'LogoutURL'))); -$forms['Server_config'] = array('Servers' => array(1 => array( - 'only_db', - 'hide_db', - 'AllowRoot', - 'AllowNoPassword', - 'DisableIS', - 'AllowDeny/order', - 'AllowDeny/rules', - 'ShowDatabasesCommand', - 'CountTables'))); -$forms['Server_pmadb'] = array('Servers' => array(1 => array( - 'pmadb' => 'phpmyadmin', - 'controluser', - 'controlpass', - 'verbose_check', - 'bookmarktable' => 'pma_bookmark', - 'relation' => 'pma_relation', - 'table_info' => 'pma_table_info', - 'table_coords' => 'pma_table_coords', - 'pdf_pages' => 'pma_pdf_pages', - 'column_info' => 'pma_column_info', - 'history' => 'pma_history', - 'tracking' => 'pma_tracking', - 'designer_coords' => 'pma_designer_coords'))); -$forms['Server_tracking'] = array('Servers' => array(1 => array( - 'tracking_version_auto_create', - 'tracking_default_statements', - 'tracking_add_drop_view', - 'tracking_add_drop_table', - 'tracking_add_drop_database', -))); -$forms['Import_export'] = array( - 'UploadDir', - 'SaveDir', - 'RecodingEngine', - 'IconvExtraParams', - 'ZipDump', - 'GZipDump', - 'BZipDump', - 'CompressOnFly'); -$forms['Security'] = array( - 'blowfish_secret', - 'ForceSSL', - 'CheckConfigurationPermissions', - 'TrustedProxies', - 'AllowUserDropDatabase', - 'AllowArbitraryServer', - 'LoginCookieRecall', - 'LoginCookieValidity', - 'LoginCookieStore', - 'LoginCookieDeleteAll'); -$forms['Sql_queries'] = array( - 'ShowSQL', - 'Confirm', - 'QueryHistoryDB', - 'QueryHistoryMax', - 'IgnoreMultiSubmitErrors', - 'VerboseMultiSubmit'); -$forms['Other_core_settings'] = array( - 'MaxDbList', - 'MaxTableList', - 'MaxCharactersInDisplayedSQL', - 'OBGzip', - 'PersistentConnections', - 'ExecTimeLimit', - 'MemoryLimit', - 'SkipLockedTables', - 'UseDbSearch'); -$forms['Left_frame'] = array( - 'LeftFrameLight', - 'LeftDisplayLogo', - 'LeftLogoLink', - 'LeftLogoLinkWindow', - 'LeftDefaultTabTable', - 'LeftPointerEnable'); -$forms['Left_servers'] = array( - 'LeftDisplayServers', - 'DisplayServersList'); -$forms['Left_databases'] = array( - 'DisplayDatabasesList', - 'LeftFrameDBTree', - 'LeftFrameDBSeparator', - 'ShowTooltipAliasDB'); -$forms['Left_tables'] = array( - 'LeftFrameTableSeparator', - 'LeftFrameTableLevel', - 'ShowTooltip', - 'ShowTooltipAliasTB'); -$forms['Startup'] = array( - 'ShowStats', - 'ShowPhpInfo', - 'ShowServerInfo', - 'ShowChgPassword', - 'ShowCreateDb', - 'SuggestDBName'); -$forms['Browse'] = array( - 'NavigationBarIconic', - 'ShowAll', - 'MaxRows', - 'Order', - 'BrowsePointerEnable', - 'BrowseMarkerEnable'); -$forms['Edit'] = array( - 'ProtectBinary', - 'ShowFunctionFields', - 'CharEditing', - 'CharTextareaCols', - 'CharTextareaRows', - 'InsertRows', - 'ForeignKeyDropdownOrder', - 'ForeignKeyMaxLimit'); -$forms['Tabs'] = array( - 'LightTabs', - 'PropertiesIconic', - 'DefaultTabServer', - 'DefaultTabDatabase', - 'DefaultTabTable', - 'QueryWindowDefTab'); -$forms['Sql_box'] = array('SQLQuery' => array( - 'Edit', - 'Explain', - 'ShowAsPHP', - 'Validate', - 'Refresh')); -$forms['Import_defaults'] = array('Import' => array( - 'format', - 'allow_interrupt', - 'skip_queries')); -$forms['Export_defaults'] = array('Export' => array( - 'format', - 'method', - 'compression', - 'asfile', - 'charset', - 'onserver', - 'onserver_overwrite', - 'remember_file_template', - 'file_template_table', - 'file_template_database', - 'file_template_server')); -?> diff --git a/setup/lib/index.lib.php b/setup/lib/index.lib.php index 115cc9fd6..17c9cf675 100644 --- a/setup/lib/index.lib.php +++ b/setup/lib/index.lib.php @@ -1,14 +1,12 @@ , http://yehg.net/lab * Version check taken from the old setup script by Michal ÄŒihaÅ™ * - * @package phpMyAdmin-setup - * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ + * @package phpMyAdmin-setup */ if (!defined('PHPMYADMIN')) { @@ -44,7 +42,6 @@ function messages_begin() function messages_set($type, $id, $title, $message) { $fresh = !isset($_SESSION['messages'][$type][$id]); - $title = PMA_lang($title); $_SESSION['messages'][$type][$id] = array( 'fresh' => $fresh, 'active' => true, @@ -118,15 +115,15 @@ function PMA_version_check() $data = curl_exec($ch); curl_close($ch); } else { - messages_set('error', $message_id, 'VersionCheck', - PMA_lang('VersionCheckWrapperError')); + messages_set('error', $message_id, __('Version check'), + __('Neither URL wrapper nor CURL is available. Version check is not possible.')); return; } } if (empty($data)) { - messages_set('error', $message_id, 'VersionCheck', - PMA_lang('VersionCheckDataError')); + messages_set('error', $message_id, __('Version check'), + __('Reading of version failed. Maybe you\'re offline or the upgrade server does not respond.')); return; } @@ -142,30 +139,30 @@ function PMA_version_check() $version_upstream = version_to_int($version); if ($version_upstream === false) { - messages_set('error', $message_id, 'VersionCheck', - PMA_lang('VersionCheckInvalid')); + messages_set('error', $message_id, __('Version check'), + __('Got invalid version string from server')); return; } $version_local = version_to_int($GLOBALS['PMA_Config']->get('PMA_VERSION')); if ($version_local === false) { - messages_set('error', $message_id, 'VersionCheck', - PMA_lang('VersionCheckUnparsable')); + messages_set('error', $message_id, __('Version check'), + __('Unparsable version string')); return; } if ($version_upstream > $version_local) { $version = htmlspecialchars($version); $date = htmlspecialchars($date); - messages_set('notice', $message_id, 'VersionCheck', - PMA_lang('VersionCheckNewAvailable', $version, $date)); + messages_set('notice', $message_id, __('Version check'), + sprintf(__('A newer version of phpMyAdmin is available and you should consider upgrading. The newest version is %s, released on %s.'), $version, $date)); } else { if ($version_local % 100 == 0) { - messages_set('notice', $message_id, 'VersionCheck', - PMA_lang('VersionCheckNewAvailableSvn', $version, $date)); + messages_set('notice', $message_id, __('Version check'), + PMA_sanitize(sprintf(__('You are using Git version, run [kbd]git pull[/kbd] :-)[br]The latest stable version is %s, released on %s.'), $version, $date))); } else { - messages_set('notice', $message_id, 'VersionCheck', - PMA_lang('VersionCheckNone')); + messages_set('notice', $message_id, __('Version check'), + __('No newer stable version is available')); } } } @@ -200,7 +197,7 @@ function version_to_int($version) $added = 0; break; default: - messages_set('notice', 'version_match', 'VersionCheck', + messages_set('notice', 'version_match', __('Version check'), 'Unknown version part: ' . htmlspecialchars($matches[6])); $added = 0; break; @@ -249,6 +246,19 @@ function perform_config_checks() $blowfish_secret = $cf->get('blowfish_secret'); $blowfish_secret_set = false; $cookie_auth_used = false; + + $strAllowArbitraryServerWarning = __('This [a@?page=form&formset=features#tab_Security]option[/a] should be disabled as it allows attackers to bruteforce login to any MySQL server. If you feel this is necessary, use [a@?page=form&formset=features#tab_Security]trusted proxies list[/a]. However, IP-based protection may not be reliable if your IP belongs to an ISP where thousands of users, including you, are connected to.'); + $strBlowfishSecretMsg = __('You didn\'t have blowfish secret set and have enabled cookie authentication, so a key was automatically generated for you. It is used to encrypt cookies; you don\'t need to remember it.'); + $strBZipDumpWarning = __('[a@?page=form&formset=features#tab_Import_export]Bzip2 compression and decompression[/a] requires functions (%s) which are unavailable on this system.'); + $strDirectoryNotice = __('This value should be double checked to ensure that this directory is neither world accessible nor readable or writable by other users on your server.'); + $strForceSSLNotice = __('This [a@?page=form&formset=features#tab_Security]option[/a] should be enabled if your web server supports it'); + $strGZipDumpWarning = __('[a@?page=form&formset=features#tab_Import_export]GZip compression and decompression[/a] requires functions (%s) which are unavailable on this system.'); + $strLoginCookieValidityWarning = __('[a@?page=form&formset=features#tab_Security]Login cookie validity[/a] should be set to 1800 seconds (30 minutes) at most. Values larger than 1800 may pose a security risk such as impersonation.'); + $strSecurityInfoMsg = __('If you feel this is necessary, use additional protection settings - [a@?page=servers&mode=edit&id=%1$d#tab_Server_config]host authentication[/a] settings and [a@?page=form&formset=features#tab_Security]trusted proxies list[/a]. However, IP-based protection may not be reliable if your IP belongs to an ISP where thousands of users, including you, are connected to.'); + $strServerAuthConfigMsg = __('You set the [kbd]config[/kbd] authentication type and included username and password for auto-login, which is not a desirable option for live hosts. Anyone who knows or guesses your phpMyAdmin URL can directly access your phpMyAdmin panel. Set [a@?page=servers&mode=edit&id=%1$d#tab_Server]authentication type[/a] to [kbd]cookie[/kbd] or [kbd]http[/kbd].'); + $strZipDumpExportWarning = __('[a@?page=form&formset=features#tab_Import_export]Zip compression[/a] requires functions (%s) which are unavailable on this system.'); + $strZipDumpImportWarning = __('[a@?page=form&formset=features#tab_Import_export]Zip decompression[/a] requires functions (%s) which are unavailable on this system.'); + for ($i = 1, $server_cnt = $cf->getServerCount(); $i <= $server_cnt; $i++) { $cookie_auth_server = ($cf->getValue("Servers/$i/auth_type") == 'cookie'); $cookie_auth_used |= $cookie_auth_server; @@ -268,9 +278,9 @@ function perform_config_checks() // should be enabled if possible // if (!$cf->getValue("Servers/$i/ssl")) { - $title = PMA_lang_name('Servers/1/ssl') . " ($server_name)"; + $title = PMA_lang(PMA_lang_name('Servers/1/ssl')) . " ($server_name)"; messages_set('notice', "Servers/$i/ssl", $title, - PMA_lang('ServerSslMsg')); + __('You should use SSL connections if your web server supports it')); } // @@ -278,9 +288,9 @@ function perform_config_checks() // warn about using 'mysql' // if ($cf->getValue("Servers/$i/extension") == 'mysql') { - $title = PMA_lang_name('Servers/1/extension') . " ($server_name)"; + $title = PMA_lang(PMA_lang_name('Servers/1/extension')) . " ($server_name)"; messages_set('notice', "Servers/$i/extension", $title, - PMA_lang('ServerExtensionMsg')); + __('You should use mysqli for performance reasons')); } // @@ -288,12 +298,12 @@ function perform_config_checks() // warn about full user credentials if 'auth_type' is 'config' // if ($cf->getValue("Servers/$i/auth_type") == 'config' - && $cf->getValue("Servers/$i/user") != '' - && $cf->getValue("Servers/$i/password") != '') { - $title = PMA_lang_name('Servers/1/auth_type') . " ($server_name)"; + && $cf->getValue("Servers/$i/user") != '' + && $cf->getValue("Servers/$i/password") != '') { + $title = PMA_lang(PMA_lang_name('Servers/1/auth_type')) . " ($server_name)"; messages_set('warning', "Servers/$i/auth_type", $title, - PMA_lang('ServerAuthConfigMsg', $i) . ' ' . - PMA_lang('ServerSecurityInfoMsg', $i)); + PMA_lang($strServerAuthConfigMsg, $i) . ' ' . + PMA_lang($strSecurityInfoMsg, $i)); } // @@ -302,11 +312,11 @@ function perform_config_checks() // serious security flaw // if ($cf->getValue("Servers/$i/AllowRoot") - && $cf->getValue("Servers/$i/AllowNoPassword")) { - $title = PMA_lang_name('Servers/1/AllowNoPassword') . " ($server_name)"; + && $cf->getValue("Servers/$i/AllowNoPassword")) { + $title = PMA_lang(PMA_lang_name('Servers/1/AllowNoPassword')) . " ($server_name)"; messages_set('warning', "Servers/$i/AllowNoPassword", $title, - PMA_lang('ServerNoPasswordMsg') . ' ' . - PMA_lang('ServerSecurityInfoMsg', $i)); + __('You allow for connecting to the server without a password.') . ' ' . + PMA_lang($strSecurityInfoMsg, $i)); } } @@ -317,25 +327,27 @@ function perform_config_checks() if ($cookie_auth_used) { if ($blowfish_secret_set) { // 'cookie' auth used, blowfish_secret was generated - messages_set('notice', 'blowfish_secret_created', 'blowfish_secret_name', - PMA_lang('BlowfishSecretMsg')); + messages_set('notice', 'blowfish_secret_created', + PMA_lang(PMA_lang_name('blowfish_secret')), + $strBlowfishSecretMsg); } else { $blowfish_warnings = array(); // check length if (strlen($blowfish_secret) < 8) { // too short key - $blowfish_warnings[] = PMA_lang('BlowfishSecretLengthMsg'); + $blowfish_warnings[] = __('Key is too short, it should have at least 8 characters'); } // check used characters $has_digits = (bool) preg_match('/\d/', $blowfish_secret); $has_chars = (bool) preg_match('/\S/', $blowfish_secret); $has_nonword = (bool) preg_match('/\W/', $blowfish_secret); if (!$has_digits || !$has_chars || !$has_nonword) { - $blowfish_warnings[] = PMA_lang('BlowfishSecretCharsMsg'); + $blowfish_warnings[] = PMA_lang(__('Key should contain letters, numbers [em]and[/em] special characters')); } if (!empty($blowfish_warnings)) { messages_set('warning', 'blowfish_warnings' . count($blowfish_warnings), - 'blowfish_secret_name', implode("
    ", $blowfish_warnings)); + PMA_lang(PMA_lang_name('blowfish_secret')), + implode("
    ", $blowfish_warnings)); } } } @@ -345,8 +357,9 @@ function perform_config_checks() // should be enabled if possible // if (!$cf->getValue('ForceSSL')) { - messages_set('notice', 'ForceSSL', 'ForceSSL_name', - PMA_lang('ForceSSLMsg')); + messages_set('notice', 'ForceSSL', + PMA_lang(PMA_lang_name('ForceSSL')), + PMA_lang($strForceSSLNotice)); } // @@ -354,8 +367,9 @@ function perform_config_checks() // should be disabled // if ($cf->getValue('AllowArbitraryServer')) { - messages_set('warning', 'AllowArbitraryServer', 'AllowArbitraryServer_name', - PMA_lang('AllowArbitraryServerMsg')); + messages_set('warning', 'AllowArbitraryServer', + PMA_lang(PMA_lang_name('AllowArbitraryServer')), + PMA_lang($strAllowArbitraryServerWarning)); } // @@ -363,8 +377,9 @@ function perform_config_checks() // should be at most 1800 (30 min) // if ($cf->getValue('LoginCookieValidity') > 1800) { - messages_set('warning', 'LoginCookieValidity', 'LoginCookieValidity_name', - PMA_lang('LoginCookieValidityMsg')); + messages_set('warning', 'LoginCookieValidity', + PMA_lang(PMA_lang_name('LoginCookieValidity')), + PMA_lang($strLoginCookieValidityWarning)); } // @@ -372,8 +387,9 @@ function perform_config_checks() // should not be world-accessible // if ($cf->getValue('SaveDir') != '') { - messages_set('notice', 'SaveDir', 'SaveDir_name', - PMA_lang('DirectoryNotice')); + messages_set('notice', 'SaveDir', + PMA_lang(PMA_lang_name('SaveDir')), + PMA_lang($strDirectoryNotice)); } // @@ -381,8 +397,9 @@ function perform_config_checks() // should not be world-accessible // if ($cf->getValue('TempDir') != '') { - messages_set('notice', 'TempDir', 'TempDir_name', - PMA_lang('DirectoryNotice')); + messages_set('notice', 'TempDir', + PMA_lang(PMA_lang_name('TempDir')), + PMA_lang($strDirectoryNotice)); } // @@ -390,9 +407,10 @@ function perform_config_checks() // requires zlib functions // if ($cf->getValue('GZipDump') - && (@!function_exists('gzopen') || @!function_exists('gzencode'))) { - messages_set('warning', 'GZipDump', 'GZipDump_name', - PMA_lang('GZipDumpWarning', 'gzencode')); + && (@!function_exists('gzopen') || @!function_exists('gzencode'))) { + messages_set('warning', 'GZipDump', + PMA_lang(PMA_lang_name('GZipDump')), + PMA_lang($strGZipDumpWarning, 'gzencode')); } // @@ -400,15 +418,16 @@ function perform_config_checks() // requires bzip2 functions // if ($cf->getValue('BZipDump') - && (!@function_exists('bzopen') || !@function_exists('bzcompress'))) { + && (!@function_exists('bzopen') || !@function_exists('bzcompress'))) { $functions = @function_exists('bzopen') - ? '' : - 'bzopen'; + ? '' : + 'bzopen'; $functions .= @function_exists('bzcompress') - ? '' - : ($functions ? ', ' : '') . 'bzcompress'; - messages_set('warning', 'BZipDump', 'BZipDump_name', - PMA_lang('BZipDumpWarning', $functions)); + ? '' + : ($functions ? ', ' : '') . 'bzcompress'; + messages_set('warning', 'BZipDump', + PMA_lang(PMA_lang_name('BZipDump')), + PMA_lang($strBZipDumpWarning, $functions)); } // @@ -416,8 +435,9 @@ function perform_config_checks() // requires zip_open in import // if ($cf->getValue('ZipDump') && !@function_exists('zip_open')) { - messages_set('warning', 'ZipDump_import', 'ZipDump_name', - PMA_lang('ZipDumpImportWarning', 'zip_open')); + messages_set('warning', 'ZipDump_import', + PMA_lang(PMA_lang_name('ZipDump')), + PMA_lang($strZipDumpImportWarning, 'zip_open')); } // @@ -425,8 +445,9 @@ function perform_config_checks() // requires gzcompress in export // if ($cf->getValue('ZipDump') && !@function_exists('gzcompress')) { - messages_set('warning', 'ZipDump_export', 'ZipDump_name', - PMA_lang('ZipDumpExportWarning', 'gzcompress')); + messages_set('warning', 'ZipDump_export', + PMA_lang(PMA_lang_name('ZipDump')), + PMA_lang($strZipDumpExportWarning, 'gzcompress')); } } -?> +?> \ No newline at end of file diff --git a/setup/lib/messages.inc.php b/setup/lib/messages.inc.php deleted file mode 100644 index 428448549..000000000 --- a/setup/lib/messages.inc.php +++ /dev/null @@ -1,381 +0,0 @@ - diff --git a/setup/scripts.js b/setup/scripts.js index 7298e63ac..f223e0436 100644 --- a/setup/scripts.js +++ b/setup/scripts.js @@ -1,7 +1,5 @@ /** - * functions used in setup script - * - * @version $Id$ + * Functions used in Setup configuration forms */ // show this window in top frame @@ -9,163 +7,6 @@ if (top != self) { window.top.location.href = location; } -// default values for fields -var defaultValues = {}; - -// language strings -var PMA_messages = {}; - -/** - * Returns field type - * - * @param Element field - */ -function getFieldType(field) { - field = $(field); - var tagName = field.attr('tagName'); - if (tagName == 'INPUT') { - return field.attr('type'); - } else if (tagName == 'SELECT') { - return 'select'; - } else if (tagName == 'TEXTAREA') { - return 'text'; - } - return ''; -} - -/** - * Sets field value - * - * value must be of type: - * o undefined (omitted) - restore default value (form default, not PMA default) - * o String - if field_type is 'text' - * o boolean - if field_type is 'checkbox' - * o Array of values - if field_type is 'select' - * - * @param Element field - * @param String field_type see getFieldType - * @param mixed value - */ -function setFieldValue(field, field_type, value) { - field = $(field); - switch (field_type) { - case 'text': - field.attr('value', (value != undefined ? value : field.attr('defaultValue'))); - break; - case 'checkbox': - field.attr('checked', (value != undefined ? value : field.attr('defaultChecked'))); - break; - case 'select': - var options = field.attr('options'); - var i, imax = options.length; - if (value == undefined) { - for (i = 0; i < imax; i++) { - options[i].selected = options[i].defaultSelected; - } - } else { - for (i = 0; i < imax; i++) { - options[i].selected = (value.indexOf(options[i].value) != -1); - } - } - break; - } - markField(field); -} - -/** - * Gets field value - * - * Will return one of: - * o String - if type is 'text' - * o boolean - if type is 'checkbox' - * o Array of values - if type is 'select' - * - * @param Element field - * @param String field_type see getFieldType - * @return mixed - */ -function getFieldValue(field, field_type) { - field = $(field); - switch (field_type) { - case 'text': - return field.attr('value'); - case 'checkbox': - return field.attr('checked'); - case 'select': - var options = field.attr('options'); - var i, imax = options.length, items = []; - for (i = 0; i < imax; i++) { - if (options[i].selected) { - items.push(options[i].value); - } - } - return items; - } -} - -/** - * Returns values for all fields in fieldsets - */ -function getAllValues() { - var elements = $('fieldset input, fieldset select, fieldset textarea'); - var values = {}; - var type, value; - for (var i = 0; i < elements.length; i++) { - type = getFieldType(elements[i]); - value = getFieldValue(elements[i], type); - if (typeof value != 'undefined') { - // we only have single selects, fatten array - if (type == 'select') { - value = value[0]; - } - values[elements[i].name] = value; - } - } - return values; -} - -/** - * Checks whether field has its default value - * - * @param Element field - * @param String type - * @return boolean - */ -function checkFieldDefault(field, type) { - field = $(field); - var field_id = field.attr('id'); - if (typeof defaultValues[field_id] == 'undefined') { - return true; - } - var isDefault = true; - var currentValue = getFieldValue(field, type); - if (type != 'select') { - isDefault = currentValue == defaultValues[field_id]; - } else { - // compare arrays, will work for our representation of select values - if (currentValue.length != defaultValues[field_id].length) { - isDefault = false; - } - else { - for (var i = 0; i < currentValue.length; i++) { - if (currentValue[i] != defaultValues[field_id][i]) { - isDefault = false; - break; - } - } - } - } - return isDefault; -} - -/** - * Returns element's id prefix - * @param Element element - */ -function getIdPrefix(element) { - return $(element).attr('id').replace(/[^-]+$/, ''); -} - // ------------------------------------------------------------------ // Messages // @@ -200,49 +41,13 @@ $(function() { // Form validation and field operations // -// form validator assignments -var validate = {}; - -// form validator list -var validators = { - // regexp: numeric value - _regexp_numeric: new RegExp('^[0-9]*$'), - /** - * Validates positive number - * - * @param boolean isKeyUp - */ - validate_positive_number: function (isKeyUp) { - var result = this.value != '0' && validators._regexp_numeric.test(this.value); - return result ? true : PMA_messages['error_nan_p']; - }, - /** - * Validates non-negative number - * - * @param boolean isKeyUp - */ - validate_non_negative_number: function (isKeyUp) { - var result = validators._regexp_numeric.test(this.value); - return result ? true : PMA_messages['error_nan_nneg']; - }, - /** - * Validates port number - * - * @param boolean isKeyUp - */ - validate_port_number: function(isKeyUp) { - var result = validators._regexp_numeric.test(this.value) && this.value != '0'; - if (!result || this.value > 65536) { - result = PMA_messages['error_incorrect_port']; - } - return result; - }, +$.extend(true, validators, { // field validators _field: { /** * hide_db field * - * @param boolean isKeyUp + * @param {boolean} isKeyUp */ hide_db: function(isKeyUp) { if (!isKeyUp && this.value != '') { @@ -255,7 +60,7 @@ var validators = { /** * TrustedProxies field * - * @param boolean isKeyUp + * @param {boolean} isKeyUp */ TrustedProxies: function(isKeyUp) { if (!isKeyUp && this.value != '') { @@ -271,7 +76,7 @@ var validators = { /** * Validates Server fieldset * - * @param boolean isKeyUp + * @param {boolean} isKeyUp */ Server: function(isKeyUp) { if (!isKeyUp) { @@ -282,7 +87,7 @@ var validators = { /** * Validates Server_login_options fieldset * - * @param boolean isKeyUp + * @param {boolean} isKeyUp */ Server_login_options: function(isKeyUp) { return validators._fieldset.Server.apply(this, [isKeyUp]); @@ -290,7 +95,7 @@ var validators = { /** * Validates Server_pmadb fieldset * - * @param boolean isKeyUp + * @param {boolean} isKeyUp */ Server_pmadb: function(isKeyUp) { if (isKeyUp) { @@ -306,14 +111,14 @@ var validators = { return true; } } -}; +}); /** * Calls server-side validation procedures * - * @param Element parent input field in
    or
    - * @param String id validator id - * @param Object values values hash (element_id: value) + * @param {Element} parent input field in
    or
    + * @param {String} id validator id + * @param {Object} values values hash {element1_id: value, ...} */ function ajaxValidate(parent, id, values) { parent = $(parent); @@ -324,7 +129,7 @@ function ajaxValidate(parent, id, values) { return false; } } - + if (parent.data('ajax') != null) { parent.data('ajax').abort(); } @@ -349,7 +154,7 @@ function ajaxValidate(parent, id, values) { } else if (typeof response['error'] != 'undefined') { error[parent.id] = [response['error']]; } else { - for (key in response) { + for (var key in response) { var value = response[key]; error[key] = jQuery.isArray(value) ? value : [value]; } @@ -364,366 +169,27 @@ function ajaxValidate(parent, id, values) { return true; } -/** - * Registers validator for given field - * - * @param String id field id - * @param String type validator (key in validators object) - * @param boolean onKeyUp whether fire on key up - * @param mixed params validation function parameters - */ -function validateField(id, type, onKeyUp, params) { - if (typeof validators[type] == 'undefined') { - return; - } - if (typeof validate[id] == 'undefined') { - validate[id] = []; - } - validate[id].push([type, params, onKeyUp]); -} - -/** - * Returns valdiation functions associated with form field - * - * @param String field_id form field id - * @param boolean onKeyUpOnly see validateField - * @return Array array of [function, paramseters to be passed to function] - */ -function getFieldValidators(field_id, onKeyUpOnly) { - // look for field bound validator - var name = field_id.match(/[^-]+$/)[0]; - if (typeof validators._field[name] != 'undefined') { - return [[validators._field[name], null]]; - } - - // look for registered validators - var functions = []; - if (typeof validate[field_id] != 'undefined') { - // validate[field_id]: array of [type, params, onKeyUp] - for (var i = 0, imax = validate[field_id].length; i < imax; i++) { - if (onKeyUpOnly && !validate[field_id][i][2]) { - continue; - } - functions.push([validators[validate[field_id][i][0]], validate[field_id][i][1]]); - } - } - - return functions; -} - -/** - * Displays errors for given form fields - * - * WARNING: created DOM elements must be identical with the ones made by - * display_input() in FormDisplay.tpl.php! - * - * @param Object error list (key: field id, value: error array) - */ -function displayErrors(error_list) { - for (field_id in error_list) { - var errors = error_list[field_id]; - var field = $('#'+field_id); - var isFieldset = field.attr('tagName') == 'FIELDSET'; - var errorCnt = isFieldset - ? field.find('dl.errors') - : field.siblings('.inline_errors'); - - // remove empty errors (used to clear error list) - errors = $.grep(errors, function(item) { - return item != ''; - }); - - if (errors.length) { - // if error container doesn't exist, create it - if (errorCnt.length == 0) { - if (isFieldset) { - errorCnt = $('
    '); - field.find('table').before(errorCnt); - } else { - errorCnt = $('
    '); - field.closest('td').append(errorCnt); - } - } - - var html = ''; - for (var i = 0, imax = errors.length; i < imax; i++) { - html += '
    ' + errors[i] + '
    '; - } - errorCnt.html(html); - } else if (errorCnt !== null) { - // remove useless error container - errorCnt.remove(); - } - } -} - -/** - * Validates fieldset and puts errors in 'errors' object - * - * @param Element field - * @param boolean isKeyUp - * @param Object errors - */ -function validate_fieldset(fieldset, isKeyUp, errors) { - fieldset = $(fieldset); - if (fieldset.length && typeof validators._fieldset[fieldset.attr('id')] != 'undefined') { - var fieldset_errors = validators._fieldset[fieldset.attr('id')].apply(fieldset[0], [isKeyUp]); - for (field_id in fieldset_errors) { - if (typeof errors[field_id] == 'undefined') { - errors[field_id] = []; - } - if (typeof fieldset_errors[field_id] == 'string') { - fieldset_errors[field_id] = [fieldset_errors[field_id]]; - } - $.merge(errors[field_id], fieldset_errors[field_id]); - } - } -} - -/** - * Validates form field and puts errors in 'errors' object - * - * @param Element field - * @param boolean isKeyUp - * @param Object errors - */ -function validate_field(field, isKeyUp, errors) { - field = $(field); - var field_id = field.attr('id'); - errors[field_id] = []; - var functions = getFieldValidators(field_id, isKeyUp); - for (var i = 0; i < functions.length; i++) { - var result = functions[i][0].apply(field[0], [isKeyUp, functions[i][1]]); - if (result !== true) { - if (typeof result == 'string') { - result = [result]; - } - $.merge(errors[field_id], result); - } - } -} - -/** - * Validates form field and parent fieldset - * - * @param Element field - * @param boolean isKeyUp - */ -function validate_field_and_fieldset(field, isKeyUp) { - field = $(field); - var errors = {}; - validate_field(field, isKeyUp, errors); - validate_fieldset(field.closest('fieldset'), isKeyUp, errors); - displayErrors(errors); -} - -/** - * Marks field depending on its value (system default or custom) - * - * @param Element field - */ -function markField(field) { - field = $(field); - var type = getFieldType(field); - var isDefault = checkFieldDefault(field, type); - - // checkboxes uses parent for marking - var fieldMarker = (type == 'checkbox') ? field.parent() : field; - setRestoreDefaultBtn(field, !isDefault); - fieldMarker[isDefault ? 'removeClass' : 'addClass']('custom'); -} - -/** - * Enables or disables the "restore default value" button - * - * @param Element field - * @param bool display - */ -function setRestoreDefaultBtn(field, display) { - var el = $(field).closest('td').find('.restore-default'); - el.css('display', (el.css('display') ? '' : 'none')); -} - -$(function() { - // register validators and mark custom values - var elements = $('input[id], select[id], textarea[id]'); - $('input[id], select[id], textarea[id]').each(function(){ - markField(this); - var el = $(this); - el.bind('change', function() { - validate_field_and_fieldset(this, false); - markField(this); - }); - var tagName = el.attr('tagName'); - // text fields can be validated after each change - if (tagName == 'INPUT' && el.attr('type') == 'text') { - el.keyup(function() { - validate_field_and_fieldset(el, true); - markField(el); - }); - } - // disable textarea spellcheck - if (tagName == 'TEXTAREA') { - el.attr('spellcheck', false); - } - }); - - // check whether we've refreshed a page and browser remembered modified - // form values - var check_page_refresh = $('#check_page_refresh'); - if (check_page_refresh.length == 0 || check_page_refresh.val() == '1') { - // run all field validators - var errors = {}; - for (var i = 0; i < elements.length; i++) { - validate_field(elements[i], false, errors); - } - // run all fieldset validators - $('fieldset').each(function(){ - validate_fieldset(this, false, errors); - }); - - displayErrors(errors); - } else if (check_page_refresh) { - check_page_refresh.val('1'); - } -}); - // // END: Form validation and field operations // ------------------------------------------------------------------ // ------------------------------------------------------------------ -// Tabbed forms -// - -/** - * Sets active tab - * - * @param Element tab_link - */ -function setTab(tab_link) { - var tabs_menu = $(tab_link).closest('.tabs'); - - var links = tabs_menu.find('a'); - var contents, link; - for (var i = 0, imax = links.length; i < imax; i++) { - link = $(links[i]); - contents = $(link.attr('href')); - if (links[i] == tab_link) { - link.addClass('active'); - contents.css('display', 'block'); - } else { - link.removeClass('active'); - contents.css('display', 'none'); - } - } - location.hash = 'tab_' + $(tab_link).attr('href').substr(1); -} - -$(function() { - var tabs = $('.tabs'); - var url_tab = location.hash.match(/^#tab_.+/) - ? $('a[href$="' + location.hash.substr(5) + '"]') : null; - if (url_tab) { - url_tab = url_tab[0]; - } - // add tabs events and activate one tab (the first one or indicated by location hash) - for (var i = 0, imax = tabs.length; i < imax; i++) { - var links = $(tabs[i]).find('a'); - var selected_tab = links[0]; - for (var j = 0, jmax = links.length; j < jmax; j++) { - $(links[j]).click(function(e) { - e.preventDefault(); - setTab(this); - }); - if (links[j] == url_tab) { - selected_tab = links[j]; - } - } - setTab(selected_tab); - } - // tab links handling, check each 200ms - // (works with history in FF, further browser support here would be an overkill) - var prev_hash = location.hash; - setInterval(function() { - if (location.hash != prev_hash) { - prev_hash = location.hash; - var url_tab = location.hash.match(/^#tab_.+/) - ? $('a[href$="' + location.hash.substr(5) + '"]') : null; - if (url_tab) { - setTab(url_tab[0]); - } - } - }, 200); -}); - -// -// END: Tabbed forms -// ------------------------------------------------------------------ - -// ------------------------------------------------------------------ -// Form reset buttons +// User preferences allow/disallow UI // $(function() { - $('input[type=button]').click(function(e) { - var fields = $(this).closest('fieldset').find('input, select, textarea'); - for (var i = 0, imax = fields.length; i < imax; i++) { - setFieldValue(fields[i], getFieldType(fields[i])); - } - }); + $('.userprefs-allow').click(function(e) { + if (this != e.target) { + return; + } + var el = $(this).find('input'); + if (el.attr('disabled')) { + return; + } + el.attr('checked', !el.attr('checked')); + }); }); // -// END: Form reset buttons -// ------------------------------------------------------------------ - -// ------------------------------------------------------------------ -// "Restore default" and "set value" buttons -// - -/** - * Restores field's default value - * - * @param String field_id - */ -function restoreField(field_id) { - var field = $('#'+field_id); - if (field.length == 0 || defaultValues[field_id] == undefined) { - return; - } - setFieldValue(field, getFieldType(field), defaultValues[field_id]); -} - -$(function() { - $('.restore-default, .set-value').each(function() { - var link = $(this); - link.css('opacity', 0.25); - if (!link.hasClass('restore-default')) { - // restore-default is handled by markField - link.css('display', ''); - } - link.bind({ - mouseenter: function() {$(this).css('opacity', 1);}, - mouseleave: function() {$(this).css('opacity', 0.25);}, - click: function(e) { - e.preventDefault(); - var href = $(this).attr('href').substr(1); - var field_id; - if ($(this).hasClass('restore-default')) { - field_id = href; - restoreField(field_id); - } else { - field_id = href.match(/^[^=]+/)[0]; - var value = href.match(/=(.+)$/)[1]; - setFieldValue($('#'+field_id), 'text', value); - } - $('#'+field_id).trigger('change'); - } - }); - }); -}); - -// -// END: "Restore default" and "set value" buttons -// ------------------------------------------------------------------ +// END: User preferences allow/disallow UI +// ------------------------------------------------------------------ \ No newline at end of file diff --git a/setup/styles.css b/setup/styles.css index d5a399d77..0cadd100b 100644 --- a/setup/styles.css +++ b/setup/styles.css @@ -195,6 +195,11 @@ ul.tabs li a:hover, ul.tabs li a:active { margin-bottom: -3px; } +.userprefs-comment { + cursor: help; + float: right; +} + /* forms */ fieldset { @@ -253,10 +258,6 @@ fieldset th { vertical-align: top; } -fieldset th small { - display: block; -} - fieldset .doc { margin-left: 1em; } @@ -266,27 +267,50 @@ fieldset td { vertical-align: top; } +fieldset td.userprefs-allow { + padding: 0; + vertical-align: middle; + text-align: center; + width: 3em; +} + +fieldset td.userprefs-allow:hover { + cursor: pointer; + background-color: #EEE; +} + fieldset th small { + display: block; font-weight: normal; font-family: sans-serif; font-size: x-small; color: #666; } -fieldset th, fieldset td { +fieldset th, fieldset td, .form .lastrow { border-top: 1px #555 dotted; } +fieldset .group-header th { + background: #EAEDFF; + border: none; +} + +fieldset .group-header + tr th, fieldset .group-header + tr td, +fieldset p + table tr:first-child td, fieldset p + table tr:first-child th { + border-top: none; +} + +fieldset .group-field th { + padding-left: 1em; +} + fieldset .lastrow, .form .lastrow { background: #F7FBFF; padding: 0.5em; text-align: center; } -.form .lastrow { - border-top: 1px #555 dotted; -} - fieldset .lastrow input, .form .lastrow input { font-weight: bold; } @@ -314,16 +338,20 @@ fieldset.simple .lastrow { span.checkbox { padding: 2px; + display: inline-block; } .custom { /* customized field */ background: #FFC; } -span.checkbox.custom { +.checkbox.custom { padding: 1px; border: 1px #EDEC90 solid; - background: #FFC; +} + +.field-error { + border-color: #C11 !important; } input[type="text"], select, textarea { @@ -335,6 +363,22 @@ input[type="text"]:focus, select:focus, textarea:focus { background: #F7FBFF; } +.field-comment { + + position: relative; +} + +.field-comment-mark { + cursor: help; + padding: 0 0.2em; + font-weight: bold; + font-style: italic; +} + +.field-comment-warning { + color: #A00; +} + .green { /* default form button */ color: #080; } diff --git a/setup/validate.php b/setup/validate.php index 68781bc98..beeba71ce 100644 --- a/setup/validate.php +++ b/setup/validate.php @@ -1,11 +1,9 @@ - * @license http://www.gnu.org/licenses/gpl.html GNU GPL 2.0 - * @version $Id$ + * @package phpMyAdmin-setup */ /** @@ -14,7 +12,7 @@ require './lib/common.inc.php'; $validators = array(); -require './setup/lib/validate.lib.php'; +require './libraries/config/validate.lib.php'; header('Content-type: application/json'); @@ -24,7 +22,7 @@ if (!($values instanceof stdClass)) { die('Wrong data'); } $values = (array)$values; -$result = validate($vids, $values, true); +$result = PMA_config_validate($vids, $values, true); if ($result === false) { $result = 'Wrong data or no validation for ' . $vids; } diff --git a/themes/darkblue_orange/css/theme_right.css.php b/themes/darkblue_orange/css/theme_right.css.php index c15f7965b..73f1c37df 100644 --- a/themes/darkblue_orange/css/theme_right.css.php +++ b/themes/darkblue_orange/css/theme_right.css.php @@ -647,48 +647,88 @@ form.login label { /* specific elements */ /* topmenu */ -ul#topmenu { +ul#topmenu, ul#topmenu2, ul.tabs { font-weight: bold; list-style-type: none; margin: 0; padding: 0; } -ul#topmenu li { +ul#topmenu2 { + margin: 0.25em 0.5em 0; + height: 2em; + clear: both; +} + +ul#topmenu li, ul#topmenu2 li { float: ; margin: 0; padding: 0; vertical-align: middle; } -#topmenu img { +#topmenu img, #topmenu2 img { vertical-align: middle; margin-: 0.1em; } /* default tab styles */ -.tab, .tabcaution, .tabactive { +ul#topmenu a, ul#topmenu span { display: block; margin: 0.2em 0.2em 0 0.2em; padding: 0.2em 0.2em 0 0.2em; white-space: nowrap; } +ul#topmenu ul a { + margin: 0; + padding-bottom: 0.2em; +} + +ul#topmenu .submenu { + position: relative; +} + +ul#topmenu ul { + padding: 0; + margin: 0; + position: absolute; + right: 0; + list-style-type: none; + display: none; + border: 1px #666 solid; +} + +ul#topmenu li:hover ul, ul#topmenu .submenuhover ul { + display: block; +} + +ul#topmenu ul li { + width: 100%; +} + +ul#topmenu2 a { + display: block; + margin: 0.1em; + padding: 0.2em; + white-space: nowrap; +} + /* disabled tabs */ -span.tab { +ul#topmenu span.tab { color: #666666; } /* disabled drop/empty tabs */ -span.tabcaution { +ul#topmenu span.tabcaution { color: #ff6666; } /* enabled drop/empty tabs */ -a.tabcaution { +ul#topmenu a.tabcaution { color: #FF0000; } -a.tabcaution:hover { +ul#topmenu a.tabcaution:hover { color: #FFFFFF; background-color: #FF0000; } @@ -702,52 +742,83 @@ fieldset.caution a:hover { /* active tab */ -a.tabactive { +ul#topmenu a.tabactive, ul#topmenu2 a.tabactive { color: black; } + +ul#topmenu ul { + background: ; +} #topmenu { margin-top: 0.5em; padding: 0.1em 0.3em 0.1em 0.3em; } -ul#topmenu li { +ul#topmenu ul { + -moz-box-shadow: 2px 2px 3px #666; + -webkit-box-shadow: 2px 2px 3px #666; + box-shadow: 2px 2px 3px #666; +} + +ul#topmenu > li { border-bottom: 1pt solid black; } /* default tab styles */ -.tab, .tabcaution, .tabactive { +ul#topmenu a, ul#topmenu span { background-color: ; - border: 1pt solid ; - border-bottom: 0; + border: 0 solid ; + border-width: 1pt 1pt 0 1pt; -moz-border-radius: 0.4em 0.4em 0 0; border-radius: 0.4em 0.4em 0 0; } +ul#topmenu ul a { + border-width: 1pt 0 0 0; + -moz-border-radius: 0; + border-radius: 0; +} + +ul#topmenu ul li:first-child a { + border-width: 0; +} + /* enabled hover/active tabs */ -a.tab:hover, -a.tabcaution:hover, -.tabactive, -.tabactive:hover { +ul#topmenu > li > a:hover, +ul#topmenu > li > .tabactive { margin: 0; padding: 0.2em 0.4em 0.2em 0.4em; text-decoration: none; } -a.tab:hover, -.tabactive { +ul#topmenu ul a:hover, +ul#topmenu ul .tabactive { + text-decoration: none; +} + +ul#topmenu a.tab:hover, +ul#topmenu .tabactive { background-color: ; } +ul#topmenu2 a.tab:hover, +ul#topmenu2 a.tabactive { + background-color: ; + -moz-border-radius: 0.3em; + border-radius: 0.3em; + text-decoration: none; +} + /* to be able to cancel the bottom border, use
  • */ -ul#topmenu li.active { +ul#topmenu > li.active { border-bottom: 1pt solid ; } /* disabled drop/empty tabs */ -span.tab, +ul#topmenu span.tab, a.warning, -span.tabcaution { +ul#topmenu span.tabcaution { cursor: url(getImgPath(); ?>error.ico), default; } @@ -1146,6 +1217,10 @@ li#li_switch_dbstats { li#li_flush_privileges { list-style-image: url(getImgPath(); ?>s_reload.png); } + +li#li_user_preferences { + list-style-image: url(getImgPath(); ?>b_tblops.png); +} /* END iconic view for ul items */ @@ -1270,6 +1345,12 @@ code.sql, div.sqlvalidate { margin-top: 0; } +.group-cnt { + padding: 0 0 0 0.5em; + display: inline-block; + width: 98%; +} + /* for elements that should be revealed only via js */ .hide { display: none; @@ -1496,4 +1577,211 @@ a.close_enum_editor { td.more_opts { display: none; white-space: nowrap; -} \ No newline at end of file +} + +/* config forms */ +.config-form ul.tabs { + margin: 1.1em 0.2em 0; + padding: 0 0 0.3em 0; + list-style: none; + font-weight: bold; +} + +.config-form ul.tabs li { + float: ; +} + +.config-form ul.tabs li a { + display: block; + margin: 0.1em 0.2em 0; + padding: 0.1em 0.4em; + white-space: nowrap; + text-decoration: none; + border: 1px solid ; + border-bottom: none; +} + +.config-form ul.tabs li a:hover, +.config-form ul.tabs li a:active, +.config-form ul.tabs li a.active { + margin: 0; + padding: 0.1em 0.6em 0.2em; +} + +.config-form ul.tabs li a.active { + background-color: ; +} + +.config-form fieldset { + margin-top: 0; + padding: 0; + clear: both; + /*border-color: ;*/ +} + +.config-form legend { + display: none; +} + +.config-form fieldset p { + margin: 0; + padding: 0.5em; +} + +.config-form fieldset .errors { /* form error list */ + margin: 0 -2px 1em -2px; + padding: 0.5em 1.5em; + background: #FBEAD9; + border: 0 #C83838 solid; + border-width: 1px 0; + list-style: none; + font-family: sans-serif; + font-size: small; +} + +.config-form fieldset .inline_errors { /* field error list */ + margin: 0.3em 0.3em 0.3em 0; + padding: 0; + list-style: none; + color: #9A0000; + font-size: small; +} + +.config-form fieldset th { + padding: 0.3em 0.3em 0.3em 0.5em; + text-align: left; + vertical-align: top; + width: 40%; + background: transparent; +} + +.config-form fieldset .doc, .config-form fieldset .disabled-notice { + margin-left: 1em; +} + +.config-form fieldset .disabled-notice { + font-size: 80%; + text-transform: uppercase; + color: #E00; + cursor: help; +} + +.config-form fieldset td { + padding-top: 0.3em; + padding-bottom: 0.3em; + vertical-align: top; +} + +.config-form fieldset th small { + display: block; + font-weight: normal; + font-family: sans-serif; + font-size: x-small; + color: #444; +} + +.config-form fieldset th, .config-form fieldset td { + border-top: 1px solid; +} + +fieldset .group-header th { + background: ; +} + +fieldset .group-header + tr th { + padding-top: 0.6em; +} + +fieldset .group-field th { + padding-left: 1.5em; +} + +fieldset .disabled-field th, +fieldset .disabled-field th small, +fieldset .disabled-field td { + color: #777; + background-color: #eee; +} + +.config-form .lastrow { + border-top: 1px #000 solid; +} + +.config-form .lastrow { + background: ;; + padding: 0.5em; + text-align: center; +} + +.config-form .lastrow input { + font-weight: bold; +} + +/* form elements */ + +.config-form span.checkbox { + padding: 2px; + display: inline-block; +} + +.config-form .custom { /* customized field */ + background: #FFC; +} + +.config-form span.checkbox.custom { + padding: 1px; + border: 1px #EDEC90 solid; + background: #FFC; +} + +.config-form .field-error { + border-color: #A11 !important; +} + +.config-form input[type="text"], +.config-form select, +.config-form textarea { + border: 1px #A7A6AA solid; + height: auto; +} + +.config-form input[type="text"]:focus, +.config-form select:focus, +.config-form textarea:focus { + border: 1px #6676FF solid; + background: #F7FBFF; +} + +.config-form .field-comment-mark { + font-family: serif; + color: #00A; + cursor: help; + padding: 0 0.2em; + font-weight: bold; + font-style: italic; +} + +.config-form .field-comment-warning { + color: #A00; +} + +/* error list */ +.config-form dd { + margin-left: 0.5em; +} + +.config-form dd:before { + content: "\25B8 "; +} + +.click-hide-message { + cursor: pointer; +} + +.prefsmanage_opts { + margin-: 2em; +} + +#prefs_autoload { + margin-bottom: 0.5em; +} diff --git a/themes/darkblue_orange/img/b_more.png b/themes/darkblue_orange/img/b_more.png new file mode 100644 index 000000000..161e0ff32 Binary files /dev/null and b/themes/darkblue_orange/img/b_more.png differ diff --git a/themes/original/css/theme_right.css.php b/themes/original/css/theme_right.css.php index 13a115274..1d6da6144 100644 --- a/themes/original/css/theme_right.css.php +++ b/themes/original/css/theme_right.css.php @@ -16,7 +16,7 @@ if (!defined('PMA_MINIMUM_COMMON')) { /* general tags */ html { font-size: get('fontsize') ? $GLOBALS['PMA_Config']->get('fontsize') : ( - isset($_COOKIE['pma_fontsize']) ? $_COOKIE['pma_fontsize'] : '84%'));?>; + isset($_COOKIE['pma_fontsize']) ? $_COOKIE['pma_fontsize'] : '82%'));?>; } input, select, textarea { @@ -59,7 +59,7 @@ a:visited, a:active { text-decoration: none; color: #0000FF; - cursor: pointer; + cursor: pointer; } a:hover { @@ -523,7 +523,6 @@ fieldset.confirmation legend { } - /* end messageboxes */ @@ -625,48 +624,88 @@ form.login label { /* specific elements */ /* topmenu */ -ul#topmenu { +ul#topmenu, ul#topmenu2, ul.tabs { font-weight: bold; list-style-type: none; margin: 0; padding: 0; } -ul#topmenu li { +ul#topmenu2 { + margin: 0.25em 0.5em 0; + height: 2em; + clear: both; +} + +ul#topmenu li, ul#topmenu2 li { float: ; margin: 0; padding: 0; vertical-align: middle; } -#topmenu img { +#topmenu img, #topmenu2 img { vertical-align: middle; margin-: 0.1em; } /* default tab styles */ -.tab, .tabcaution, .tabactive { +ul#topmenu a, ul#topmenu span { display: block; margin: 0.2em 0.2em 0 0.2em; padding: 0.2em 0.2em 0 0.2em; white-space: nowrap; } +ul#topmenu ul a { + margin: 0; + padding-bottom: 0.2em; +} + +ul#topmenu .submenu { + position: relative; +} + +ul#topmenu ul { + margin: 0; + padding: 0; + position: absolute; + right: 0; + list-style-type: none; + display: none; + border: 1px #666 solid; +} + +ul#topmenu li:hover ul, ul#topmenu .submenuhover ul { + display: block; +} + +ul#topmenu ul li { + width: 100%; +} + +ul#topmenu2 a { + display: block; + margin: 0.1em; + padding: 0.2em; + white-space: nowrap; +} + /* disabled tabs */ -span.tab { +ul#topmenu span.tab { color: #666666; } /* disabled drop/empty tabs */ -span.tabcaution { +ul#topmenu span.tabcaution { color: #ff6666; } /* enabled drop/empty tabs */ -a.tabcaution { +ul#topmenu a.tabcaution { color: #FF0000; } -a.tabcaution:hover { +ul#topmenu a.tabcaution:hover { color: #FFFFFF; background-color: #FF0000; } @@ -680,52 +719,83 @@ fieldset.caution a:hover { /* active tab */ -a.tabactive { +ul#topmenu a.tabactive, ul#topmenu2 a.tabactive { color: black; } + +ul#topmenu ul { + background: ; +} #topmenu { margin-top: 0.5em; padding: 0.1em 0.3em 0.1em 0.3em; } -ul#topmenu li { +ul#topmenu ul { + -moz-box-shadow: 2px 2px 3px #666; + -webkit-box-shadow: 2px 2px 3px #666; + box-shadow: 2px 2px 3px #666; +} + +ul#topmenu > li { border-bottom: 1pt solid black; } /* default tab styles */ -.tab, .tabcaution, .tabactive { +ul#topmenu a, ul#topmenu span { background-color: ; - border: 1pt solid ; - border-bottom: 0; + border: 0 solid ; + border-width: 1pt 1pt 0 1pt; -moz-border-radius: 0.4em 0.4em 0 0; border-radius: 0.4em 0.4em 0 0; } +ul#topmenu ul a { + border-width: 1pt 0 0 0; + -moz-border-radius: 0; + border-radius: 0; +} + +ul#topmenu ul li:first-child a { + border-width: 0; +} + /* enabled hover/active tabs */ -a.tab:hover, -a.tabcaution:hover, -.tabactive, -.tabactive:hover { +ul#topmenu > li > a:hover, +ul#topmenu > li > .tabactive { margin: 0; padding: 0.2em 0.4em 0.2em 0.4em; text-decoration: none; } -a.tab:hover, -.tabactive { +ul#topmenu ul a:hover, +ul#topmenu ul .tabactive { + text-decoration: none; +} + +ul#topmenu a.tab:hover, +ul#topmenu .tabactive { background-color: ; } +ul#topmenu2 a.tab:hover, +ul#topmenu2 a.tabactive { + background-color: ; + -moz-border-radius: 0.3em; + border-radius: 0.3em; + text-decoration: none; +} + /* to be able to cancel the bottom border, use
  • */ -ul#topmenu li.active { +ul#topmenu > li.active { border-bottom: 1pt solid ; } /* disabled drop/empty tabs */ -span.tab, +ul#topmenu span.tab, a.warning, -span.tabcaution { +ul#topmenu span.tabcaution { cursor: url(getImgPath(); ?>error.ico), default; } @@ -1097,6 +1167,10 @@ li#li_switch_dbstats { li#li_flush_privileges { list-style-image: url(getImgPath(); ?>s_reload.png); } + +li#li_user_preferences { + list-style-image: url(getImgPath(); ?>b_tblops.png); +} /* END iconic view for ul items */ @@ -1220,11 +1294,17 @@ code.sql, div.sqlvalidate { } .group h2 { - background: ; + background: ; padding: 0.1em 0.3em; margin-top: 0; } +.group-cnt { + padding: 0 0 0 0.5em; + display: inline-block; + width: 98%; +} + /* for elements that should be revealed only via js */ .hide { display: none; @@ -1423,8 +1503,6 @@ a.close_enum_editor { width: 100%; } -#enum_editor input, #enum_editor_no_js input { - float: bottom; } #enum_editor_output { @@ -1452,4 +1530,211 @@ iframe.IE_hack { display: none; border: 0; filter: alpha(opacity=0); -} \ No newline at end of file +} + +/* config forms */ +.config-form ul.tabs { + margin: 1.1em 0.2em 0; + padding: 0 0 0.3em 0; + list-style: none; + font-weight: bold; +} + +.config-form ul.tabs li { + float: ; +} + +.config-form ul.tabs li a { + display: block; + margin: 0.1em 0.2em 0; + padding: 0.1em 0.4em; + white-space: nowrap; + text-decoration: none; + border: 1px solid ; + border-bottom: none; +} + +.config-form ul.tabs li a:hover, +.config-form ul.tabs li a:active, +.config-form ul.tabs li a.active { + margin: 0; + padding: 0.1em 0.6em 0.2em; +} + +.config-form ul.tabs li a.active { + background-color: ; +} + +.config-form fieldset { + margin-top: 0; + padding: 0; + clear: both; + /*border-color: ;*/ +} + +.config-form legend { + display: none; +} + +.config-form fieldset p { + margin: 0; + padding: 0.5em; +} + +.config-form fieldset .errors { /* form error list */ + margin: 0 -2px 1em -2px; + padding: 0.5em 1.5em; + background: #FBEAD9; + border: 0 #C83838 solid; + border-width: 1px 0; + list-style: none; + font-family: sans-serif; + font-size: small; +} + +.config-form fieldset .inline_errors { /* field error list */ + margin: 0.3em 0.3em 0.3em 0; + padding: 0; + list-style: none; + color: #9A0000; + font-size: small; +} + +.config-form fieldset th { + padding: 0.3em 0.3em 0.3em 0.5em; + text-align: left; + vertical-align: top; + width: 40%; + background: transparent; +} + +.config-form fieldset .doc, .config-form fieldset .disabled-notice { + margin-left: 1em; +} + +.config-form fieldset .disabled-notice { + font-size: 80%; + text-transform: uppercase; + color: #E00; + cursor: help; +} + +.config-form fieldset td { + padding-top: 0.3em; + padding-bottom: 0.3em; + vertical-align: top; +} + +.config-form fieldset th small { + display: block; + font-weight: normal; + font-family: sans-serif; + font-size: x-small; + color: #444; +} + +.config-form fieldset th, .config-form fieldset td { + border-top: 1px solid; +} + +fieldset .group-header th { + background: ; +} + +fieldset .group-header + tr th { + padding-top: 0.6em; +} + +fieldset .group-field th { + padding-left: 1.5em; +} + +fieldset .disabled-field th, +fieldset .disabled-field th small, +fieldset .disabled-field td { + color: #666; + background-color: #ddd; +} + +.config-form .lastrow { + border-top: 1px #000 solid; +} + +.config-form .lastrow { + background: ;; + padding: 0.5em; + text-align: center; +} + +.config-form .lastrow input { + font-weight: bold; +} + +/* form elements */ + +.config-form span.checkbox { + padding: 2px; + display: inline-block; +} + +.config-form .custom { /* customized field */ + background: #FFC; +} + +.config-form span.checkbox.custom { + padding: 1px; + border: 1px #EDEC90 solid; + background: #FFC; +} + +.config-form .field-error { + border-color: #A11 !important; +} + +.config-form input[type="text"], +.config-form select, +.config-form textarea { + border: 1px #A7A6AA solid; + height: auto; +} + +.config-form input[type="text"]:focus, +.config-form select:focus, +.config-form textarea:focus { + border: 1px #6676FF solid; + background: #F7FBFF; +} + +.config-form .field-comment-mark { + font-family: serif; + color: #007; + cursor: help; + padding: 0 0.2em; + font-weight: bold; + font-style: italic; +} + +.config-form .field-comment-warning { + color: #A00; +} + +/* error list */ +.config-form dd { + margin-left: 0.5em; +} + +.config-form dd:before { + content: "\25B8 "; +} + +.click-hide-message { + cursor: pointer; +} + +.prefsmanage_opts { + margin-: 2em; +} + +#prefs_autoload { + margin-bottom: 0.5em; +} diff --git a/themes/original/img/b_more.png b/themes/original/img/b_more.png new file mode 100644 index 000000000..4a12e8cda Binary files /dev/null and b/themes/original/img/b_more.png differ