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:
+
+
+ set up pmadb and the linked-tables infrastructure
+ put the table name in $cfg['Servers'][$i]['userconfig']
+
+
$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
- 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($(''))
+ .mouseenter(function() {
+ if ($(this).find('ul .tabactive').length == 0) {
+ $(this).addClass('submenuhover').find('> a').addClass('tabactive');
+ }
+ })
+ .mouseleave(function() {
+ if ($(this).find('ul .tabactive').length == 0) {
+ $(this).removeClass('submenuhover').find('> a').removeClass('tabactive');
+ }
+ })
+ .hide();
+ topmenu.append(submenu);
+
+ // populate submenu and register resize event
+ $(window).resize(menuResize);
+ menuResize();
+});
diff --git a/js/messages.php b/js/messages.php
index 4999002c7..94e1e67e4 100644
--- a/js/messages.php
+++ b/js/messages.php
@@ -66,6 +66,9 @@ $js_messages['strChangeDisplay'] = __('Choose column to display');
$js_messages['strGeneratePassword'] = __('Generate password');
$js_messages['strGenerate'] = __('Generate');
+/* navigation tabs */
+$js_messages['strMore'] = __('More');
+
echo "var PMA_messages = new Array();\n";
foreach ($js_messages as $name => $js_message) {
PMA_printJsValue("PMA_messages['" . $name . "']", $js_message);
diff --git a/libraries/Config.class.php b/libraries/Config.class.php
index 7389b9f76..16fe5a1b3 100644
--- a/libraries/Config.class.php
+++ b/libraries/Config.class.php
@@ -23,6 +23,11 @@ class PMA_Config
*/
var $default_source = './libraries/config.default.php';
+ /**
+ * @var array default configuration settings
+ */
+ var $default = array();
+
/**
* @var array configuration settings
*/
@@ -320,6 +325,7 @@ class PMA_Config
$this->default_server = $cfg['Servers'][1];
unset($cfg['Servers']);
+ $this->default = $cfg;
$this->settings = PMA_array_merge_recursive($this->settings, $cfg);
$this->error_config_default_file = false;
@@ -408,6 +414,176 @@ class PMA_Config
return true;
}
+ /**
+ * Loads user preferences and merges them with current config
+ * must be called after control connection has been estabilished
+ *
+ * @uses $GLOBALS['cfg']
+ * @uses $GLOBALS['collation_connection']
+ * @uses $GLOBALS['lang']
+ * @uses $_SESSION['cache']['server_$server']['config_mtime']
+ * @uses $_SESSION['cache']['server_$server']['userprefs']
+ * @uses $_SESSION['cache']['server_$server']['userprefs_mtime']
+ * @uses $_SESSION['PMA_Theme_Manager']
+ * @uses PMA_apply_userprefs()
+ * @uses PMA_array_merge_recursive()
+ * @uses PMA_load_userprefs()
+ * @return boolean
+ */
+ function loadUserPreferences()
+ {
+ // index.php should load these settings, so that phpmyadmin.css.php
+ // will have everything avaiable in session cache
+ $server = isset($GLOBALS['server'])
+ ? $GLOBALS['server']
+ : (!empty($GLOBALS['cfg']['ServerDefault']) ? $GLOBALS['cfg']['ServerDefault'] : 0);
+ $cache_key = 'server_' . $server;
+ if ($server > 0 && !defined('PMA_MINIMUM_COMMON')) {
+ $config_mtime = max($this->default_source_mtime, $this->source_mtime);
+ // cache user preferences, use database only when needed
+ if (!isset($_SESSION['cache'][$cache_key]['userprefs'])
+ || $_SESSION['cache'][$cache_key]['config_mtime'] < $config_mtime) {
+ // load required libraries
+ require_once './libraries/user_preferences.lib.php';
+ $prefs = PMA_load_userprefs();
+ $_SESSION['cache'][$cache_key]['userprefs'] = PMA_apply_userprefs($prefs['config_data']);
+ $_SESSION['cache'][$cache_key]['userprefs_mtime'] = $prefs['mtime'];
+ $_SESSION['cache'][$cache_key]['userprefs_type'] = $prefs['type'];
+ $_SESSION['cache'][$cache_key]['config_mtime'] = $config_mtime;
+ }
+ } else if ($server == 0 || !isset($_SESSION['cache'][$cache_key]['userprefs'])) {
+ $this->set('user_preferences', false);
+ return;
+ }
+ $config_data = $_SESSION['cache'][$cache_key]['userprefs'];
+ // type is 'db' or 'session'
+ $this->set('user_preferences', $_SESSION['cache'][$cache_key]['userprefs_type']);
+ $this->set('user_preferences_mtime', $_SESSION['cache'][$cache_key]['userprefs_mtime']);
+
+ // backup some settings
+ $org_fontsize = $this->settings['fontsize'];
+ // load config array
+ $this->settings = PMA_array_merge_recursive($this->settings, $config_data);
+ $GLOBALS['cfg'] = PMA_array_merge_recursive($GLOBALS['cfg'], $config_data);
+ if (defined('PMA_MINIMUM_COMMON')) {
+ return;
+ }
+
+ // settings below start really working on next page load, but
+ // changes are made only in index.php so everything is set when
+ // in frames
+
+ // save theme
+ $tmanager = $_SESSION['PMA_Theme_Manager'];
+ if ($tmanager->getThemeCookie() || isset($_REQUEST['set_theme'])) {
+ if ((!isset($config_data['ThemeDefault']) && $tmanager->theme->getId() != 'original')
+ || isset($config_data['ThemeDefault']) && $config_data['ThemeDefault'] != $tmanager->theme->getId()) {
+ // new theme was set in common.inc.php
+ $this->setUserValue(null, 'ThemeDefault', $tmanager->theme->getId(), 'original');
+ }
+ } else {
+ // no cookie - read default from settings
+ if ($this->settings['ThemeDefault'] != $tmanager->theme->getId()
+ && $tmanager->checkTheme($this->settings['ThemeDefault'])) {
+ $tmanager->setActiveTheme($this->settings['ThemeDefault']);
+ $tmanager->setThemeCookie();
+ }
+ }
+
+ // save font size
+ if ((!isset($config_data['fontsize']) && $org_fontsize != '82%')
+ || isset($config_data['fontsize']) && $org_fontsize != $config_data['fontsize']) {
+ $this->setUserValue(null, 'fontsize', $org_fontsize, '82%');
+ }
+
+ // save language
+ if (isset($_COOKIE['pma_lang']) || isset($_POST['lang'])) {
+ if ((!isset($config_data['lang']) && $GLOBALS['lang'] != 'en')
+ || isset($config_data['lang']) && $GLOBALS['lang'] != $config_data['lang']) {
+ $this->setUserValue(null, 'lang', $GLOBALS['lang'], 'en');
+ }
+ } else {
+ // read language from settings
+ if (isset($config_data['lang']) && PMA_langSet($config_data['lang'])) {
+ $this->setCookie('pma_lang', $GLOBALS['lang']);
+ }
+ }
+
+ // save connection collation
+ if (isset($_COOKIE['pma_collation_connection']) || isset($_POST['collation_connection'])) {
+ if ((!isset($config_data['collation_connection']) && $GLOBALS['collation_connection'] != 'utf8_general_ci')
+ || isset($config_data['collation_connection']) && $GLOBALS['collation_connection'] != $config_data['collation_connection']) {
+ $this->setUserValue(null, 'collation_connection', $GLOBALS['collation_connection'], 'utf8_general_ci');
+ }
+ } else {
+ // read collation from settings
+ if (isset($config_data['collation_connection'])) {
+ $GLOBALS['collation_connection'] = $config_data['collation_connection'];
+ $this->setCookie('pma_collation_connection', $GLOBALS['collation_connection']);
+ }
+ }
+ }
+
+ /**
+ * Sets config value which is stored in user preferences (if available) or in a cookie.
+ *
+ * If user preferences are not yet initialized, option is applied to global config and
+ * added to a update queue, which is processed by {@link loadUserPreferences()}
+ *
+ * @uses $GLOBALS['cfg']
+ * @uses PMA_array_read()
+ * @uses PMA_array_write()
+ * @uses PMA_persist_option()
+ * @param string $cookie_name can be null
+ * @param string $cfg_path
+ * @param mixed $new_cfg_value
+ * @param mixed $default_value
+ */
+ function setUserValue($cookie_name, $cfg_path, $new_cfg_value, $default_value = null)
+ {
+ // use permanent user preferences if possible
+ $prefs_type = $this->get('user_preferences');
+ if ($prefs_type) {
+ require_once './libraries/user_preferences.lib.php';
+ if ($default_value === null) {
+ $default_value = PMA_array_read($cfg_path, $this->default);
+ }
+ PMA_persist_option($cfg_path, $new_cfg_value, $default_value);
+ }
+ if ($prefs_type != 'db' && $cookie_name) {
+ // fall back to cookies
+ if ($default_value === null) {
+ $default_value = PMA_array_read($cfg_path, $this->settings);
+ }
+ $this->setCookie($cookie_name, $new_cfg_value, $default_value);
+ }
+ PMA_array_write($cfg_path, $GLOBALS['cfg'], $new_cfg_value);
+ PMA_array_write($cfg_path, $this->settings, $new_cfg_value);
+ }
+
+ /**
+ * Reads value stored by {@link setUserValue()}
+ *
+ * @param string $cookie_name
+ * @param mixed $cfg_value
+ * @return mixed
+ */
+ function getUserValue($cookie_name, $cfg_value)
+ {
+ $cookie_exists = isset($_COOKIE) && !empty($_COOKIE[$cookie_name]);
+ $prefs_type = $this->get('user_preferences');
+ if ($prefs_type == 'db') {
+ // permanent user preferences value exists, remove cookie
+ if ($cookie_exists) {
+ $this->removeCookie($cookie_name);
+ }
+ } else if ($cookie_exists) {
+ return $_COOKIE[$cookie_name];
+ }
+ // return value from $cfg array
+ return $cfg_value;
+ }
+
/**
* set source
* @param string $source
@@ -537,6 +713,7 @@ class PMA_Config
$fontsize +
$this->source_mtime +
$this->default_source_mtime +
+ $this->get('user_preferences_mtime') +
$_SESSION['PMA_Theme']->mtime_info +
$_SESSION['PMA_Theme']->filesize_info)
. (isset($_SESSION['tmp_user_values']['custom_color']) ? substr($_SESSION['tmp_user_values']['custom_color'],1,6) : '');
@@ -726,10 +903,10 @@ class PMA_Config
{
$new_fontsize = '';
- if (isset($_GET['fontsize'])) {
- $new_fontsize = $_GET['fontsize'];
- } elseif (isset($_POST['fontsize'])) {
- $new_fontsize = $_POST['fontsize'];
+ if (isset($_GET['set_fontsize'])) {
+ $new_fontsize = $_GET['set_fontsize'];
+ } elseif (isset($_POST['set_fontsize'])) {
+ $new_fontsize = $_POST['set_fontsize'];
} elseif (isset($_COOKIE['pma_fontsize'])) {
$new_fontsize = $_COOKIE['pma_fontsize'];
}
@@ -1011,7 +1188,7 @@ class PMA_Config
$options = PMA_Config::_getFontsizeOptions($current_size);
$return = '' . __('Font size') . ': ' . "\n";
- $return .= '' . "\n";
+ $return .= '' . "\n";
foreach ($options as $option) {
$return .= 'loadUserPreferences();
+
// remove sensitive values from session
$GLOBALS['PMA_Config']->set('blowfish_secret', '');
$GLOBALS['PMA_Config']->set('Servers', '');
diff --git a/libraries/common.lib.php b/libraries/common.lib.php
index 23b3eee31..741d8cbc5 100644
--- a/libraries/common.lib.php
+++ b/libraries/common.lib.php
@@ -1576,7 +1576,7 @@ function PMA_generate_html_tab($tab, $url_params = array())
$defaults = array(
'text' => '',
'class' => '',
- 'active' => false,
+ 'active' => null,
'link' => '',
'sep' => '?',
'attr' => '',
@@ -1595,7 +1595,7 @@ function PMA_generate_html_tab($tab, $url_params = array())
} elseif (! empty($tab['active'])
|| PMA_isValid($GLOBALS['active_page'], 'identical', $tab['link'])) {
$tab['class'] = 'active';
- } elseif (empty($GLOBALS['active_page'])
+ } elseif (is_null($tab['active']) && empty($GLOBALS['active_page'])
&& basename($GLOBALS['PMA_PHP_SELF']) == $tab['link']
&& empty($tab['warning'])) {
$tab['class'] = 'active';
diff --git a/libraries/config.default.php b/libraries/config.default.php
index 691a0f730..9c5ea621d 100644
--- a/libraries/config.default.php
+++ b/libraries/config.default.php
@@ -340,6 +340,15 @@ $cfg['Servers'][$i]['designer_coords'] = '';
*/
$cfg['Servers'][$i]['tracking'] = '';
+/**
+ * table to store user preferences
+ * - leave blank to disable server storage
+ * SUGGESTED: 'pma_userconfig'
+ *
+ * @global string $cfg['Servers'][$i]['userconfig']
+ */
+$cfg['Servers'][$i]['userconfig'] = '';
+
/**
* set to false if you know that your pma_* tables are up to date.
* This prevents compatibility checks and thereby increases performance.
@@ -1063,6 +1072,13 @@ $cfg['Export'] = array();
*/
$cfg['Export']['format'] = 'sql';
+/**
+ * quick/custom/custom-no-form
+ *
+ * @global string $cfg['Export']['format']
+ */
+$cfg['Export']['method'] = 'quick';
+
/**
* none/zip/gzip/bzip2
*
@@ -1147,6 +1163,13 @@ $cfg['Export']['file_template_server'] = '@SERVER@';
*/
$cfg['Export']['codegen_structure_or_data'] = 'data';
+/**
+ *
+ *
+ * @global $cfg['Export']['codegen_format']
+ */
+$cfg['Export']['codegen_format'] = 0;
+
/**
*
*
@@ -1336,6 +1359,13 @@ $cfg['Export']['csv_escaped'] = '\\';
*/
$cfg['Export']['csv_terminated'] = 'AUTO';
+/**
+ *
+ *
+ * @global string $cfg['Export']['csv_removeCRLF']
+ */
+$cfg['Export']['csv_removeCRLF'] = false;
+
/**
*
*
@@ -1357,6 +1387,13 @@ $cfg['Export']['excel_null'] = 'NULL';
*/
$cfg['Export']['excel_edition'] = 'win';
+/**
+ *
+ *
+ * @global string $cfg['Export']['excel_removeCRLF']
+ */
+$cfg['Export']['excel_removeCRLF'] = false;
+
/**
*
*
@@ -1616,7 +1653,7 @@ $cfg['Export']['sql_hex_for_blob'] = true;
*
* @global string $cfg['Export']['sql_type']
*/
-$cfg['Export']['sql_type'] = 'insert';
+$cfg['Export']['sql_type'] = 'INSERT';
/**
*
@@ -1673,6 +1710,7 @@ $cfg['Export']['pdf_report_title'] = '';
*@global string $cfg['Export']['xml_structure_or_data']
*/
$cfg['Export']['xml_structure_or_data'] = 'data';
+
/**
* Export schema for each structure
*
@@ -1728,6 +1766,7 @@ $cfg['Export']['xml_export_contents'] = true;
* @global string $cfg['Export']['yaml_structure_or_data']
*/
$cfg['Export']['yaml_structure_or_data'] = 'data';
+
/*******************************************************************************
* Import defaults
*/
@@ -1759,7 +1798,7 @@ $cfg['Import']['allow_interrupt'] = true;
*
* @global integer $cfg['Import']['skip_queries']
*/
-$cfg['Import']['skip_queries'] = '0';
+$cfg['Import']['skip_queries'] = 0;
/**
*
@@ -1782,6 +1821,13 @@ $cfg['Import']['sql_no_auto_value_on_zero'] = true;
*/
$cfg['Import']['csv_replace'] = false;
+/**
+ *
+ *
+ * @global boolean $cfg['Import']['csv_ignore']
+ */
+$cfg['Import']['csv_ignore'] = false;
+
/**
*
*
@@ -1831,6 +1877,13 @@ $cfg['Import']['csv_col_names'] = false;
*/
$cfg['Import']['ldi_replace'] = false;
+/**
+ *
+ *
+ * @global boolean $cfg['Import']['ldi_ignore']
+ */
+$cfg['Import']['ldi_ignore'] = false;
+
/**
*
*
@@ -1915,6 +1968,13 @@ $cfg['Import']['xls_col_names'] = false;
*/
$cfg['Import']['xls_empty_rows'] = true;
+/**
+ *
+ *
+ * @global string $cfg['Import']['xlsx_col_names']
+ */
+$cfg['Import']['xlsx_col_names'] = false;
+
/**
* Link to the official MySQL documentation.
* Be sure to include no trailing slash on the path.
@@ -2171,7 +2231,7 @@ $cfg['DefaultDisplay'] = 'horizontal';
/**
* default display direction for altering/creating columns (tbl_properties)
* (horizontal|vertical|)
- * number indicates maximal number for which vertical model is used
+ * number indicates maximum number for which vertical model is used
*
* @global integer $cfg['DefaultPropDisplay']
*/
@@ -2295,7 +2355,18 @@ $cfg['NaturalOrder'] = true;
*/
$cfg['InitialSlidersState'] = 'closed';
+/**
+ * User preferences: disallow these settings
+ * For possible setting names look in libraries/config/user_preferences.forms.php
+ *
+ * @global array $cfg['UserprefsDisallow']
+ */
+$cfg['UserprefsDisallow'] = array();
+/**
+ * User preferences: enable the Developer tab
+ */
+$cfg['UserprefsDeveloperTab'] = false;
/*******************************************************************************
* Window title settings
diff --git a/libraries/config.values.php b/libraries/config.values.php
new file mode 100644
index 000000000..dea8a2337
--- /dev/null
+++ b/libraries/config.values.php
@@ -0,0 +1,189 @@
+ 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['MemoryLimit'] = 'short_string';
+$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['LeftFrameDBSeparator'] = 'short_string';
+$cfg_db['LeftFrameTableSeparator'] = 'short_string';
+$cfg_db['NavigationBarIconic'] = array(true, false, 'both');
+$cfg_db['Order'] = array('ASC', 'DESC', 'SMART');
+$cfg_db['ProtectBinary'] = array(false, 'blob', 'all');
+$cfg_db['DefaultDisplay'] = array('horizontal', 'vertical', 'horizontalflipped');
+$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['InitialSlidersState'] = array('open', 'closed');
+$cfg_db['Import']['format'] = array(
+ 'csv', // CSV
+ 'docsql', // DocSQL
+ 'ldi', // CSV using LOAD DATA
+ 'sql'); // SQL
+$cfg_db['Import']['charset'] = array_merge(array(''), $GLOBALS['cfg']['AvailableCharsets']);
+$cfg_db['Import']['sql_compatibility'] = $cfg_db['Export']['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']['csv_terminated'] = 'short_string';
+$cfg_db['Import']['csv_enclosed'] = 'short_string';
+$cfg_db['Import']['csv_escaped'] = 'short_string';
+$cfg_db['Import']['ldi_terminated'] = 'short_string';
+$cfg_db['Import']['ldi_enclosed'] = 'short_string';
+$cfg_db['Import']['ldi_escaped'] = 'short_string';
+$cfg_db['Import']['ldi_local_option'] = array('auto', true, false);
+$cfg_db['Export']['_sod_select'] = array(
+ 'structure' => __('structure'),
+ 'data' => __('data'),
+ 'structure_and_data' => __('structure and data'));
+$cfg_db['Export']['method'] = array(
+ 'quick' => __('Quick - display only the minimal options to configure'),
+ 'custom' => __('Custom - display all possible options to configure'),
+ 'custom-no-form' => __('Custom - like above, but without the quick/custom choice'));
+$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']['codegen_format'] = array('#', 'NHibernate C# DO', 'NHibernate XML');
+$cfg_db['Export']['csv_separator'] = 'short_string';
+$cfg_db['Export']['csv_terminated'] = 'short_string';
+$cfg_db['Export']['csv_enclosed'] = 'short_string';
+$cfg_db['Export']['csv_escaped'] = 'short_string';
+$cfg_db['Export']['csv_null'] = 'short_string';
+$cfg_db['Export']['excel_null'] = 'short_string';
+$cfg_db['Export']['excel_edition'] = array('win' => 'Windows',
+ 'mac_excel2003' => 'Excel 2003 / Macintosh', 'mac_excel2008' => 'Excel 2008 / Macintosh');
+$cfg_db['Export']['sql_structure_or_data'] = $cfg_db['Export']['_sod_select'];
+$cfg_db['Export']['sql_type'] = array('INSERT', 'UPDATE', 'REPLACE');
+$cfg_db['Export']['sql_insert_syntax'] = array(
+ 'complete' => __('complete inserts'),
+ 'extended' => __('extended inserts'),
+ 'both' => __('both of the above'),
+ 'none' => __('neither of the above'));
+$cfg_db['Export']['xls_null'] = 'short_string';
+$cfg_db['Export']['xlsx_null'] = 'short_string';
+$cfg_db['Export']['htmlword_structure_or_data'] = $cfg_db['Export']['_sod_select'];
+$cfg_db['Export']['htmlword_null'] = 'short_string';
+$cfg_db['Export']['ods_null'] = 'short_string';
+$cfg_db['Export']['odt_null'] = 'short_string';
+$cfg_db['Export']['odt_structure_or_data'] = $cfg_db['Export']['_sod_select'];
+$cfg_db['Export']['texytext_structure_or_data'] = $cfg_db['Export']['_sod_select'];
+$cfg_db['Export']['texytext_null'] = 'short_string';
+
+/**
+ * Default values overrides
+ * Use only full paths
+ */
+$cfg_db['_overrides'] = array();
+$cfg_db['_overrides']['Servers/1/extension'] = extension_loaded('mysqli')
+ ? 'mysqli' : 'mysql';
+
+/**
+ * Basic validator assignments (functions from libraries/config/validate.lib.php and 'validators'
+ * object in js/config.js)
+ * Use only full paths and form ids
+ */
+$cfg_db['_validators'] = array(
+ 'CharTextareaCols' => 'validate_positive_number',
+ 'CharTextareaRows' => 'validate_positive_number',
+ 'DefaultPropDisplay' => array(array('validate_by_regex', '/^(?:horizontal|vertical|\d+)$/')),
+ 'ExecTimeLimit' => 'validate_non_negative_number',
+ 'Export/sql_max_query_size' => 'validate_positive_number',
+ 'ForeignKeyMaxLimit' => 'validate_positive_number',
+ 'Import/csv_enclosed' => array(array('validate_by_regex', '/^.?$/')),
+ 'Import/csv_escaped' => array(array('validate_by_regex', '/^.$/')),
+ 'Import/csv_terminated' => array(array('validate_by_regex', '/^.$/')),
+ 'Import/ldi_enclosed' => array(array('validate_by_regex', '/^.?$/')),
+ 'Import/ldi_escaped' => array(array('validate_by_regex', '/^.$/')),
+ 'Import/ldi_terminated' => array(array('validate_by_regex', '/^.$/')),
+ 'Import/skip_queries' => 'validate_non_negative_number',
+ 'InsertRows' => 'validate_positive_number',
+ 'LeftFrameTableLevel' => 'validate_positive_number',
+ 'LimitChars' => 'validate_positive_number',
+ 'LoginCookieValidity' => 'validate_positive_number',
+ 'LoginCookieStore' => 'validate_non_negative_number',
+ 'MaxDbList' => 'validate_positive_number',
+ 'MaxCharactersInDisplayedSQL' => 'validate_positive_number',
+ 'MaxRows' => 'validate_positive_number',
+ 'MaxTableList' => 'validate_positive_number',
+ 'MemoryLimit' => array(array('validate_by_regex', '/^\d+(?:[kmg])?$/i')),
+ 'QueryHistoryMax' => 'validate_positive_number',
+ 'QueryWindowWidth' => 'validate_positive_number',
+ 'QueryWindowHeight' => 'validate_positive_number',
+ 'RepeatCells' => 'validate_non_negative_number',
+ 'Server' => 'validate_server',
+ 'Server_pmadb' => 'validate_pmadb',
+ 'Servers/1/port' => 'validate_port_number',
+ 'Servers/1/hide_db' => 'validate_regex',
+ 'TextareaCols' => 'validate_positive_number',
+ 'TextareaRows' => 'validate_positive_number',
+ 'TrustedProxies' => 'validate_trusted_proxies');
+
+/**
+ * Additional validators used for user preferences
+ */
+$cfg_db['_userValidators'] = array(
+ 'MaxDbList' => array(array('validate_upper_bound', 'value:MaxDbList')),
+ 'MaxTableList' => array(array('validate_upper_bound', 'value:MaxTableList')),
+ 'QueryHistoryMax' => array(array('validate_upper_bound', 'value:QueryHistoryMax')),);
+?>
\ No newline at end of file
diff --git a/libraries/config/ConfigFile.class.php b/libraries/config/ConfigFile.class.php
new file mode 100644
index 000000000..41ffe4b07
--- /dev/null
+++ b/libraries/config/ConfigFile.class.php
@@ -0,0 +1,486 @@
+cfg;
+ require './libraries/config.default.php';
+ $cfg['fontsize'] = '82%';
+
+ // load additional config information
+ $cfg_db = &$this->cfgDb;
+ require './libraries/config.values.php';
+
+ // apply default values overrides
+ if (count($cfg_db['_overrides'])) {
+ foreach ($cfg_db['_overrides'] as $path => $value) {
+ PMA_array_write($path, $cfg, $value);
+ }
+ }
+
+ $this->id = 'ConfigFile' . $GLOBALS['server'];
+ if (!isset($_SESSION[$this->id])) {
+ $_SESSION[$this->id] = array();
+ }
+ }
+
+ /**
+ * Returns class instance
+ *
+ * @return ConfigFile
+ */
+ public static function getInstance()
+ {
+ if (is_null(self::$_instance)) {
+ self::$_instance = new ConfigFile();
+ }
+ return self::$_instance;
+ }
+
+ /**
+ * Sets names of config options which will be placed in config file even if they are set
+ * to their default values (use only full paths)
+ *
+ * @param array $keys
+ */
+ public function setPersistKeys($keys)
+ {
+ // checking key presence is much faster than searching so move values to keys
+ $this->persistKeys = array_flip($keys);
+ }
+
+ /**
+ * Returns flipped array set by {@link setPersistKeys()}
+ *
+ * @return array
+ */
+ public function getPersistKeysMap()
+ {
+ return $this->persistKeys;
+ }
+
+ /**
+ * By default ConfigFile allows setting of all configuration keys, use this method
+ * to set up a filter on {@link set()} method
+ *
+ * @param array|null $keys array of allowed keys or null to remove filter
+ */
+ public function setAllowedKeys($keys)
+ {
+ if ($keys === null) {
+ $this->setFilter = null;
+ return;
+ }
+ // checking key presence is much faster than searching so move values to keys
+ $this->setFilter = array_flip($keys);
+ }
+
+ /**
+ * Sets path mapping for updating config in {@link updateWithGlobalConfig()} or reading
+ * by {@link getConfig()} or {@link getConfigArray()}
+ * @var array
+ */
+ public function setCfgUpdateReadMapping(array $mapping)
+ {
+ $this->cfgUpdateReadMapping = $mapping;
+ }
+
+ /**
+ * Resets configuration data
+ */
+ public function resetConfigData()
+ {
+ $_SESSION[$this->id] = array();
+ }
+
+ /**
+ * Sets configuration data (overrides old data)
+ *
+ * @param array $cfg
+ */
+ public function setConfigData(array $cfg)
+ {
+ $_SESSION[$this->id] = $cfg;
+ }
+
+ /**
+ * Sets config value
+ *
+ * @uses PMA_array_remove()
+ * @uses PMA_array_write()
+ * @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);
+ }
+ // apply key whitelist
+ if ($this->setFilter !== null && !isset($this->setFilter[$canonical_path])) {
+ return;
+ }
+ // 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)))) {
+ PMA_array_remove($path, $_SESSION[$this->id]);
+ } else {
+ PMA_array_write($path, $_SESSION[$this->id], $value);
+ }
+ }
+
+ /**
+ * Flattens multidimensional array, changes indices to paths (eg. 'key/subkey').
+ * Used as array_walk() callback.
+ *
+ * @param mixed $value
+ * @param mixed $key
+ * @param mixed $prefix
+ */
+ private function _flattenArray($value, $key, $prefix)
+ {
+ // no recursion for numeric arrays
+ if (is_array($value) && !isset($value[0])) {
+ $prefix .= $key . '/';
+ array_walk($value, array($this, '_flattenArray'), $prefix);
+ } else {
+ $this->_flattenArrayResult[$prefix . $key] = $value;
+ }
+ }
+
+ /**
+ * Returns default config in a flattened array
+ *
+ * @return array
+ */
+ public function getFlatDefaultConfig()
+ {
+ $this->_flattenArrayResult = array();
+ array_walk($this->cfg, array($this, '_flattenArray'), '');
+ $flat_cfg = $this->_flattenArrayResult;
+ $this->_flattenArrayResult = null;
+ return $flat_cfg;
+ }
+
+ /**
+ * Updates config with values read from given array
+ * (config will contain differences to defaults from config.defaults.php).
+ *
+ * @param array $cfg
+ */
+ public function updateWithGlobalConfig(array $cfg)
+ {
+ // load config array and flatten it
+ $this->_flattenArrayResult = array();
+ array_walk($cfg, array($this, '_flattenArray'), '');
+ $flat_cfg = $this->_flattenArrayResult;
+ $this->_flattenArrayResult = null;
+
+ // save values
+ // map for translating a few user preferences paths, should be complemented
+ // by code reading from generated config to perform inverse mapping
+ foreach ($flat_cfg as $path => $value) {
+ if (isset($this->cfgUpdateReadMapping[$path])) {
+ $path = $this->cfgUpdateReadMapping[$path];
+ }
+ $this->set($path, $value, $path);
+ }
+ }
+
+ /**
+ * Returns config value or $default if it's not set
+ *
+ * @uses PMA_array_read()
+ * @param string $path
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($path, $default = null)
+ {
+ return PMA_array_read($path, $_SESSION[$this->id], $default);
+ }
+
+ /**
+ * Returns default config value or $default it it's not set ie. it doesn't
+ * exist in config.default.php ($cfg) and config.values.php
+ * ($_cfg_db['_overrides'])
+ *
+ * @uses PMA_array_read()
+ * @param string $canonical_path
+ * @param mixed $default
+ * @return mixed
+ */
+ public function getDefault($canonical_path, $default = null)
+ {
+ return PMA_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
+ *
+ * @uses PMA_array_read()
+ * @param string $path
+ * @param mixed $default
+ * @return mixed
+ */
+ public function getValue($path, $default = null)
+ {
+ $v = PMA_array_read($path, $_SESSION[$this->id], 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)
+ *
+ * @uses PMA_array_read()
+ * @param string $path
+ * @param mixed $default
+ * @return mixed
+ */
+ public function getDbEntry($path, $default = null)
+ {
+ return PMA_array_read($path, $this->cfgDb, $default);
+ }
+
+ /**
+ * Returns server count
+ *
+ * @return int
+ */
+ public function getServerCount()
+ {
+ return isset($_SESSION[$this->id]['Servers'])
+ ? count($_SESSION[$this->id]['Servers'])
+ : 0;
+ }
+
+ /**
+ * Returns server list
+ *
+ * @return array|null
+ */
+ public function getServers()
+ {
+ return isset($_SESSION[$this->id]['Servers'])
+ ? $_SESSION[$this->id]['Servers']
+ : null;
+ }
+
+ /**
+ * Returns DSN of given server
+ *
+ * @param integer $server
+ * @return string
+ */
+ function getServerDSN($server)
+ {
+ if (!isset($_SESSION[$this->id]['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[$this->id]['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[$this->id]['Servers'][$server])) {
+ return;
+ }
+ $last_server = $this->getServerCount();
+
+ for ($i = $server; $i < $last_server; $i++) {
+ $_SESSION[$this->id]['Servers'][$i] = $_SESSION[$this->id]['Servers'][$i+1];
+ }
+ unset($_SESSION[$this->id]['Servers'][$last_server]);
+
+ if (isset($_SESSION[$this->id]['ServerDefault'])
+ && $_SESSION[$this->id]['ServerDefault'] >= 0) {
+ unset($_SESSION[$this->id]['ServerDefault']);
+ }
+ }
+
+ /**
+ * Returns config file path, relative to phpMyAdmin's root path
+ *
+ * @return string
+ */
+ public function getFilePath()
+ {
+ // Load paths
+ if (!defined('SETUP_CONFIG_FILE')) {
+ require_once './libraries/vendor_config.php';
+ }
+
+ return SETUP_CONFIG_FILE;
+ }
+
+ /**
+ * Returns configuration array (full, multidimensional format)
+ *
+ * @return array
+ */
+ public function getConfig()
+ {
+ $c = $_SESSION[$this->id];
+ foreach ($this->cfgUpdateReadMapping as $map_to => $map_from) {
+ PMA_array_write($map_to, $c, PMA_array_read($map_from, $c));
+ PMA_array_remove($map_from, $c);
+ }
+ return $c;
+ }
+
+ /**
+ * Returns configuration array (flat format)
+ *
+ * @return array
+ */
+ public function getConfigArray()
+ {
+ $this->_flattenArrayResult = array();
+ array_walk($_SESSION[$this->id], array($this, '_flattenArray'), '');
+ $c = $this->_flattenArrayResult;
+ $this->_flattenArrayResult = null;
+
+ $persistKeys = array_diff(array_keys($this->persistKeys), array_keys($c));
+ foreach ($persistKeys as $k) {
+ $c[$k] = $this->getDefault($k);
+ }
+
+ foreach ($this->cfgUpdateReadMapping as $map_to => $map_from) {
+ if (!isset($c[$map_from])) {
+ continue;
+ }
+ $c[$map_to] = $c[$map_from];
+ unset($c[$map_from]);
+ }
+ return $c;
+ }
+}
+?>
\ No newline at end of file
diff --git a/setup/lib/Form.class.php b/libraries/config/Form.class.php
similarity index 67%
rename from setup/lib/Form.class.php
rename to libraries/config/Form.class.php
index 1e1fad4a4..b53d86f85 100644
--- a/setup/lib/Form.class.php
+++ b/libraries/config/Form.class.php
@@ -1,17 +1,16 @@
index = $index;
- $this->loadForm($form_name);
+ $this->loadForm($form_name, $form);
}
/**
@@ -80,6 +74,8 @@ class Form
/**
* Returns allowed values for select fields
*
+ * @uses ConfigFile::getDbEntry()
+ * @uses ConfigFile::getInstance()
* @param string $option_path
* @return array
*/
@@ -94,12 +90,33 @@ class Form
trigger_error("$option_path - not a static value list", E_USER_ERROR);
return array();
}
+ // convert array('#', 'a', 'b') to array('a', 'b')
+ if (isset($value[0]) && $value[0] === '#') {
+ // remove first element ('#')
+ array_shift($value);
+ } else {
+ // convert value list array('a', 'b') to array('a' => 'a', 'b' => 'b')
+ $has_string_keys = false;
+ $keys = array();
+ for ($i = 0; $i < count($value); $i++) {
+ if (!isset($value[$i])) {
+ $has_string_keys = true;
+ break;
+ }
+ $keys[] = is_bool($value[$i]) ? (int)$value[$i] : $value[$i];
+ }
+ if (!$has_string_keys) {
+ $value = array_combine($keys, $value);
+ }
+ }
+
+ // $value has keys and value names, return it
return $value;
}
/**
* array_walk callback function, reads path of form fields from
- * array (see file comment in forms.inc.php)
+ * array (see file comment in setup.forms.php or user_preferences.forms.inc)
*
* @param mixed $value
* @param mixed $key
@@ -108,41 +125,33 @@ class Form
private function _readFormPathsCallback($value, $key, $prefix)
{
if (is_array($value)) {
- $prefix .= (empty($prefix) ? '' : '/') . $key;
+ $prefix .= $key . '/';
array_walk($value, array($this, '_readFormPathsCallback'), $prefix);
} else {
if (!is_int($key)) {
- $this->default[$prefix . '/' . $key] = $value;
+ $this->default[$prefix . $key] = $value;
$value = $key;
}
- $this->fields[] = $prefix . '/' . $value;
+ $this->fields[] = $prefix . $value;
}
}
/**
* Reads form paths to {@link $fields}
+ *
+ * @param array $form
*/
- protected function readFormPaths()
+ protected function readFormPaths($form)
{
- if (is_null(self::$_forms)) {
- $forms =& self::$_forms;
- require './setup/lib/forms.inc.php';
- }
-
- if (!isset(self::$_forms[$this->name])) {
- return;
- }
-
// flatten form fields' paths and save them to $fields
$this->fields = array();
- array_walk(self::$_forms[$this->name], array($this, '_readFormPathsCallback'), '');
+ array_walk($form, array($this, '_readFormPathsCallback'), '');
// $this->fields is an array of the form: [0..n] => 'field path'
// change numeric indexes to contain field names (last part of the path)
$paths = $this->fields;
$this->fields = array();
foreach ($paths as $path) {
- $path = ltrim($path, '/');
$key = ltrim(substr($path, strrpos($path, '/')), '/');
$this->fields[$key] = $path;
}
@@ -151,11 +160,19 @@ class Form
/**
* Reads fields' types to $this->fieldsTypes
+ *
+ * @uses ConfigFile::getDbEntry()
+ * @uses ConfigFile::getDefault()
+ * @uses ConfigFile::getInstance()
*/
protected function readTypes()
{
$cf = ConfigFile::getInstance();
foreach ($this->fields as $name => $path) {
+ if (strpos($name, ':group:') === 0) {
+ $this->fieldsTypes[$name] = 'group';
+ continue;
+ }
$v = $cf->getDbEntry($path);
if ($v !== null) {
$type = is_array($v) ? 'select' : $v;
@@ -171,12 +188,13 @@ class Form
* config file
*
* @param string $form_name
+ * @param array $form
*/
- public function loadForm($form_name)
+ public function loadForm($form_name, $form)
{
$this->name = $form_name;
- $this->readFormPaths();
+ $this->readFormPaths($form);
$this->readTypes();
}
}
-?>
+?>
\ No newline at end of file
diff --git a/setup/lib/FormDisplay.class.php b/libraries/config/FormDisplay.class.php
similarity index 61%
rename from setup/lib/FormDisplay.class.php
rename to libraries/config/FormDisplay.class.php
index 7b6bfebb4..5a08f5e87 100644
--- a/setup/lib/FormDisplay.class.php
+++ b/libraries/config/FormDisplay.class.php
@@ -1,4 +1,5 @@
js_lang_strings = array(
+ 'error_nan_p' => __('Not a positive number'),
+ 'error_nan_nneg' => __('Not a non-negative number'),
+ 'error_incorrect_port' => __('Not a valid port number'),
+ 'error_invalid_value' => __('Incorrect value'),
+ 'error_value_lte' => __('Value must be equal or lower than %s'));
+ // initialize validators
+ PMA_config_get_validators();
+ }
/**
* Registers form in form manager
*
* @param string $form_name
+ * @param array $form
* @param int $server_id 0 if new server, validation; >= 1 if editing a server
*/
- public function registerForm($form_name, $server_id = null)
+ public function registerForm($form_name, array $form, $server_id = null)
{
- $this->forms[$form_name] = new Form($form_name, $server_id);
- $this->is_valdiated = false;
+ $this->forms[$form_name] = new Form($form_name, $form, $server_id);
+ $this->is_validated = false;
foreach ($this->forms[$form_name]->fields as $path) {
$work_path = $server_id === null
? $path
@@ -90,12 +113,12 @@ class FormDisplay
* Processes forms, returns true on successful save
*
* @param bool $allow_partial_save allows for partial form saving on failed validation
+ * @param bool $check_form_submit whether check for $_POST['submit_save']
* @return boolean
*/
- public function process($allow_partial_save = true)
+ public function process($allow_partial_save = true, $check_form_submit = true)
{
- // gather list of forms to save
- if (!isset($_POST['submit_save'])) {
+ if ($check_form_submit && !isset($_POST['submit_save'])) {
return false;
}
@@ -108,10 +131,14 @@ class FormDisplay
/**
* Runs validation for all registered forms
+ *
+ * @uses ConfigFile::getInstance()
+ * @uses ConfigFile::getValue()
+ * @uses PMA_config_validate()
*/
private function _validate()
{
- if ($this->is_valdiated) {
+ if ($this->is_validated) {
return;
}
@@ -130,7 +157,7 @@ class FormDisplay
}
// run validation
- $errors = validate($paths, $values, false);
+ $errors = PMA_config_validate($paths, $values, false);
// change error keys from canonical paths to work paths
if (is_array($errors) && count($errors) > 0) {
@@ -145,13 +172,25 @@ class FormDisplay
$this->errors[$work_path] = $error_list;
}
}
- $this->is_valdiated = true;
+ $this->is_validated = true;
}
-
/**
* Outputs HTML for forms
*
+ * @uses ConfigFile::getInstance()
+ * @uses ConfigFile::get()
+ * @uses display_fieldset_bottom()
+ * @uses display_fieldset_top()
+ * @uses display_form_bottom()
+ * @uses display_form_top()
+ * @uses display_js()
+ * @uses display_tabs_bottom()
+ * @uses display_tabs_top()
+ * @uses js_validate()
+ * @uses PMA_config_get_validators()
+ * @uses PMA_jsFormat()
+ * @uses PMA_lang()
* @param bool $tabbed_form
* @param bool $show_restore_default whether show "restore default" button besides the input field
*/
@@ -162,7 +201,7 @@ class FormDisplay
$js = array();
$js_default = array();
$tabbed_form = $tabbed_form && (count($this->forms) > 1);
- $validators = ConfigFile::getInstance()->getDbEntry('_validators');
+ $validators = PMA_config_get_validators();
display_form_top();
@@ -187,10 +226,13 @@ class FormDisplay
$this->_validate();
}
+ // user preferences
+ $this->_loadUserprefsInfo();
+
// display forms
foreach ($this->forms as $form) {
/* @var $form Form */
- $form_desc = isset($GLOBALS["strSetupForm_{$form->name}_desc"])
+ $form_desc = isset($GLOBALS["strConfigForm_{$form->name}_desc"])
? PMA_lang("Form_{$form->name}_desc")
: '';
$form_errors = isset($this->errors[$form->name])
@@ -201,9 +243,14 @@ class FormDisplay
foreach ($form->fields as $field => $path) {
$work_path = array_search($path, $this->system_paths);
$translated_path = $this->translated_paths[$work_path];
+ // always true/false for user preferences display
+ // otherwise null
+ $userprefs_allow = isset($this->userprefs_keys[$path])
+ ? !isset($this->userprefs_disallow[$path])
+ : null;
// display input
$this->_displayFieldInput($form, $field, $path, $work_path,
- $translated_path, $show_restore_default, $js_default);
+ $translated_path, $show_restore_default, $userprefs_allow, $js_default);
// register JS validators for this field
if (isset($validators[$path])) {
js_validate($translated_path, $validators[$path], $js);
@@ -221,35 +268,45 @@ class FormDisplay
if (!$js_lang_sent) {
$js_lang_sent = true;
$js_lang = array();
- foreach ($this->js_lang_strings as $str) {
- $lang = isset($GLOBALS["strSetup$str"])
- ? $GLOBALS["strSetup$str"]
- : filter_input($GLOBALS["str$str"]); // null if not set
- $js_lang[] = "'$str': '" . PMA_jsFormat($lang, false) . '\'';
+ foreach ($this->js_lang_strings as $strName => $strValue) {
+ $js_lang[] = "'$strName': '" . PMA_jsFormat($strValue, false) . '\'';
}
- $js[] = '$.extend(PMA_messages, {' . implode(",\n\t", $js_lang) . '})';
+ $js[] = "$.extend(PMA_messages, {\n\t" . implode(",\n\t", $js_lang) . '})';
}
- $js[] = '$.extend(defaultValues, {' . implode(",\n\t", $js_default) . '})';
+ $js[] = "$.extend(defaultValues, {\n\t" . implode(",\n\t", $js_default) . '})';
display_js($js);
}
/**
* Prepares data for input field display and outputs HTML code
*
+ * @uses ConfigFile::get()
+ * @uses ConfigFile::getDefault()
+ * @uses ConfigFile::getInstance()
+ * @uses display_group_footer()
+ * @uses display_group_header()
+ * @uses display_input()
+ * @uses Form::getOptionType()
+ * @uses Form::getOptionValueList()
+ * @uses PMA_escapeJsString()
+ * @uses PMA_lang_desc()
+ * @uses PMA_lang_name()
* @param Form $form
* @param string $field field name as it appears in $form
* @param string $system_path field path, eg. Servers/1/verbose
* @param string $work_path work path, eg. Servers/4/verbose
* @param string $translated_path work path changed so that it can be used as XHTML id
* @param bool $show_restore_default whether show "restore default" button besides the input field
+ * @param mixed $userprefs_allow whether user preferences are enabled for this field
+ * (null - no support, true/false - enabled/disabled)
* @param array &$js_default array which stores JavaScript code to be displayed
*/
private function _displayFieldInput(Form $form, $field, $system_path, $work_path,
- $translated_path, $show_restore_default, array &$js_default)
+ $translated_path, $show_restore_default, $userprefs_allow, array &$js_default)
{
$name = PMA_lang_name($system_path);
- $description = PMA_lang_desc($system_path);
+ $description = PMA_lang_name($system_path, 'desc', '');
$cf = ConfigFile::getInstance();
$value = $cf->get($work_path);
@@ -263,7 +320,9 @@ class FormDisplay
$opts = array(
'doc' => $this->getDocLink($system_path),
'wiki' => $this->getWikiLink($system_path),
- 'show_restore_default' => $show_restore_default);
+ 'show_restore_default' => $show_restore_default,
+ 'userprefs_allow' => $userprefs_allow,
+ 'userprefs_comment' => PMA_lang_name($system_path, 'cmt', ''));
if (isset($form->default[$system_path])) {
$opts['setvalue'] = $form->default[$system_path];
}
@@ -275,28 +334,33 @@ class FormDisplay
case 'string':
$type = 'text';
break;
- case 'double':
- $type = 'text';
+ case 'short_string':
+ $type = 'short_text';
break;
+ case 'double':
case 'integer':
- $type = 'text';
+ $type = 'number_text';
break;
case 'boolean':
$type = 'checkbox';
break;
case 'select':
$type = 'select';
- $opts['values'] = array();
- $values = $form->getOptionValueList($form->fields[$field]);
- foreach ($values as $v) {
- $opts['values'][$v] = $v;
- }
+ $opts['values'] = $form->getOptionValueList($form->fields[$field]);
break;
case 'array':
$type = 'list';
$value = (array) $value;
$value_default = (array) $value_default;
break;
+ case 'group':
+ $text = substr($field, 7);
+ if ($text != 'end') {
+ display_group_header($text);
+ } else {
+ display_group_footer();
+ }
+ return;
case 'NULL':
trigger_error("Field $system_path has no type", E_USER_WARNING);
return;
@@ -310,11 +374,14 @@ class FormDisplay
}
}
}
+ $this->_setComments($system_path, $opts);
// send default value to form's JS
$js_line = '\'' . $translated_path . '\': ';
switch ($type) {
case 'text':
+ case 'short_text':
+ case 'number_text':
$js_line .= '\'' . PMA_escapeJsString($value_default) . '\'';
break;
case 'checkbox':
@@ -338,6 +405,9 @@ class FormDisplay
/**
* Displays errors
+ *
+ * @uses display_errors()
+ * @uses PMA_lang_name()
*/
public function displayErrors()
{
@@ -351,7 +421,7 @@ class FormDisplay
$path = $this->system_paths[$system_path];
$name = PMA_lang_name($path);
} else {
- $name = $GLOBALS["strSetupForm_$system_path"];
+ $name = $GLOBALS["strConfigForm_$system_path"];
}
display_errors($name, $error_list);
}
@@ -359,6 +429,11 @@ class FormDisplay
/**
* Reverts erroneous fields to their default values
+ *
+ * @uses ConfigFile::getDefault()
+ * @uses ConfigFile::getInstance()
+ * @uses ConfigFile::set()
+ *
*/
public function fixErrors()
{
@@ -386,11 +461,17 @@ class FormDisplay
*/
private function _validateSelect(&$value, array $allowed)
{
- foreach ($allowed as $v) {
- if ($value == $v) {
- settype($value, gettype($v));
- return true;
- }
+ $value_cmp = is_bool($value)
+ ? (int) $value
+ : $value;
+ foreach ($allowed as $vk => $v) {
+ // equality comparison only if both values are numeric or not numeric
+ // (allows to skip 0 == 'string' equalling to true) or identity (for string-string)
+ if (($vk == $value && !(is_numeric($value_cmp) xor is_numeric($vk)))
+ || $vk === $value) {
+ settype($value, gettype($v));
+ return true;
+ }
}
return false;
}
@@ -398,6 +479,13 @@ class FormDisplay
/**
* Validates and saves form data to session
*
+ * @uses ConfigFile::get()
+ * @uses ConfigFile::getInstance()
+ * @uses ConfigFile::getServerCount()
+ * @uses ConfigFile::set()
+ * @uses Form::getOptionType()
+ * @uses Form::getOptionValueList()
+ * @uses PMA_lang_name()
* @param array|string $forms array of form names
* @param bool $allow_partial_save allows for partial form saving on failed validation
* @return boolean true on success (no errors and all saved)
@@ -410,6 +498,11 @@ class FormDisplay
$values = array();
$to_save = array();
+ $is_setup_script = defined('PMA_SETUP') && PMA_SETUP;
+ if ($is_setup_script) {
+ $this->_loadUserprefsInfo();
+ }
+
$this->errors = array();
foreach ($forms as $form) {
/* @var $form Form */
@@ -426,23 +519,38 @@ class FormDisplay
foreach ($form->fields as $field => $system_path) {
$work_path = array_search($system_path, $this->system_paths);
$key = $this->translated_paths[$work_path];
+ $type = $form->getOptionType($field);
+
+ // skip groups
+ if ($type == 'group') {
+ continue;
+ }
// ensure the value is set
if (!isset($_POST[$key])) {
// checkboxes aren't set by browsers if they're off
- if ($form->getOptionType($field) == 'boolean') {
+ if ($type == 'boolean') {
$_POST[$key] = false;
} else {
- $this->errors[$form->name][] = PMA_lang(
- 'error_missing_field_data',
+ $this->errors[$form->name][] = sprintf(
+ __('Missing data for %s'),
'' . PMA_lang_name($system_path) . ' ');
$result = false;
continue;
}
}
+ // user preferences allow/disallow
+ if ($is_setup_script && isset($this->userprefs_keys[$system_path])) {
+ if (isset($this->userprefs_disallow[$system_path])
+ && isset($_POST[$key . '-userprefs-allow'])) {
+ unset($this->userprefs_disallow[$system_path]);
+ } else if (!isset($_POST[$key . '-userprefs-allow'])) {
+ $this->userprefs_disallow[$system_path] = true;
+ }
+ }
+
// cast variables to correct type
- $type = $form->getOptionType($field);
switch ($type) {
case 'double':
settype($_POST[$key], 'float');
@@ -461,11 +569,14 @@ class FormDisplay
}
break;
case 'string':
+ case 'short_string':
$_POST[$key] = trim($_POST[$key]);
break;
case 'array':
// eliminate empty values and ensure we have an array
- $post_values = explode("\n", $_POST[$key]);
+ $post_values = is_array($_POST[$key])
+ ? $_POST[$key]
+ : explode("\n", $_POST[$key]);
$_POST[$key] = array();
foreach ($post_values as $v) {
$v = trim($v);
@@ -509,6 +620,9 @@ class FormDisplay
}
$cf->set($work_path, $values[$path], $path);
}
+ if ($is_setup_script) {
+ $cf->set('UserprefsDisallow', array_keys($this->userprefs_disallow));
+ }
}
// don't look for non-critical errors
@@ -540,7 +654,7 @@ class FormDisplay
if ($test == 'Import' || $test == 'Export') {
return '';
}
- return '../Documentation.html#cfg_' . self::_getOptName($path);
+ return 'Documentation.html#cfg_' . $this->_getOptName($path);
}
/**
@@ -551,7 +665,7 @@ class FormDisplay
*/
public function getWikiLink($path)
{
- $opt_name = self::_getOptName($path);
+ $opt_name = $this->_getOptName($path);
if (substr($opt_name, 0, 7) == 'Servers') {
$opt_name = substr($opt_name, 8);
if (strpos($opt_name, 'AllowDeny') === 0) {
@@ -577,9 +691,88 @@ class FormDisplay
* @param string $path
* @return string
*/
- private static function _getOptName($path)
+ private function _getOptName($path)
{
- return str_replace(array('Servers/1/', '/'), array('Servers/', '_'), $path);
+ return str_replace(array('Servers/1/', '/'), array('Servers/', '_'), $path);
+ }
+
+ /**
+ * Fills out {@link userprefs_keys} and {@link userprefs_disallow}
+ *
+ * @uses PMA_read_userprefs_fieldnames()
+ */
+ private function _loadUserprefsInfo()
+ {
+ if ($this->userprefs_keys === null) {
+ $this->userprefs_keys = array_flip(PMA_read_userprefs_fieldnames());
+ // read real config for user preferences display
+ $userprefs_disallow = defined('PMA_SETUP') && PMA_SETUP
+ ? ConfigFile::getInstance()->get('UserprefsDisallow', array())
+ : $GLOBALS['cfg']['UserprefsDisallow'];
+ $this->userprefs_disallow = array_flip($userprefs_disallow);
+ }
+ }
+
+ /**
+ * Sets field comments and warnings based on current environment
+ *
+ * @param string $system_path
+ * @param array $opts
+ */
+ private function _setComments($system_path, array &$opts)
+ {
+ // RecodingEngine - mark unavailable types
+ if ($system_path == 'RecodingEngine') {
+ $comment = '';
+ if (!function_exists('iconv')) {
+ $opts['values']['iconv'] .= ' (' . __('unavailable') . ')';
+ $comment = sprintf(__('"%s" requires %s extension'), 'iconv', 'iconv');
+ }
+ if (!function_exists('recode_string')) {
+ $opts['values']['recode'] .= ' (' . __('unavailable') . ')';
+ $comment .= ($comment ? ", " : '') . sprintf(__('"%s" requires %s extension'),
+ 'recode', 'recode');
+ }
+ $opts['comment'] = $comment;
+ $opts['comment_warning'] = true;
+ }
+ // ZipDump, GZipDump, BZipDump - check function availability
+ if ($system_path == 'ZipDump' || $system_path == 'GZipDump' || $system_path == 'BZipDump') {
+ $comment = '';
+ $funcs = array(
+ 'ZipDump' => array('zip_open', 'gzcompress'),
+ 'GZipDump' => array('gzopen', 'gzencode'),
+ 'BZipDump' => array('bzopen', 'bzcompress'));
+ if (!function_exists($funcs[$system_path][0])) {
+ $comment = sprintf(__('import will not work, missing function (%s)'),
+ $funcs[$system_path][0]);
+ }
+ if (!function_exists($funcs[$system_path][1])) {
+ $comment .= ($comment ? '; ' : '') . sprintf(__('export will not work, missing function (%s)'),
+ $funcs[$system_path][1]);
+ }
+ $opts['comment'] = $comment;
+ $opts['comment_warning'] = true;
+ }
+ if ($system_path == 'SQLQuery/Validate' && !$GLOBALS['cfg']['SQLValidator']['use']) {
+ $opts['comment'] = __('SQL Validator is disabled');
+ $opts['comment_warning'] = true;
+ }
+ if ($system_path == 'SQLValidator/use') {
+ if (!class_exists('SOAPClient')) {
+ @include_once 'SOAP/Client.php';
+ if (!class_exists('SOAP_Client')) {
+ $opts['comment'] = __('SOAP extension not found');
+ $opts['comment_warning'] = true;
+ }
+ }
+ }
+ if (!defined('PMA_SETUP') || !PMA_SETUP) {
+ if (($system_path == 'MaxDbList' || $system_path == 'MaxTableList'
+ || $system_path == 'QueryHistoryMax')) {
+ $opts['comment'] = sprintf(__('maximum %s'), $GLOBALS['cfg'][$system_path]);
+ }
+ }
}
}
-?>
+?>
\ No newline at end of file
diff --git a/libraries/config/FormDisplay.tpl.php b/libraries/config/FormDisplay.tpl.php
new file mode 100644
index 000000000..40c59d1c4
--- /dev/null
+++ b/libraries/config/FormDisplay.tpl.php
@@ -0,0 +1,429 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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();
+}
+?>
+
+
+
+
+
+
+
+
+ ', '');
+ echo PMA_showDocu('setup_script');
+ ?>
+
+
+
+
+
+
+
+
\ 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 '/^ {print $2}' | grep -Ev '(strSetup.*_(desc|name)|strSetupForm_|strSetupFormset_)'
+diff $tmp1 $tmp2 | awk '/^ {print $2}' | grep -Ev '(strConfig.*_(desc|name)|strConfigForm_|strConfigFormset_)'
rm -f $tmp1 $tmp2
diff --git a/setup/config.php b/setup/config.php
index e2bd13214..fa181069f 100644
--- a/setup/config.php
+++ b/setup/config.php
@@ -1,21 +1,23 @@
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 @@
getConfigFile())
+ echo htmlspecialchars(ConfigGenerator::getConfigFile())
?>
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) {
-?>
-
-
-
- '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 ' ';
- }
-?>
-
-
-
-
-
-
-
-\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