aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Config
diff options
context:
space:
mode:
authorCharles <sircharlesaze@gmail.com>2020-01-09 10:55:03 +0100
committerCharles <sircharlesaze@gmail.com>2020-01-09 13:09:38 +0100
commit04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa (patch)
tree5c691241355c943a3c68ddb06b8cf8c60aa11319 /srcs/phpmyadmin/libraries/classes/Config
parent7e0d85db834d6351ed85d01e5126ac31dc510b86 (diff)
downloadft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.tar.gz
ft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.tar.bz2
ft_server-04d6d5ca99ebfd1cebb8ce06618fb3811fc1a8aa.zip
phpmyadmin working
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Config')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/ConfigFile.php531
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Descriptions.php934
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Form.php238
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/FormDisplay.php924
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/FormDisplayTemplate.php526
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/BaseForm.php89
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/BaseFormList.php150
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/BrowseForm.php30
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/DbStructureForm.php30
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/EditForm.php32
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ExportForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ImportForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/NaviForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/PageFormList.php37
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/SqlForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Page/TableStructureForm.php30
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ConfigForm.php32
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ExportForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/FeaturesForm.php77
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ImportForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/MainForm.php29
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/NaviForm.php18
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ServersForm.php116
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SetupFormList.php37
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SqlForm.php28
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/ExportForm.php160
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/FeaturesForm.php95
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/ImportForm.php73
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/MainForm.php98
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/NaviForm.php74
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/SqlForm.php54
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Forms/User/UserFormList.php35
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/PageSettings.php233
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/ServerConfigChecks.php583
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/SpecialSchemaLinks.php478
-rw-r--r--srcs/phpmyadmin/libraries/classes/Config/Validator.php594
36 files changed, 6473 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Config/ConfigFile.php b/srcs/phpmyadmin/libraries/classes/Config/ConfigFile.php
new file mode 100644
index 0000000..1e053dd
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/ConfigFile.php
@@ -0,0 +1,531 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Config file management
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config;
+use PhpMyAdmin\Core;
+
+/**
+ * Config file management class.
+ * Stores its data in $_SESSION
+ *
+ * @package PhpMyAdmin
+ */
+class ConfigFile
+{
+ /**
+ * Stores default PMA config from config.default.php
+ * @var array
+ */
+ private $_defaultCfg;
+
+ /**
+ * Stores allowed values for non-standard fields
+ * @var array
+ */
+ private $_cfgDb;
+
+ /**
+ * Stores original PMA config, not modified by user preferences
+ * @var array|null
+ */
+ private $_baseCfg;
+
+ /**
+ * Whether we are currently working in PMA Setup context
+ * @var bool
+ */
+ private $_isInSetup;
+
+ /**
+ * Keys which will be always written to config file
+ * @var array
+ */
+ private $_persistKeys = [];
+
+ /**
+ * Changes keys while updating config in {@link updateWithGlobalConfig()}
+ * or reading by {@link getConfig()} or {@link getConfigArray()}
+ * @var array
+ */
+ private $_cfgUpdateReadMapping = [];
+
+ /**
+ * Key filter for {@link set()}
+ * @var array|null
+ */
+ private $_setFilter;
+
+ /**
+ * Instance id (key in $_SESSION array, separate for each server -
+ * ConfigFile{server id})
+ * @var string
+ */
+ private $_id;
+
+ /**
+ * Result for {@link _flattenArray()}
+ * @var array|null
+ */
+ private $_flattenArrayResult;
+
+ /**
+ * Constructor
+ *
+ * @param array|null $baseConfig base configuration read from
+ * {@link PhpMyAdmin\Config::$base_config},
+ * use only when not in PMA Setup
+ */
+ public function __construct($baseConfig = null)
+ {
+ // load default config values
+ $cfg = &$this->_defaultCfg;
+ include ROOT_PATH . 'libraries/config.default.php';
+
+ // load additional config information
+ $this->_cfgDb = include ROOT_PATH . 'libraries/config.values.php';
+
+ // apply default values overrides
+ if (count($this->_cfgDb['_overrides'])) {
+ foreach ($this->_cfgDb['_overrides'] as $path => $value) {
+ Core::arrayWrite($path, $cfg, $value);
+ }
+ }
+
+ $this->_baseCfg = $baseConfig;
+ $this->_isInSetup = $baseConfig === null;
+ $this->_id = 'ConfigFile' . $GLOBALS['server'];
+ if (! isset($_SESSION[$this->_id])) {
+ $_SESSION[$this->_id] = [];
+ }
+ }
+
+ /**
+ * 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 the names of the config options
+ *
+ * @return void
+ */
+ public function setPersistKeys(array $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
+ *
+ * @return void
+ */
+ 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()}
+ *
+ * @param array $mapping Contains the mapping of "Server/config options"
+ * to "Server/1/config options"
+ *
+ * @return void
+ */
+ public function setCfgUpdateReadMapping(array $mapping)
+ {
+ $this->_cfgUpdateReadMapping = $mapping;
+ }
+
+ /**
+ * Resets configuration data
+ *
+ * @return void
+ */
+ public function resetConfigData()
+ {
+ $_SESSION[$this->_id] = [];
+ }
+
+ /**
+ * Sets configuration data (overrides old data)
+ *
+ * @param array $cfg Configuration options
+ *
+ * @return void
+ */
+ public function setConfigData(array $cfg)
+ {
+ $_SESSION[$this->_id] = $cfg;
+ }
+
+ /**
+ * Sets config value
+ *
+ * @param string $path Path
+ * @param mixed $value Value
+ * @param string $canonicalPath Canonical path
+ *
+ * @return void
+ */
+ public function set($path, $value, $canonicalPath = null)
+ {
+ if ($canonicalPath === null) {
+ $canonicalPath = $this->getCanonicalPath($path);
+ }
+ // apply key whitelist
+ if ($this->_setFilter !== null
+ && ! isset($this->_setFilter[$canonicalPath])
+ ) {
+ return;
+ }
+ // if the path isn't protected it may be removed
+ if (isset($this->_persistKeys[$canonicalPath])) {
+ Core::arrayWrite($path, $_SESSION[$this->_id], $value);
+ return;
+ }
+
+ $defaultValue = $this->getDefault($canonicalPath);
+ $removePath = $value === $defaultValue;
+ if ($this->_isInSetup) {
+ // remove if it has a default value or is empty
+ $removePath = $removePath
+ || (empty($value) && empty($defaultValue));
+ } else {
+ // get original config values not overwritten by user
+ // preferences to allow for overwriting options set in
+ // config.inc.php with default values
+ $instanceDefaultValue = Core::arrayRead(
+ $canonicalPath,
+ $this->_baseCfg
+ );
+ // remove if it has a default value and base config (config.inc.php)
+ // uses default value
+ $removePath = $removePath
+ && ($instanceDefaultValue === $defaultValue);
+ }
+ if ($removePath) {
+ Core::arrayRemove($path, $_SESSION[$this->_id]);
+ return;
+ }
+
+ Core::arrayWrite($path, $_SESSION[$this->_id], $value);
+ }
+
+ /**
+ * Flattens multidimensional array, changes indices to paths
+ * (eg. 'key/subkey').
+ * Used as array_walk() callback.
+ *
+ * @param mixed $value Value
+ * @param mixed $key Key
+ * @param mixed $prefix Prefix
+ *
+ * @return void
+ */
+ private function _flattenArray($value, $key, $prefix)
+ {
+ // no recursion for numeric arrays
+ if (is_array($value) && ! isset($value[0])) {
+ $prefix .= $key . '/';
+ array_walk($value, [$this, '_flattenArray'], $prefix);
+ } else {
+ $this->_flattenArrayResult[$prefix . $key] = $value;
+ }
+ }
+
+ /**
+ * Returns default config in a flattened array
+ *
+ * @return array
+ */
+ public function getFlatDefaultConfig()
+ {
+ $this->_flattenArrayResult = [];
+ array_walk($this->_defaultCfg, [$this, '_flattenArray'], '');
+ $flatConfig = $this->_flattenArrayResult;
+ $this->_flattenArrayResult = null;
+ return $flatConfig;
+ }
+
+ /**
+ * Updates config with values read from given array
+ * (config will contain differences to defaults from config.defaults.php).
+ *
+ * @param array $cfg Configuration
+ *
+ * @return void
+ */
+ public function updateWithGlobalConfig(array $cfg)
+ {
+ // load config array and flatten it
+ $this->_flattenArrayResult = [];
+ array_walk($cfg, [$this, '_flattenArray'], '');
+ $flatConfig = $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 ($flatConfig 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
+ *
+ * @param string $path Path of config file
+ * @param mixed $default Default values
+ *
+ * @return mixed
+ */
+ public function get($path, $default = null)
+ {
+ return Core::arrayRead($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'])
+ *
+ * @param string $canonicalPath Canonical path
+ * @param mixed $default Default value
+ *
+ * @return mixed
+ */
+ public function getDefault($canonicalPath, $default = null)
+ {
+ return Core::arrayRead($canonicalPath, $this->_defaultCfg, $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 Path
+ * @param mixed $default Default value
+ *
+ * @return mixed
+ */
+ public function getValue($path, $default = null)
+ {
+ $v = Core::arrayRead($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 Path
+ *
+ * @return string
+ */
+ public function getCanonicalPath($path)
+ {
+ return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path);
+ }
+
+ /**
+ * Returns config database entry for $path
+ *
+ * @param string $path path of the variable in config db
+ * @param mixed $default default value
+ *
+ * @return mixed
+ */
+ public function getDbEntry($path, $default = null)
+ {
+ return Core::arrayRead($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 server index
+ *
+ * @return string
+ */
+ public function getServerDSN($server)
+ {
+ if (! isset($_SESSION[$this->_id]['Servers'][$server])) {
+ return '';
+ }
+
+ $path = 'Servers/' . $server;
+ $dsn = 'mysqli://';
+ if ($this->getValue("$path/auth_type") == 'config') {
+ $dsn .= $this->getValue("$path/user");
+ if (! empty($this->getValue("$path/password"))) {
+ $dsn .= ':***';
+ }
+ $dsn .= '@';
+ }
+ if ($this->getValue("$path/host") != 'localhost') {
+ $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 server index
+ *
+ * @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 server index
+ *
+ * @return void
+ */
+ public function removeServer($server)
+ {
+ if (! isset($_SESSION[$this->_id]['Servers'][$server])) {
+ return;
+ }
+ $lastServer = $this->getServerCount();
+
+ for ($i = $server; $i < $lastServer; $i++) {
+ $_SESSION[$this->_id]['Servers'][$i]
+ = $_SESSION[$this->_id]['Servers'][$i + 1];
+ }
+ unset($_SESSION[$this->_id]['Servers'][$lastServer]);
+
+ if (isset($_SESSION[$this->_id]['ServerDefault'])
+ && $_SESSION[$this->_id]['ServerDefault'] == $lastServer
+ ) {
+ unset($_SESSION[$this->_id]['ServerDefault']);
+ }
+ }
+
+ /**
+ * Returns configuration array (full, multidimensional format)
+ *
+ * @return array
+ */
+ public function getConfig()
+ {
+ $c = $_SESSION[$this->_id];
+ foreach ($this->_cfgUpdateReadMapping as $mapTo => $mapFrom) {
+ // if the key $c exists in $map_to
+ if (Core::arrayRead($mapTo, $c) !== null) {
+ Core::arrayWrite($mapTo, $c, Core::arrayRead($mapFrom, $c));
+ Core::arrayRemove($mapFrom, $c);
+ }
+ }
+ return $c;
+ }
+
+ /**
+ * Returns configuration array (flat format)
+ *
+ * @return array
+ */
+ public function getConfigArray()
+ {
+ $this->_flattenArrayResult = [];
+ array_walk($_SESSION[$this->_id], [$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($this->getCanonicalPath($k));
+ }
+
+ foreach ($this->_cfgUpdateReadMapping as $mapTo => $mapFrom) {
+ if (! isset($c[$mapFrom])) {
+ continue;
+ }
+ $c[$mapTo] = $c[$mapFrom];
+ unset($c[$mapFrom]);
+ }
+ return $c;
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Descriptions.php b/srcs/phpmyadmin/libraries/classes/Config/Descriptions.php
new file mode 100644
index 0000000..22e7c1f
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Descriptions.php
@@ -0,0 +1,934 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Verbose descriptions for settings.
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Sanitize;
+
+/**
+ * Base class for forms, loads default configuration options, checks allowed
+ * values etc.
+ *
+ * @package PhpMyAdmin
+ */
+class Descriptions
+{
+ /**
+ * Return
+ * Return name or description for a configuration path.
+ *
+ * @param string $path Path of configuration
+ * @param string $type Type of message, either 'name', 'cmt' or 'desc'
+ *
+ * @return string
+ */
+ public static function get($path, $type = 'name')
+ {
+ $key = str_replace(
+ [
+ 'Servers/1/',
+ '/',
+ ],
+ [
+ 'Servers/',
+ '_',
+ ],
+ $path
+ );
+ $value = self::getString($key, $type);
+
+ /* Fallback to path for name and empty string for description and comment */
+ if ($value === null) {
+ if ($type == 'name') {
+ $value = $path;
+ } else {
+ $value = '';
+ }
+ }
+
+ return Sanitize::sanitizeMessage($value);
+ }
+
+ /**
+ * Return name or description for a cleaned up configuration path.
+ *
+ * @param string $path Path of configuration
+ * @param string $type Type of message, either 'name', 'cmt' or 'desc'
+ *
+ * @return string|null Null if not found
+ */
+ public static function getString($path, $type = 'name')
+ {
+ $descriptions = [
+ 'AllowArbitraryServer_desc' => __('If enabled, user can enter any MySQL server in login form for cookie auth.'),
+ 'AllowArbitraryServer_name' => __('Allow login to any MySQL server'),
+ 'ArbitraryServerRegexp_desc' => __(
+ 'Restricts the MySQL servers the user can enter when a login to an arbitrary '
+ . 'MySQL server is enabled by matching the IP or hostname of the MySQL server ' .
+ 'to the given regular expression.'
+ ),
+ 'ArbitraryServerRegexp_name' => __('Restrict login to MySQL server'),
+ 'AllowThirdPartyFraming_desc' => __(
+ 'Enabling this allows a page located on a different domain to call phpMyAdmin '
+ . 'inside a frame, and is a potential [strong]security hole[/strong] allowing '
+ . 'cross-frame scripting (XSS) attacks.'
+ ),
+ 'AllowThirdPartyFraming_name' => __('Allow third party framing'),
+ 'AllowUserDropDatabase_name' => __('Show "Drop database" link to normal users'),
+ 'blowfish_secret_desc' => __(
+ 'Secret passphrase used for encrypting cookies in [kbd]cookie[/kbd] '
+ . 'authentication.'
+ ),
+ 'blowfish_secret_name' => __('Blowfish secret'),
+ 'BrowseMarkerEnable_desc' => __('Highlight selected rows.'),
+ 'BrowseMarkerEnable_name' => __('Row marker'),
+ 'BrowsePointerEnable_desc' => __('Highlight row pointed by the mouse cursor.'),
+ 'BrowsePointerEnable_name' => __('Highlight pointer'),
+ 'BZipDump_desc' => __(
+ 'Enable bzip2 compression for'
+ . ' import operations.'
+ ),
+ 'BZipDump_name' => __('Bzip2'),
+ 'CharEditing_desc' => __(
+ 'Defines which type of editing controls should be used for CHAR and VARCHAR '
+ . 'columns; [kbd]input[/kbd] - allows limiting of input length, '
+ . '[kbd]textarea[/kbd] - allows newlines in columns.'
+ ),
+ 'CharEditing_name' => __('CHAR columns editing'),
+ 'CodemirrorEnable_desc' => __(
+ 'Use user-friendly editor for editing SQL queries '
+ . '(CodeMirror) with syntax highlighting and '
+ . 'line numbers.'
+ ),
+ 'CodemirrorEnable_name' => __('Enable CodeMirror'),
+ 'LintEnable_desc' => __(
+ 'Find any errors in the query before executing it.'
+ . ' Requires CodeMirror to be enabled.'
+ ),
+ 'LintEnable_name' => __('Enable linter'),
+ 'MinSizeForInputField_desc' => __(
+ 'Defines the minimum size for input fields generated for CHAR and VARCHAR '
+ . 'columns.'
+ ),
+ 'MinSizeForInputField_name' => __('Minimum size for input field'),
+ 'MaxSizeForInputField_desc' => __(
+ 'Defines the maximum size for input fields generated for CHAR and VARCHAR '
+ . 'columns.'
+ ),
+ 'MaxSizeForInputField_name' => __('Maximum size for input field'),
+ 'CharTextareaCols_desc' => __('Number of columns for CHAR/VARCHAR textareas.'),
+ 'CharTextareaCols_name' => __('CHAR textarea columns'),
+ 'CharTextareaRows_desc' => __('Number of rows for CHAR/VARCHAR textareas.'),
+ 'CharTextareaRows_name' => __('CHAR textarea rows'),
+ 'CheckConfigurationPermissions_name' => __('Check config file permissions'),
+ 'CompressOnFly_desc' => __(
+ 'Compress gzip exports on the fly without the need for much memory; if '
+ . 'you encounter problems with created gzip files disable this feature.'
+ ),
+ 'CompressOnFly_name' => __('Compress on the fly'),
+ 'Confirm_desc' => __(
+ 'Whether a warning ("Are your really sure…") should be displayed '
+ . 'when you\'re about to lose data.'
+ ),
+ 'Confirm_name' => __('Confirm DROP queries'),
+ 'DBG_sql_desc' => __('Log SQL queries and their execution time, to be displayed in the console'),
+ 'DBG_sql_name' => __('Debug SQL'),
+ 'DefaultTabDatabase_desc' => __('Tab that is displayed when entering a database.'),
+ 'DefaultTabDatabase_name' => __('Default database tab'),
+ 'DefaultTabServer_desc' => __('Tab that is displayed when entering a server.'),
+ 'DefaultTabServer_name' => __('Default server tab'),
+ 'DefaultTabTable_desc' => __('Tab that is displayed when entering a table.'),
+ 'DefaultTabTable_name' => __('Default table tab'),
+ 'EnableAutocompleteForTablesAndColumns_desc' => __('Autocomplete of the table and column names in the SQL queries.'),
+ 'EnableAutocompleteForTablesAndColumns_name' => __('Enable autocomplete for table and column names'),
+ 'HideStructureActions_desc' => __('Whether the table structure actions should be hidden.'),
+ 'ShowColumnComments_name' => __('Show column comments'),
+ 'ShowColumnComments_desc' => __('Whether column comments should be shown in table structure view'),
+ 'HideStructureActions_name' => __('Hide table structure actions'),
+ 'DefaultTransformations_Hex_name' => __('Default transformations for Hex'),
+ 'DefaultTransformations_Hex_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_Substring_name' => __('Default transformations for Substring'),
+ 'DefaultTransformations_Substring_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_Bool2Text_name' => __('Default transformations for Bool2Text'),
+ 'DefaultTransformations_Bool2Text_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_External_name' => __('Default transformations for External'),
+ 'DefaultTransformations_External_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_PreApPend_name' => __('Default transformations for PreApPend'),
+ 'DefaultTransformations_PreApPend_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_DateFormat_name' => __('Default transformations for DateFormat'),
+ 'DefaultTransformations_DateFormat_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_Inline_name' => __('Default transformations for Inline'),
+ 'DefaultTransformations_Inline_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_TextImageLink_name' => __('Default transformations for TextImageLink'),
+ 'DefaultTransformations_TextImageLink_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+ 'DefaultTransformations_TextLink_name' => __('Default transformations for TextLink'),
+ 'DefaultTransformations_TextLink_desc' => __('Values for options list for default transformations. These will be overwritten if transformation is filled in at table structure page.'),
+
+ 'DisplayServersList_desc' => __('Show server listing as a list instead of a drop down.'),
+ 'DisplayServersList_name' => __('Display servers as a list'),
+ 'DisableMultiTableMaintenance_desc' => __(
+ 'Disable the table maintenance mass operations, like optimizing or repairing '
+ . 'the selected tables of a database.'
+ ),
+ 'DisableMultiTableMaintenance_name' => __('Disable multi table maintenance'),
+ 'ExecTimeLimit_desc' => __(
+ 'Set the number of seconds a script is allowed to run ([kbd]0[/kbd] for no '
+ . 'limit).'
+ ),
+ 'ExecTimeLimit_name' => __('Maximum execution time'),
+ 'Export_lock_tables_name' => sprintf(
+ __('Use %s statement'),
+ htmlspecialchars('<code>LOCK TABLES</code>')
+ ),
+ 'Export_asfile_name' => __('Save as file'),
+ 'Export_charset_name' => __('Character set of the file'),
+ 'Export_codegen_format_name' => __('Format'),
+ 'Export_compression_name' => __('Compression'),
+ 'Export_csv_columns_name' => __('Put columns names in the first row'),
+ 'Export_csv_enclosed_name' => __('Columns enclosed with'),
+ 'Export_csv_escaped_name' => __('Columns escaped with'),
+ 'Export_csv_null_name' => __('Replace NULL with'),
+ 'Export_csv_removeCRLF_name' => __('Remove CRLF characters within columns'),
+ 'Export_csv_separator_name' => __('Columns terminated with'),
+ 'Export_csv_terminated_name' => __('Lines terminated with'),
+ 'Export_excel_columns_name' => __('Put columns names in the first row'),
+ 'Export_excel_edition_name' => __('Excel edition'),
+ 'Export_excel_null_name' => __('Replace NULL with'),
+ 'Export_excel_removeCRLF_name' => __('Remove CRLF characters within columns'),
+ 'Export_file_template_database_name' => __('Database name template'),
+ 'Export_file_template_server_name' => __('Server name template'),
+ 'Export_file_template_table_name' => __('Table name template'),
+ 'Export_format_name' => __('Format'),
+ 'Export_htmlword_columns_name' => __('Put columns names in the first row'),
+ 'Export_htmlword_null_name' => __('Replace NULL with'),
+ 'Export_htmlword_structure_or_data_name' => __('Dump table'),
+ 'Export_latex_caption_name' => __('Include table caption'),
+ 'Export_latex_columns_name' => __('Put columns names in the first row'),
+ 'Export_latex_comments_name' => __('Comments'),
+ 'Export_latex_data_caption_name' => __('Table caption'),
+ 'Export_latex_data_continued_caption_name' => __('Continued table caption'),
+ 'Export_latex_data_label_name' => __('Label key'),
+ 'Export_latex_mime_name' => __('Media (MIME) type'),
+ 'Export_latex_null_name' => __('Replace NULL with'),
+ 'Export_latex_relation_name' => __('Relationships'),
+ 'Export_latex_structure_caption_name' => __('Table caption'),
+ 'Export_latex_structure_continued_caption_name' => __('Continued table caption'),
+ 'Export_latex_structure_label_name' => __('Label key'),
+ 'Export_latex_structure_or_data_name' => __('Dump table'),
+ 'Export_method_name' => __('Export method'),
+ 'Export_ods_columns_name' => __('Put columns names in the first row'),
+ 'Export_ods_null_name' => __('Replace NULL with'),
+ 'Export_odt_columns_name' => __('Put columns names in the first row'),
+ 'Export_odt_comments_name' => __('Comments'),
+ 'Export_odt_mime_name' => __('Media (MIME) type'),
+ 'Export_odt_null_name' => __('Replace NULL with'),
+ 'Export_odt_relation_name' => __('Relationships'),
+ 'Export_odt_structure_or_data_name' => __('Dump table'),
+ 'Export_onserver_name' => __('Save on server'),
+ 'Export_onserver_overwrite_name' => __('Overwrite existing file(s)'),
+ 'Export_as_separate_files_name' => __('Export as separate files'),
+ 'Export_quick_export_onserver_name' => __('Save on server'),
+ 'Export_quick_export_onserver_overwrite_name' => __('Overwrite existing file(s)'),
+ 'Export_remember_file_template_name' => __('Remember file name template'),
+ 'Export_sql_auto_increment_name' => __('Add AUTO_INCREMENT value'),
+ 'Export_sql_backquotes_name' => __('Enclose table and column names with backquotes'),
+ 'Export_sql_compatibility_name' => __('SQL compatibility mode'),
+ 'Export_sql_dates_name' => __('Creation/Update/Check dates'),
+ 'Export_sql_delayed_name' => __('Use delayed inserts'),
+ 'Export_sql_disable_fk_name' => __('Disable foreign key checks'),
+ 'Export_sql_views_as_tables_name' => __('Export views as tables'),
+ 'Export_sql_metadata_name' => __('Export related metadata from phpMyAdmin configuration storage'),
+ 'Export_sql_create_database_name' => sprintf(__('Add %s'), 'CREATE DATABASE / USE'),
+ 'Export_sql_drop_database_name' => sprintf(__('Add %s'), 'DROP DATABASE'),
+ 'Export_sql_drop_table_name' => sprintf(
+ __('Add %s'),
+ 'DROP TABLE / VIEW / PROCEDURE / FUNCTION / EVENT / TRIGGER'
+ ),
+ 'Export_sql_create_table_name' => sprintf(__('Add %s'), 'CREATE TABLE'),
+ 'Export_sql_create_view_name' => sprintf(__('Add %s'), 'CREATE VIEW'),
+ 'Export_sql_create_trigger_name' => sprintf(__('Add %s'), 'CREATE TRIGGER'),
+ 'Export_sql_hex_for_binary_name' => __('Use hexadecimal for BINARY & BLOB'),
+ 'Export_sql_if_not_exists_name' => __(
+ 'Add IF NOT EXISTS (less efficient as indexes will be generated during'
+ . ' table creation)'
+ ),
+ 'Export_sql_view_current_user' => __('Exclude definition of current user'),
+ 'Export_sql_or_replace_view_name' => sprintf(__('%s view'), 'OR REPLACE'),
+ 'Export_sql_ignore_name' => __('Use ignore inserts'),
+ 'Export_sql_include_comments_name' => __('Comments'),
+ 'Export_sql_insert_syntax_name' => __('Syntax to use when inserting data'),
+ 'Export_sql_max_query_size_name' => __('Maximal length of created query'),
+ 'Export_sql_mime_name' => __('Media (MIME) type'),
+ 'Export_sql_procedure_function_name' => sprintf(__('Add %s'), 'CREATE PROCEDURE / FUNCTION / EVENT'),
+ 'Export_sql_relation_name' => __('Relationships'),
+ 'Export_sql_structure_or_data_name' => __('Dump table'),
+ 'Export_sql_type_name' => __('Export type'),
+ 'Export_sql_use_transaction_name' => __('Enclose export in a transaction'),
+ 'Export_sql_utc_time_name' => __('Export time in UTC'),
+ 'Export_texytext_columns_name' => __('Put columns names in the first row'),
+ 'Export_texytext_null_name' => __('Replace NULL with'),
+ 'Export_texytext_structure_or_data_name' => __('Dump table'),
+ 'ForeignKeyDropdownOrder_desc' => __(
+ 'Sort order for items in a foreign-key dropdown box; [kbd]content[/kbd] is '
+ . 'the referenced data, [kbd]id[/kbd] is the key value.'
+ ),
+ 'ForeignKeyDropdownOrder_name' => __('Foreign key dropdown order'),
+ 'ForeignKeyMaxLimit_desc' => __('A dropdown will be used if fewer items are present.'),
+ 'ForeignKeyMaxLimit_name' => __('Foreign key limit'),
+ 'DefaultForeignKeyChecks_desc' => __('Default value for foreign key checks checkbox for some queries.'),
+ 'DefaultForeignKeyChecks_name' => __('Foreign key checks'),
+ 'Form_Browse_name' => __('Browse mode'),
+ 'Form_Browse_desc' => __('Customize browse mode.'),
+ 'Form_CodeGen_name' => 'CodeGen',
+ 'Form_CodeGen_desc' => __('Customize default options.'),
+ 'Form_Csv_name' => __('CSV'),
+ 'Form_Csv_desc' => __('Customize default options.'),
+ 'Form_Developer_name' => __('Developer'),
+ 'Form_Developer_desc' => __('Settings for phpMyAdmin developers.'),
+ 'Form_Edit_name' => __('Edit mode'),
+ 'Form_Edit_desc' => __('Customize edit mode.'),
+ 'Form_Export_defaults_name' => __('Export defaults'),
+ 'Form_Export_defaults_desc' => __('Customize default export options.'),
+ 'Form_General_name' => __('General'),
+ 'Form_General_desc' => __('Set some commonly used options.'),
+ 'Form_Import_defaults_name' => __('Import defaults'),
+ 'Form_Import_defaults_desc' => __('Customize default common import options.'),
+ 'Form_Import_export_name' => __('Import / export'),
+ 'Form_Import_export_desc' => __('Set import and export directories and compression options.'),
+ 'Form_Latex_name' => __('LaTeX'),
+ 'Form_Latex_desc' => __('Customize default options.'),
+ 'Form_Navi_databases_name' => __('Databases'),
+ 'Form_Navi_databases_desc' => __('Databases display options.'),
+ 'Form_Navi_panel_name' => __('Navigation panel'),
+ 'Form_Navi_panel_desc' => __('Customize appearance of the navigation panel.'),
+ 'Form_Navi_tree_name' => __('Navigation tree'),
+ 'Form_Navi_tree_desc' => __('Customize the navigation tree.'),
+ 'Form_Navi_servers_name' => __('Servers'),
+ 'Form_Navi_servers_desc' => __('Servers display options.'),
+ 'Form_Navi_tables_name' => __('Tables'),
+ 'Form_Navi_tables_desc' => __('Tables display options.'),
+ 'Form_Main_panel_name' => __('Main panel'),
+ 'Form_Microsoft_Office_name' => __('Microsoft Office'),
+ 'Form_Microsoft_Office_desc' => __('Customize default options.'),
+ 'Form_Open_Document_name' => 'OpenDocument',
+ 'Form_Open_Document_desc' => __('Customize default options.'),
+ 'Form_Other_core_settings_name' => __('Other core settings'),
+ 'Form_Other_core_settings_desc' => __('Settings that didn\'t fit anywhere else.'),
+ 'Form_Page_titles_name' => __('Page titles'),
+ 'Form_Page_titles_desc' => __(
+ 'Specify browser\'s title bar text. Refer to '
+ . '[doc@faq6-27]documentation[/doc] for magic strings that can be used '
+ . 'to get special values.'
+ ),
+ 'Form_Security_name' => __('Security'),
+ 'Form_Security_desc' => __(
+ 'Please note that phpMyAdmin is just a user interface and its features do not '
+ . 'limit MySQL.'
+ ),
+ 'Form_Server_name' => __('Basic settings'),
+ 'Form_Server_auth_name' => __('Authentication'),
+ 'Form_Server_auth_desc' => __('Authentication settings.'),
+ 'Form_Server_config_name' => __('Server configuration'),
+ 'Form_Server_config_desc' => __(
+ 'Advanced server configuration, do not change these options unless you know '
+ . 'what they are for.'
+ ),
+ 'Form_Server_desc' => __('Enter server connection parameters.'),
+ 'Form_Server_pmadb_name' => __('Configuration storage'),
+ 'Form_Server_pmadb_desc' => __(
+ 'Configure phpMyAdmin configuration storage to gain access to additional '
+ . 'features, see [doc@linked-tables]phpMyAdmin configuration storage[/doc] in '
+ . 'documentation.'
+ ),
+ 'Form_Server_tracking_name' => __('Changes tracking'),
+ 'Form_Server_tracking_desc' => __(
+ 'Tracking of changes made in database. Requires the phpMyAdmin configuration '
+ . 'storage.'
+ ),
+ 'Form_Sql_name' => __('SQL'),
+ 'Form_Sql_box_name' => __('SQL Query box'),
+ 'Form_Sql_box_desc' => __('Customize links shown in SQL Query boxes.'),
+ 'Form_Sql_desc' => __('Customize default options.'),
+ 'Form_Sql_queries_name' => __('SQL queries'),
+ 'Form_Sql_queries_desc' => __('SQL queries settings.'),
+ 'Form_Startup_name' => __('Startup'),
+ 'Form_Startup_desc' => __('Customize startup page.'),
+ 'Form_DbStructure_name' => __('Database structure'),
+ 'Form_DbStructure_desc' => __('Choose which details to show in the database structure (list of tables).'),
+ 'Form_TableStructure_name' => __('Table structure'),
+ 'Form_TableStructure_desc' => __('Settings for the table structure (list of columns).'),
+ 'Form_Tabs_name' => __('Tabs'),
+ 'Form_Tabs_desc' => __('Choose how you want tabs to work.'),
+ 'Form_DisplayRelationalSchema_name' => __('Display relational schema'),
+ 'Form_DisplayRelationalSchema_desc' => '',
+ 'PDFDefaultPageSize_name' => __('Paper size'),
+ 'PDFDefaultPageSize_desc' => '',
+ 'Form_Databases_name' => __('Databases'),
+ 'Form_Text_fields_name' => __('Text fields'),
+ 'Form_Text_fields_desc' => __('Customize text input fields.'),
+ 'Form_Texy_name' => __('Texy! text'),
+ 'Form_Texy_desc' => __('Customize default options'),
+ 'Form_Warnings_name' => __('Warnings'),
+ 'Form_Warnings_desc' => __('Disable some of the warnings shown by phpMyAdmin.'),
+ 'Form_Console_name' => __('Console'),
+ 'GZipDump_desc' => __(
+ 'Enable gzip compression for import '
+ . 'and export operations.'
+ ),
+ 'GZipDump_name' => __('GZip'),
+ 'IconvExtraParams_name' => __('Extra parameters for iconv'),
+ 'IgnoreMultiSubmitErrors_desc' => __(
+ 'If enabled, phpMyAdmin continues computing multiple-statement queries even if '
+ . 'one of the queries failed.'
+ ),
+ 'IgnoreMultiSubmitErrors_name' => __('Ignore multiple statement errors'),
+ 'Import_allow_interrupt_desc' => __(
+ 'Allow interrupt of import in case script detects it is close to time limit. '
+ . 'This might be a good way to import large files, however it can break '
+ . 'transactions.'
+ ),
+ 'enable_drag_drop_import_name' => __('Enable drag and drop import'),
+ 'enable_drag_drop_import_desc' => __('Uncheck the checkbox to disable drag and drop import'),
+ 'Import_allow_interrupt_name' => __('Partial import: allow interrupt'),
+ 'Import_charset_name' => __('Character set of the file'),
+ 'Import_csv_col_names_name' => __('Lines terminated with'),
+ 'Import_csv_enclosed_name' => __('Columns enclosed with'),
+ 'Import_csv_escaped_name' => __('Columns escaped with'),
+ 'Import_csv_ignore_name' => __('Do not abort on INSERT error'),
+ 'Import_csv_replace_name' => __('Add ON DUPLICATE KEY UPDATE'),
+ 'Import_csv_replace_desc' => __('Update data when duplicate keys found on import'),
+ 'Import_csv_terminated_name' => __('Columns terminated with'),
+ 'Import_format_desc' => __(
+ 'Default format; be aware that this list depends on location (database, table) '
+ . 'and only SQL is always available.'
+ ),
+ 'Import_format_name' => __('Format of imported file'),
+ 'Import_ldi_enclosed_name' => __('Columns enclosed with'),
+ 'Import_ldi_escaped_name' => __('Columns escaped with'),
+ 'Import_ldi_ignore_name' => __('Do not abort on INSERT error'),
+ 'Import_ldi_local_option_name' => __('Use LOCAL keyword'),
+ 'Import_ldi_replace_name' => __('Add ON DUPLICATE KEY UPDATE'),
+ 'Import_ldi_replace_desc' => __('Update data when duplicate keys found on import'),
+ 'Import_ldi_terminated_name' => __('Columns terminated with'),
+ 'Import_ods_col_names_name' => __('Column names in first row'),
+ 'Import_ods_empty_rows_name' => __('Do not import empty rows'),
+ 'Import_ods_recognize_currency_name' => __('Import currencies ($5.00 to 5.00)'),
+ 'Import_ods_recognize_percentages_name' => __('Import percentages as proper decimals (12.00% to .12)'),
+ 'Import_skip_queries_desc' => __('Number of queries to skip from start.'),
+ 'Import_skip_queries_name' => __('Partial import: skip queries'),
+ 'Import_sql_compatibility_name' => __('SQL compatibility mode'),
+ 'Import_sql_no_auto_value_on_zero_name' => __('Do not use AUTO_INCREMENT for zero values'),
+ 'Import_sql_read_as_multibytes_name' => __('Read as multibytes'),
+ 'InitialSlidersState_name' => __('Initial state for sliders'),
+ 'InsertRows_desc' => __('How many rows can be inserted at one time.'),
+ 'InsertRows_name' => __('Number of inserted rows'),
+ 'LimitChars_desc' => __('Maximum number of characters shown in any non-numeric column on browse view.'),
+ 'LimitChars_name' => __('Limit column characters'),
+ 'LoginCookieDeleteAll_desc' => __(
+ 'If TRUE, logout deletes cookies for all servers; when set to FALSE, logout '
+ . 'only occurs for the current server. Setting this to FALSE makes it easy to '
+ . 'forget to log out from other servers when connected to multiple servers.'
+ ),
+ 'LoginCookieDeleteAll_name' => __('Delete all cookies on logout'),
+ 'LoginCookieRecall_desc' => __(
+ 'Define whether the previous login should be recalled or not in '
+ . '[kbd]cookie[/kbd] authentication mode.'
+ ),
+ 'LoginCookieRecall_name' => __('Recall user name'),
+ 'LoginCookieStore_desc' => __(
+ 'Defines how long (in seconds) a login cookie should be stored in browser. '
+ . 'The default of 0 means that it will be kept for the existing session only, '
+ . 'and will be deleted as soon as you close the browser window. This is '
+ . 'recommended for non-trusted environments.'
+ ),
+ 'LoginCookieStore_name' => __('Login cookie store'),
+ 'LoginCookieValidity_desc' => __('Define how long (in seconds) a login cookie is valid.'),
+ 'LoginCookieValidity_name' => __('Login cookie validity'),
+ 'LongtextDoubleTextarea_desc' => __('Double size of textarea for LONGTEXT columns.'),
+ 'LongtextDoubleTextarea_name' => __('Bigger textarea for LONGTEXT'),
+ 'MaxCharactersInDisplayedSQL_desc' => __('Maximum number of characters used when a SQL query is displayed.'),
+ 'MaxCharactersInDisplayedSQL_name' => __('Maximum displayed SQL length'),
+ 'MaxDbList_cmt' => __('Users cannot set a higher value'),
+ 'MaxDbList_desc' => __('Maximum number of databases displayed in database list.'),
+ 'MaxDbList_name' => __('Maximum databases'),
+ 'FirstLevelNavigationItems_desc' => __(
+ 'The number of items that can be displayed on each page on the first level'
+ . ' of the navigation tree.'
+ ),
+ 'FirstLevelNavigationItems_name' => __('Maximum items on first level'),
+ 'MaxNavigationItems_desc' => __('The number of items that can be displayed on each page of the navigation tree.'),
+ 'MaxNavigationItems_name' => __('Maximum items in branch'),
+ 'MaxRows_desc' => __(
+ 'Number of rows displayed when browsing a result set. If the result set '
+ . 'contains more rows, "Previous" and "Next" links will be '
+ . 'shown.'
+ ),
+ 'MaxRows_name' => __('Maximum number of rows to display'),
+ 'MaxTableList_cmt' => __('Users cannot set a higher value'),
+ 'MaxTableList_desc' => __('Maximum number of tables displayed in table list.'),
+ 'MaxTableList_name' => __('Maximum tables'),
+ 'MemoryLimit_desc' => __(
+ 'The number of bytes a script is allowed to allocate, eg. [kbd]32M[/kbd] '
+ . '([kbd]-1[/kbd] for no limit and [kbd]0[/kbd] for no change).'
+ ),
+ 'MemoryLimit_name' => __('Memory limit'),
+ 'ShowDatabasesNavigationAsTree_desc' => __('In the navigation panel, replaces the database tree with a selector'),
+ 'ShowDatabasesNavigationAsTree_name' => __('Show databases navigation as tree'),
+ 'NavigationWidth_name' => __('Navigation panel width'),
+ 'NavigationWidth_desc' => __('Set to 0 to collapse navigation panel.'),
+ 'NavigationLinkWithMainPanel_desc' => __('Link with main panel by highlighting the current database or table.'),
+ 'NavigationLinkWithMainPanel_name' => __('Link with main panel'),
+ 'NavigationDisplayLogo_desc' => __('Show logo in navigation panel.'),
+ 'NavigationDisplayLogo_name' => __('Display logo'),
+ 'NavigationLogoLink_desc' => __('URL where logo in the navigation panel will point to.'),
+ 'NavigationLogoLink_name' => __('Logo link URL'),
+ 'NavigationLogoLinkWindow_desc' => __(
+ 'Open the linked page in the main window ([kbd]main[/kbd]) or in a new one '
+ . '([kbd]new[/kbd]).'
+ ),
+ 'NavigationLogoLinkWindow_name' => __('Logo link target'),
+ 'NavigationDisplayServers_desc' => __('Display server choice at the top of the navigation panel.'),
+ 'NavigationDisplayServers_name' => __('Display servers selection'),
+ 'NavigationTreeDefaultTabTable_name' => __('Target for quick access icon'),
+ 'NavigationTreeDefaultTabTable2_name' => __('Target for second quick access icon'),
+ 'NavigationTreeDisplayItemFilterMinimum_desc' => __(
+ 'Defines the minimum number of items (tables, views, routines and events) to '
+ . 'display a filter box.'
+ ),
+ 'NavigationTreeDisplayItemFilterMinimum_name' => __('Minimum number of items to display the filter box'),
+ 'NavigationTreeDisplayDbFilterMinimum_name' => __('Minimum number of databases to display the database filter box'),
+ 'NavigationTreeEnableGrouping_desc' => __(
+ 'Group items in the navigation tree (determined by the separator defined in ' .
+ 'the Databases and Tables tabs above).'
+ ),
+ 'NavigationTreeEnableGrouping_name' => __('Group items in the tree'),
+ 'NavigationTreeDbSeparator_desc' => __('String that separates databases into different tree levels.'),
+ 'NavigationTreeDbSeparator_name' => __('Database tree separator'),
+ 'NavigationTreeTableSeparator_desc' => __('String that separates tables into different tree levels.'),
+ 'NavigationTreeTableSeparator_name' => __('Table tree separator'),
+ 'NavigationTreeTableLevel_name' => __('Maximum table tree depth'),
+ 'NavigationTreePointerEnable_desc' => __('Highlight server under the mouse cursor.'),
+ 'NavigationTreePointerEnable_name' => __('Enable highlighting'),
+ 'NavigationTreeEnableExpansion_desc' => __('Whether to offer the possibility of tree expansion in the navigation panel.'),
+ 'NavigationTreeEnableExpansion_name' => __('Enable navigation tree expansion'),
+ 'NavigationTreeShowTables_name' => __('Show tables in tree'),
+ 'NavigationTreeShowTables_desc' => __('Whether to show tables under database in the navigation tree'),
+ 'NavigationTreeShowViews_name' => __('Show views in tree'),
+ 'NavigationTreeShowViews_desc' => __('Whether to show views under database in the navigation tree'),
+ 'NavigationTreeShowFunctions_name' => __('Show functions in tree'),
+ 'NavigationTreeShowFunctions_desc' => __('Whether to show functions under database in the navigation tree'),
+ 'NavigationTreeShowProcedures_name' => __('Show procedures in tree'),
+ 'NavigationTreeShowProcedures_desc' => __('Whether to show procedures under database in the navigation tree'),
+ 'NavigationTreeShowEvents_name' => __('Show events in tree'),
+ 'NavigationTreeShowEvents_desc' => __('Whether to show events under database in the navigation tree'),
+ 'NavigationTreeAutoexpandSingleDb_name' => __('Expand single database'),
+ 'NavigationTreeAutoexpandSingleDb_desc' => __('Whether to expand single database in the navigation tree automatically.'),
+ 'NumRecentTables_desc' => __('Maximum number of recently used tables; set 0 to disable.'),
+ 'NumFavoriteTables_desc' => __('Maximum number of favorite tables; set 0 to disable.'),
+ 'NumRecentTables_name' => __('Recently used tables'),
+ 'NumFavoriteTables_name' => __('Favorite tables'),
+ 'RowActionLinks_desc' => __('These are Edit, Copy and Delete links.'),
+ 'RowActionLinks_name' => __('Where to show the table row links'),
+ 'RowActionLinksWithoutUnique_desc' => __('Whether to show row links even in the absence of a unique key.'),
+ 'RowActionLinksWithoutUnique_name' => __('Show row links anyway'),
+ 'DisableShortcutKeys_name' => __('Disable shortcut keys'),
+ 'DisableShortcutKeys_desc' => __('Disable shortcut keys'),
+ 'NaturalOrder_desc' => __('Use natural order for sorting table and database names.'),
+ 'NaturalOrder_name' => __('Natural order'),
+ 'TableNavigationLinksMode_desc' => __('Use only icons, only text or both.'),
+ 'TableNavigationLinksMode_name' => __('Table navigation bar'),
+ 'OBGzip_desc' => __('Use GZip output buffering for increased speed in HTTP transfers.'),
+ 'OBGzip_name' => __('GZip output buffering'),
+ 'Order_desc' => __(
+ '[kbd]SMART[/kbd] - i.e. descending order for columns of type TIME, DATE, '
+ . 'DATETIME and TIMESTAMP, ascending order otherwise.'
+ ),
+ 'Order_name' => __('Default sorting order'),
+ 'PersistentConnections_desc' => __('Use persistent connections to MySQL databases.'),
+ 'PersistentConnections_name' => __('Persistent connections'),
+ 'PmaNoRelation_DisableWarning_desc' => __(
+ 'Disable the default warning that is displayed on the database details '
+ . 'Structure page if any of the required tables for the phpMyAdmin '
+ . 'configuration storage could not be found.'
+ ),
+ 'PmaNoRelation_DisableWarning_name' => __('Missing phpMyAdmin configuration storage tables'),
+ 'ReservedWordDisableWarning_desc' => __(
+ 'Disable the default warning that is displayed on the Structure page if column '
+ . 'names in a table are reserved MySQL words.'
+ ),
+ 'ReservedWordDisableWarning_name' => __('MySQL reserved word warning'),
+ 'TabsMode_desc' => __('Use only icons, only text or both.'),
+ 'TabsMode_name' => __('How to display the menu tabs'),
+ 'ActionLinksMode_desc' => __('Use only icons, only text or both.'),
+ 'ActionLinksMode_name' => __('How to display various action links'),
+ 'ProtectBinary_desc' => __('Disallow BLOB and BINARY columns from editing.'),
+ 'ProtectBinary_name' => __('Protect binary columns'),
+ 'QueryHistoryDB_desc' => __(
+ 'Enable if you want DB-based query history (requires phpMyAdmin configuration '
+ . 'storage). If disabled, this utilizes JS-routines to display query history '
+ . '(lost by window close).'
+ ),
+ 'QueryHistoryDB_name' => __('Permanent query history'),
+ 'QueryHistoryMax_cmt' => __('Users cannot set a higher value'),
+ 'QueryHistoryMax_desc' => __('How many queries are kept in history.'),
+ 'QueryHistoryMax_name' => __('Query history length'),
+ 'RecodingEngine_desc' => __('Select which functions will be used for character set conversion.'),
+ 'RecodingEngine_name' => __('Recoding engine'),
+ 'RememberSorting_desc' => __('When browsing tables, the sorting of each table is remembered.'),
+ 'RememberSorting_name' => __('Remember table\'s sorting'),
+ 'TablePrimaryKeyOrder_desc' => __('Default sort order for tables with a primary key.'),
+ 'TablePrimaryKeyOrder_name' => __('Primary key default sort order'),
+ 'RepeatCells_desc' => __('Repeat the headers every X cells, [kbd]0[/kbd] deactivates this feature.'),
+ 'RepeatCells_name' => __('Repeat headers'),
+ 'GridEditing_name' => __('Grid editing: trigger action'),
+ 'RelationalDisplay_name' => __('Relational display'),
+ 'RelationalDisplay_desc' => __('For display Options'),
+ 'SaveCellsAtOnce_name' => __('Grid editing: save all edited cells at once'),
+ 'SaveDir_desc' => __('Directory where exports can be saved on server.'),
+ 'SaveDir_name' => __('Save directory'),
+ 'Servers_AllowDeny_order_desc' => __('Leave blank if not used.'),
+ 'Servers_AllowDeny_order_name' => __('Host authorization order'),
+ 'Servers_AllowDeny_rules_desc' => __('Leave blank for defaults.'),
+ 'Servers_AllowDeny_rules_name' => __('Host authorization rules'),
+ 'Servers_AllowNoPassword_name' => __('Allow logins without a password'),
+ 'Servers_AllowRoot_name' => __('Allow root login'),
+ 'Servers_SessionTimeZone_name' => __('Session timezone'),
+ 'Servers_SessionTimeZone_desc' => __(
+ 'Sets the effective timezone; possibly different than the one from your '
+ . 'database server'
+ ),
+ 'Servers_auth_http_realm_desc' => __('HTTP Basic Auth Realm name to display when doing HTTP Auth.'),
+ 'Servers_auth_http_realm_name' => __('HTTP Realm'),
+ 'Servers_auth_type_desc' => __('Authentication method to use.'),
+ 'Servers_auth_type_name' => __('Authentication type'),
+ 'Servers_bookmarktable_desc' => __(
+ 'Leave blank for no [doc@bookmarks@]bookmark[/doc] '
+ . 'support, suggested: [kbd]pma__bookmark[/kbd]'
+ ),
+ 'Servers_bookmarktable_name' => __('Bookmark table'),
+ 'Servers_column_info_desc' => __(
+ 'Leave blank for no column comments/media (MIME) types, suggested: '
+ . '[kbd]pma__column_info[/kbd].'
+ ),
+ 'Servers_column_info_name' => __('Column information table'),
+ 'Servers_compress_desc' => __('Compress connection to MySQL server.'),
+ 'Servers_compress_name' => __('Compress connection'),
+ 'Servers_controlpass_name' => __('Control user password'),
+ 'Servers_controluser_desc' => __(
+ 'A special MySQL user configured with limited permissions, more information '
+ . 'available on [doc@linked-tables]documentation[/doc].'
+ ),
+ 'Servers_controluser_name' => __('Control user'),
+ 'Servers_controlhost_desc' => __(
+ 'An alternate host to hold the configuration storage; leave blank to use the '
+ . 'already defined host.'
+ ),
+ 'Servers_controlhost_name' => __('Control host'),
+ 'Servers_controlport_desc' => __(
+ 'An alternate port to connect to the host that holds the configuration storage; '
+ . 'leave blank to use the default port, or the already defined port, if the '
+ . 'controlhost equals host.'
+ ),
+ 'Servers_controlport_name' => __('Control port'),
+ 'Servers_hide_db_desc' => __('Hide databases matching regular expression (PCRE).'),
+ 'Servers_DisableIS_desc' => __(
+ 'More information on [a@https://github.com/phpmyadmin/phpmyadmin/issues/8970]phpMyAdmin '
+ . 'issue tracker[/a] and [a@https://bugs.mysql.com/19588]MySQL Bugs[/a]'
+ ),
+ 'Servers_DisableIS_name' => __('Disable use of INFORMATION_SCHEMA'),
+ 'Servers_hide_db_name' => __('Hide databases'),
+ 'Servers_history_desc' => __(
+ 'Leave blank for no SQL query history support, suggested: '
+ . '[kbd]pma__history[/kbd].'
+ ),
+ 'Servers_history_name' => __('SQL query history table'),
+ 'Servers_host_desc' => __('Hostname where MySQL server is running.'),
+ 'Servers_host_name' => __('Server hostname'),
+ 'Servers_LogoutURL_name' => __('Logout URL'),
+ 'Servers_MaxTableUiprefs_desc' => __(
+ 'Limits number of table preferences which are stored in database, the oldest '
+ . 'records are automatically removed.'
+ ),
+ 'Servers_MaxTableUiprefs_name' => __('Maximal number of table preferences to store'),
+ 'Servers_savedsearches_name' => __('QBE saved searches table'),
+ 'Servers_savedsearches_desc' => __(
+ 'Leave blank for no QBE saved searches support, suggested: '
+ . '[kbd]pma__savedsearches[/kbd].'
+ ),
+ 'Servers_export_templates_name' => __('Export templates table'),
+ 'Servers_export_templates_desc' => __(
+ 'Leave blank for no export template support, suggested: '
+ . '[kbd]pma__export_templates[/kbd].'
+ ),
+ 'Servers_central_columns_name' => __('Central columns table'),
+ 'Servers_central_columns_desc' => __(
+ 'Leave blank for no central columns support, suggested: '
+ . '[kbd]pma__central_columns[/kbd].'
+ ),
+ 'Servers_only_db_desc' => __(
+ 'You can use MySQL wildcard characters (% and _), escape them if you want to '
+ . 'use their literal instances, i.e. use [kbd]\'my\_db\'[/kbd] and not '
+ . '[kbd]\'my_db\'[/kbd].'
+ ),
+ 'Servers_only_db_name' => __('Show only listed databases'),
+ 'Servers_password_desc' => __('Leave empty if not using config auth.'),
+ 'Servers_password_name' => __('Password for config auth'),
+ 'Servers_pdf_pages_desc' => __('Leave blank for no PDF schema support, suggested: [kbd]pma__pdf_pages[/kbd].'),
+ 'Servers_pdf_pages_name' => __('PDF schema: pages table'),
+ 'Servers_pmadb_desc' => __(
+ 'Database used for relations, bookmarks, and PDF features. See '
+ . '[doc@linked-tables]pmadb[/doc] for complete information. '
+ . 'Leave blank for no support. Suggested: [kbd]phpmyadmin[/kbd].'
+ ),
+ 'Servers_pmadb_name' => __('Database name'),
+ 'Servers_port_desc' => __('Port on which MySQL server is listening, leave empty for default.'),
+ 'Servers_port_name' => __('Server port'),
+ 'Servers_recent_desc' => __(
+ 'Leave blank for no "persistent" recently used tables across sessions, '
+ . 'suggested: [kbd]pma__recent[/kbd].'
+ ),
+ 'Servers_recent_name' => __('Recently used table'),
+ 'Servers_favorite_desc' => __(
+ 'Leave blank for no "persistent" favorite tables across sessions, '
+ . 'suggested: [kbd]pma__favorite[/kbd].'
+ ),
+ 'Servers_favorite_name' => __('Favorites table'),
+ 'Servers_relation_desc' => __(
+ 'Leave blank for no '
+ . '[doc@relations@]relation-links[/doc] support, '
+ . 'suggested: [kbd]pma__relation[/kbd].'
+ ),
+ 'Servers_relation_name' => __('Relation table'),
+ 'Servers_SignonSession_desc' => __(
+ 'See [doc@authentication-modes]authentication '
+ . 'types[/doc] for an example.'
+ ),
+ 'Servers_SignonSession_name' => __('Signon session name'),
+ 'Servers_SignonURL_name' => __('Signon URL'),
+ 'Servers_socket_desc' => __('Socket on which MySQL server is listening, leave empty for default.'),
+ 'Servers_socket_name' => __('Server socket'),
+ 'Servers_ssl_desc' => __('Enable SSL for connection to MySQL server.'),
+ 'Servers_ssl_name' => __('Use SSL'),
+ 'Servers_table_coords_desc' => __('Leave blank for no PDF schema support, suggested: [kbd]pma__table_coords[/kbd].'),
+ 'Servers_table_coords_name' => __('Designer and PDF schema: table coordinates'),
+ 'Servers_table_info_desc' => __(
+ 'Table to describe the display columns, leave blank for no support; '
+ . 'suggested: [kbd]pma__table_info[/kbd].'
+ ),
+ 'Servers_table_info_name' => __('Display columns table'),
+ 'Servers_table_uiprefs_desc' => __(
+ 'Leave blank for no "persistent" tables\' UI preferences across sessions, '
+ . 'suggested: [kbd]pma__table_uiprefs[/kbd].'
+ ),
+ 'Servers_table_uiprefs_name' => __('UI preferences table'),
+ 'Servers_tracking_add_drop_database_desc' => __(
+ 'Whether a DROP DATABASE IF EXISTS statement will be added as first line to '
+ . 'the log when creating a database.'
+ ),
+ 'Servers_tracking_add_drop_database_name' => __('Add DROP DATABASE'),
+ 'Servers_tracking_add_drop_table_desc' => __(
+ 'Whether a DROP TABLE IF EXISTS statement will be added as first line to the '
+ . 'log when creating a table.'
+ ),
+ 'Servers_tracking_add_drop_table_name' => __('Add DROP TABLE'),
+ 'Servers_tracking_add_drop_view_desc' => __(
+ 'Whether a DROP VIEW IF EXISTS statement will be added as first line to the '
+ . 'log when creating a view.'
+ ),
+ 'Servers_tracking_add_drop_view_name' => __('Add DROP VIEW'),
+ 'Servers_tracking_default_statements_desc' => __('Defines the list of statements the auto-creation uses for new versions.'),
+ 'Servers_tracking_default_statements_name' => __('Statements to track'),
+ 'Servers_tracking_desc' => __(
+ 'Leave blank for no SQL query tracking support, suggested: '
+ . '[kbd]pma__tracking[/kbd].'
+ ),
+ 'Servers_tracking_name' => __('SQL query tracking table'),
+ 'Servers_tracking_version_auto_create_desc' => __(
+ 'Whether the tracking mechanism creates versions for tables and views '
+ . 'automatically.'
+ ),
+ 'Servers_tracking_version_auto_create_name' => __('Automatically create versions'),
+ 'Servers_userconfig_desc' => __(
+ 'Leave blank for no user preferences storage in database, suggested: '
+ . '[kbd]pma__userconfig[/kbd].'
+ ),
+ 'Servers_userconfig_name' => __('User preferences storage table'),
+ 'Servers_users_desc' => __(
+ 'Both this table and the user groups table are required to enable the ' .
+ 'configurable menus feature; leaving either one of them blank will disable ' .
+ 'this feature, suggested: [kbd]pma__users[/kbd].'
+ ),
+ 'Servers_users_name' => __('Users table'),
+ 'Servers_usergroups_desc' => __(
+ 'Both this table and the users table are required to enable the configurable ' .
+ 'menus feature; leaving either one of them blank will disable this feature, ' .
+ 'suggested: [kbd]pma__usergroups[/kbd].'
+ ),
+ 'Servers_usergroups_name' => __('User groups table'),
+ 'Servers_navigationhiding_desc' => __(
+ 'Leave blank to disable the feature to hide and show navigation items, ' .
+ 'suggested: [kbd]pma__navigationhiding[/kbd].'
+ ),
+ 'Servers_navigationhiding_name' => __('Hidden navigation items table'),
+ 'Servers_user_desc' => __('Leave empty if not using config auth.'),
+ 'Servers_user_name' => __('User for config auth'),
+ 'Servers_verbose_desc' => __(
+ 'A user-friendly description of this server. Leave blank to display the ' .
+ 'hostname instead.'
+ ),
+ 'Servers_verbose_name' => __('Verbose name of this server'),
+ 'ShowAll_desc' => __('Whether a user should be displayed a "show all (rows)" button.'),
+ 'ShowAll_name' => __('Allow to display all the rows'),
+ 'ShowChgPassword_desc' => __(
+ 'Please note that enabling this has no effect with [kbd]config[/kbd] ' .
+ 'authentication mode because the password is hard coded in the configuration ' .
+ 'file; this does not limit the ability to execute the same command directly.'
+ ),
+ 'ShowChgPassword_name' => __('Show password change form'),
+ 'ShowCreateDb_name' => __('Show create database form'),
+ 'ShowDbStructureComment_desc' => __('Show or hide a column displaying the comments for all tables.'),
+ 'ShowDbStructureComment_name' => __('Show table comments'),
+ 'ShowDbStructureCreation_desc' => __('Show or hide a column displaying the Creation timestamp for all tables.'),
+ 'ShowDbStructureCreation_name' => __('Show creation timestamp'),
+ 'ShowDbStructureLastUpdate_desc' => __('Show or hide a column displaying the Last update timestamp for all tables.'),
+ 'ShowDbStructureLastUpdate_name' => __('Show last update timestamp'),
+ 'ShowDbStructureLastCheck_desc' => __('Show or hide a column displaying the Last check timestamp for all tables.'),
+ 'ShowDbStructureLastCheck_name' => __('Show last check timestamp'),
+ 'ShowDbStructureCharset_desc' => __('Show or hide a column displaying the charset for all tables.'),
+ 'ShowDbStructureCharset_name' => __('Show table charset'),
+ 'ShowFieldTypesInDataEditView_desc' => __(
+ 'Defines whether or not type fields should be initially displayed in ' .
+ 'edit/insert mode.'
+ ),
+ 'ShowFieldTypesInDataEditView_name' => __('Show field types'),
+ 'ShowFunctionFields_desc' => __('Display the function fields in edit/insert mode.'),
+ 'ShowFunctionFields_name' => __('Show function fields'),
+ 'ShowHint_desc' => __('Whether to show hint or not.'),
+ 'ShowHint_name' => __('Show hint'),
+ 'ShowPhpInfo_desc' => __(
+ 'Shows link to [a@https://php.net/manual/function.phpinfo.php]phpinfo()[/a] ' .
+ 'output.'
+ ),
+ 'ShowPhpInfo_name' => __('Show phpinfo() link'),
+ 'ShowServerInfo_name' => __('Show detailed MySQL server information'),
+ 'ShowSQL_desc' => __('Defines whether SQL queries generated by phpMyAdmin should be displayed.'),
+ 'ShowSQL_name' => __('Show SQL queries'),
+ 'RetainQueryBox_desc' => __('Defines whether the query box should stay on-screen after its submission.'),
+ 'RetainQueryBox_name' => __('Retain query box'),
+ 'ShowStats_desc' => __('Allow to display database and table statistics (eg. space usage).'),
+ 'ShowStats_name' => __('Show statistics'),
+ 'SkipLockedTables_desc' => __('Mark used tables and make it possible to show databases with locked tables.'),
+ 'SkipLockedTables_name' => __('Skip locked tables'),
+ 'SQLQuery_Edit_name' => __('Edit'),
+ 'SQLQuery_Explain_name' => __('Explain SQL'),
+ 'SQLQuery_Refresh_name' => __('Refresh'),
+ 'SQLQuery_ShowAsPHP_name' => __('Create PHP code'),
+ 'SuhosinDisableWarning_desc' => __(
+ 'Disable the default warning that is displayed on the main page if Suhosin is ' .
+ 'detected.'
+ ),
+ 'SuhosinDisableWarning_name' => __('Suhosin warning'),
+ 'LoginCookieValidityDisableWarning_desc' => __(
+ 'Disable the default warning that is displayed on the main page if the value ' .
+ 'of the PHP setting session.gc_maxlifetime is less than the value of ' .
+ '`LoginCookieValidity`.'
+ ),
+ 'LoginCookieValidityDisableWarning_name' => __('Login cookie validity warning'),
+ 'TextareaCols_desc' => __(
+ 'Textarea size (columns) in edit mode, this value will be emphasized for SQL ' .
+ 'query textareas (*2).'
+ ),
+ 'TextareaCols_name' => __('Textarea columns'),
+ 'TextareaRows_desc' => __(
+ 'Textarea size (rows) in edit mode, this value will be emphasized for SQL ' .
+ 'query textareas (*2).'
+ ),
+ 'TextareaRows_name' => __('Textarea rows'),
+ 'TitleDatabase_desc' => __('Title of browser window when a database is selected.'),
+ 'TitleDatabase_name' => __('Database'),
+ 'TitleDefault_desc' => __('Title of browser window when nothing is selected.'),
+ 'TitleDefault_name' => __('Default title'),
+ 'TitleServer_desc' => __('Title of browser window when a server is selected.'),
+ 'TitleServer_name' => __('Server'),
+ 'TitleTable_desc' => __('Title of browser window when a table is selected.'),
+ 'TitleTable_name' => __('Table'),
+ 'TrustedProxies_desc' => __(
+ 'Input proxies as [kbd]IP: trusted HTTP header[/kbd]. The following example ' .
+ 'specifies that phpMyAdmin should trust a HTTP_X_FORWARDED_FOR ' .
+ '(X-Forwarded-For) header coming from the proxy 1.2.3.4:[br][kbd]1.2.3.4: ' .
+ 'HTTP_X_FORWARDED_FOR[/kbd].'
+ ),
+ 'TrustedProxies_name' => __('List of trusted proxies for IP allow/deny'),
+ 'UploadDir_desc' => __('Directory on server where you can upload files for import.'),
+ 'UploadDir_name' => __('Upload directory'),
+ 'UseDbSearch_desc' => __('Allow for searching inside the entire database.'),
+ 'UseDbSearch_name' => __('Use database search'),
+ 'UserprefsDeveloperTab_desc' => __(
+ 'When disabled, users cannot set any of the options below, regardless of the ' .
+ 'checkbox on the right.'
+ ),
+ 'UserprefsDeveloperTab_name' => __('Enable the Developer tab in settings'),
+ 'VersionCheck_desc' => __('Enables check for latest version on main phpMyAdmin page.'),
+ 'VersionCheck_name' => __('Version check'),
+ 'ProxyUrl_desc' => __(
+ 'The url of the proxy to be used when retrieving the information about the ' .
+ 'latest version of phpMyAdmin or when submitting error reports. You need this ' .
+ 'if the server where phpMyAdmin is installed does not have direct access to ' .
+ 'the internet. The format is: "hostname:portnumber".'
+ ),
+ 'ProxyUrl_name' => __('Proxy url'),
+ 'ProxyUser_desc' => __(
+ 'The username for authenticating with the proxy. By default, no ' .
+ 'authentication is performed. If a username is supplied, Basic ' .
+ 'Authentication will be performed. No other types of authentication are ' .
+ 'currently supported.'
+ ),
+ 'ProxyUser_name' => __('Proxy username'),
+ 'ProxyPass_desc' => __('The password for authenticating with the proxy.'),
+ 'ProxyPass_name' => __('Proxy password'),
+
+ 'ZipDump_desc' => __('Enable ZIP compression for import and export operations.'),
+ 'ZipDump_name' => __('ZIP'),
+ 'CaptchaLoginPublicKey_desc' => __('Enter your public key for your domain reCaptcha service.'),
+ 'CaptchaLoginPublicKey_name' => __('Public key for reCaptcha'),
+ 'CaptchaLoginPrivateKey_desc' => __('Enter your private key for your domain reCaptcha service.'),
+ 'CaptchaLoginPrivateKey_name' => __('Private key for reCaptcha'),
+
+ 'SendErrorReports_desc' => __('Choose the default action when sending error reports.'),
+ 'SendErrorReports_name' => __('Send error reports'),
+
+ 'ConsoleEnterExecutes_desc' => __(
+ 'Queries are executed by pressing Enter (instead of Ctrl+Enter). New lines ' .
+ 'will be inserted with Shift+Enter.'
+ ),
+ 'ConsoleEnterExecutes_name' => __('Enter executes queries in console'),
+
+ 'ZeroConf_desc' => __(
+ 'Enable Zero Configuration mode which lets you setup phpMyAdmin '
+ . 'configuration storage tables automatically.'
+ ),
+ 'ZeroConf_name' => __('Enable Zero Configuration mode'),
+ 'Console_StartHistory_name' => __('Show query history at start'),
+ 'Console_AlwaysExpand_name' => __('Always expand query messages'),
+ 'Console_CurrentQuery_name' => __('Show current browsing query'),
+ 'Console_EnterExecutes_name' => __('Execute queries on Enter and insert new line with Shift + Enter'),
+ 'Console_DarkTheme_name' => __('Switch to dark theme'),
+ 'Console_Height_name' => __('Console height'),
+ 'Console_Mode_name' => __('Console mode'),
+ 'Console_GroupQueries_name' => __('Group queries'),
+ 'Console_Order_name' => __('Order'),
+ 'Console_OrderBy_name' => __('Order by'),
+ 'DefaultConnectionCollation_name' => __('Server connection collation'),
+ ];
+
+ $key = $path . '_' . $type;
+
+ return $descriptions[$key] ?? null;
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Form.php b/srcs/phpmyadmin/libraries/classes/Config/Form.php
new file mode 100644
index 0000000..8c1cbc5
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Form.php
@@ -0,0 +1,238 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Form handling code.
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config\ConfigFile;
+
+/**
+ * Base class for forms, loads default configuration options, checks allowed
+ * values etc.
+ *
+ * @package PhpMyAdmin
+ */
+class Form
+{
+ /**
+ * Form name
+ * @var string
+ */
+ public $name;
+
+ /**
+ * Arbitrary index, doesn't affect class' behavior
+ * @var int
+ */
+ public $index;
+
+ /**
+ * Form fields (paths), filled by {@link readFormPaths()}, indexed by field name
+ * @var array
+ */
+ public $fields;
+
+ /**
+ * Stores default values for some fields (eg. pmadb tables)
+ * @var array
+ */
+ public $default;
+
+ /**
+ * Caches field types, indexed by field names
+ * @var array
+ */
+ private $_fieldsTypes;
+
+ /**
+ * ConfigFile instance
+ * @var ConfigFile
+ */
+ private $_configFile;
+
+ /**
+ * Constructor, reads default config values
+ *
+ * @param string $formName Form name
+ * @param array $form Form data
+ * @param ConfigFile $cf Config file instance
+ * @param int $index arbitrary index, stored in Form::$index
+ */
+ public function __construct(
+ $formName,
+ array $form,
+ ConfigFile $cf,
+ $index = null
+ ) {
+ $this->index = $index;
+ $this->_configFile = $cf;
+ $this->loadForm($formName, $form);
+ }
+
+ /**
+ * Returns type of given option
+ *
+ * @param string $optionName path or field name
+ *
+ * @return string|null one of: boolean, integer, double, string, select, array
+ */
+ public function getOptionType($optionName)
+ {
+ $key = ltrim(
+ mb_substr(
+ $optionName,
+ (int) mb_strrpos($optionName, '/')
+ ),
+ '/'
+ );
+ return isset($this->_fieldsTypes[$key])
+ ? $this->_fieldsTypes[$key]
+ : null;
+ }
+
+ /**
+ * Returns allowed values for select fields
+ *
+ * @param string $optionPath Option path
+ *
+ * @return array
+ */
+ public function getOptionValueList($optionPath)
+ {
+ $value = $this->_configFile->getDbEntry($optionPath);
+ if ($value === null) {
+ trigger_error("$optionPath - select options not defined", E_USER_ERROR);
+ return [];
+ }
+ if (! is_array($value)) {
+ trigger_error("$optionPath - not a static value list", E_USER_ERROR);
+ return [];
+ }
+ // convert array('#', 'a', 'b') to array('a', 'b')
+ if (isset($value[0]) && $value[0] === '#') {
+ // remove first element ('#')
+ array_shift($value);
+ // $value has keys and value names, return it
+ return $value;
+ }
+
+ // convert value list array('a', 'b') to array('a' => 'a', 'b' => 'b')
+ $hasStringKeys = false;
+ $keys = [];
+ for ($i = 0, $nb = count($value); $i < $nb; $i++) {
+ if (! isset($value[$i])) {
+ $hasStringKeys = true;
+ break;
+ }
+ $keys[] = is_bool($value[$i]) ? (int) $value[$i] : $value[$i];
+ }
+ if (! $hasStringKeys) {
+ $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 docs for \PhpMyAdmin\Config\Forms\BaseForm::getForms)
+ *
+ * @param mixed $value Value
+ * @param mixed $key Key
+ * @param mixed $prefix Prefix
+ *
+ * @return void
+ */
+ private function _readFormPathsCallback($value, $key, $prefix)
+ {
+ static $groupCounter = 0;
+
+ if (is_array($value)) {
+ $prefix .= $key . '/';
+ array_walk($value, [$this, '_readFormPathsCallback'], $prefix);
+ return;
+ }
+
+ if (! is_int($key)) {
+ $this->default[$prefix . $key] = $value;
+ $value = $key;
+ }
+ // add unique id to group ends
+ if ($value == ':group:end') {
+ $value .= ':' . $groupCounter++;
+ }
+ $this->fields[] = $prefix . $value;
+ }
+
+ /**
+ * Reads form paths to {@link $fields}
+ *
+ * @param array $form Form
+ *
+ * @return void
+ */
+ protected function readFormPaths(array $form)
+ {
+ // flatten form fields' paths and save them to $fields
+ $this->fields = [];
+ array_walk($form, [$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 = [];
+ foreach ($paths as $path) {
+ $key = ltrim(
+ mb_substr($path, (int) mb_strrpos($path, '/')),
+ '/'
+ );
+ $this->fields[$key] = $path;
+ }
+ // now $this->fields is an array of the form: 'field name' => 'field path'
+ }
+
+ /**
+ * Reads fields' types to $this->_fieldsTypes
+ *
+ * @return void
+ */
+ protected function readTypes()
+ {
+ $cf = $this->_configFile;
+ foreach ($this->fields as $name => $path) {
+ if (mb_strpos((string) $name, ':group:') === 0) {
+ $this->_fieldsTypes[$name] = 'group';
+ continue;
+ }
+ $v = $cf->getDbEntry($path);
+ if ($v !== null) {
+ $type = is_array($v) ? 'select' : $v;
+ } else {
+ $type = gettype($cf->getDefault($path));
+ }
+ $this->_fieldsTypes[$name] = $type;
+ }
+ }
+
+ /**
+ * Reads form settings and prepares class to work with given subset of
+ * config file
+ *
+ * @param string $formName Form name
+ * @param array $form Form
+ *
+ * @return void
+ */
+ public function loadForm($formName, array $form)
+ {
+ $this->name = $formName;
+ $this->readFormPaths($form);
+ $this->readTypes();
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/FormDisplay.php b/srcs/phpmyadmin/libraries/classes/Config/FormDisplay.php
new file mode 100644
index 0000000..500706c
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/FormDisplay.php
@@ -0,0 +1,924 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Form management class, displays and processes forms
+ *
+ * Explanation of used terms:
+ * o work_path - original field path, eg. Servers/4/verbose
+ * o system_path - work_path modified so that it points to the first server,
+ * eg. Servers/1/verbose
+ * o translated_path - work_path modified for HTML field name, a path with
+ * slashes changed to hyphens, eg. Servers-4-verbose
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config\Forms\User\UserFormList;
+use PhpMyAdmin\Sanitize;
+use PhpMyAdmin\Util;
+
+/**
+ * Form management class, displays and processes forms
+ *
+ * @package PhpMyAdmin
+ */
+class FormDisplay
+{
+ /**
+ * ConfigFile instance
+ * @var ConfigFile
+ */
+ private $_configFile;
+
+ /**
+ * Form list
+ * @var Form[]
+ */
+ private $_forms = [];
+
+ /**
+ * Stores validation errors, indexed by paths
+ * [ Form_name ] is an array of form errors
+ * [path] is a string storing error associated with single field
+ * @var array
+ */
+ private $_errors = [];
+
+ /**
+ * Paths changed so that they can be used as HTML ids, indexed by paths
+ * @var array
+ */
+ private $_translatedPaths = [];
+
+ /**
+ * Server paths change indexes so we define maps from current server
+ * path to the first one, indexed by work path
+ * @var array
+ */
+ private $_systemPaths = [];
+
+ /**
+ * Language strings which will be sent to Messages JS variable
+ * Will be looked up in $GLOBALS: str{value} or strSetup{value}
+ * @var array
+ */
+ private $_jsLangStrings = [];
+
+ /**
+ * Tells whether forms have been validated
+ * @var bool
+ */
+ private $_isValidated = true;
+
+ /**
+ * Dictionary with user preferences keys
+ * @var array|null
+ */
+ private $_userprefsKeys;
+
+ /**
+ * Dictionary with disallowed user preferences keys
+ * @var array
+ */
+ private $_userprefsDisallow;
+
+ /**
+ * @var FormDisplayTemplate
+ */
+ private $formDisplayTemplate;
+
+ /**
+ * Constructor
+ *
+ * @param ConfigFile $cf Config file instance
+ */
+ public function __construct(ConfigFile $cf)
+ {
+ $this->formDisplayTemplate = new FormDisplayTemplate($GLOBALS['PMA_Config']);
+ $this->_jsLangStrings = [
+ '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 less than or equal to %s!'),
+ ];
+ $this->_configFile = $cf;
+ // initialize validators
+ Validator::getValidators($this->_configFile);
+ }
+
+ /**
+ * Returns {@link ConfigFile} associated with this instance
+ *
+ * @return ConfigFile
+ */
+ public function getConfigFile()
+ {
+ return $this->_configFile;
+ }
+
+ /**
+ * Registers form in form manager
+ *
+ * @param string $formName Form name
+ * @param array $form Form data
+ * @param int $serverId 0 if new server, validation; >= 1 if editing a server
+ *
+ * @return void
+ */
+ public function registerForm($formName, array $form, $serverId = null)
+ {
+ $this->_forms[$formName] = new Form(
+ $formName,
+ $form,
+ $this->_configFile,
+ $serverId
+ );
+ $this->_isValidated = false;
+ foreach ($this->_forms[$formName]->fields as $path) {
+ $workPath = $serverId === null
+ ? $path
+ : str_replace('Servers/1/', "Servers/$serverId/", $path);
+ $this->_systemPaths[$workPath] = $path;
+ $this->_translatedPaths[$workPath] = str_replace('/', '-', $workPath);
+ }
+ }
+
+ /**
+ * Processes forms, returns true on successful save
+ *
+ * @param bool $allowPartialSave allows for partial form saving
+ * on failed validation
+ * @param bool $checkFormSubmit whether check for $_POST['submit_save']
+ *
+ * @return boolean whether processing was successful
+ */
+ public function process($allowPartialSave = true, $checkFormSubmit = true)
+ {
+ if ($checkFormSubmit && ! isset($_POST['submit_save'])) {
+ return false;
+ }
+
+ // save forms
+ if (count($this->_forms) > 0) {
+ return $this->save(array_keys($this->_forms), $allowPartialSave);
+ }
+ return false;
+ }
+
+ /**
+ * Runs validation for all registered forms
+ *
+ * @return void
+ */
+ private function _validate()
+ {
+ if ($this->_isValidated) {
+ return;
+ }
+
+ $paths = [];
+ $values = [];
+ foreach ($this->_forms as $form) {
+ /** @var Form $form */
+ $paths[] = $form->name;
+ // collect values and paths
+ foreach ($form->fields as $path) {
+ $workPath = array_search($path, $this->_systemPaths);
+ $values[$path] = $this->_configFile->getValue($workPath);
+ $paths[] = $path;
+ }
+ }
+
+ // run validation
+ $errors = Validator::validate(
+ $this->_configFile,
+ $paths,
+ $values,
+ false
+ );
+
+ // change error keys from canonical paths to work paths
+ if (is_array($errors) && count($errors) > 0) {
+ $this->_errors = [];
+ foreach ($errors as $path => $errorList) {
+ $workPath = array_search($path, $this->_systemPaths);
+ // field error
+ if (! $workPath) {
+ // form error, fix path
+ $workPath = $path;
+ }
+ $this->_errors[$workPath] = $errorList;
+ }
+ }
+ $this->_isValidated = true;
+ }
+
+ /**
+ * Outputs HTML for the forms under the menu tab
+ *
+ * @param bool $showRestoreDefault whether to show "restore default"
+ * button besides the input field
+ * @param array $jsDefault stores JavaScript code
+ * to be displayed
+ * @param array $js will be updated with javascript code
+ * @param bool $showButtons whether show submit and reset button
+ *
+ * @return string
+ */
+ private function _displayForms(
+ $showRestoreDefault,
+ array &$jsDefault,
+ array &$js,
+ $showButtons
+ ) {
+ $htmlOutput = '';
+ $validators = Validator::getValidators($this->_configFile);
+
+ foreach ($this->_forms as $form) {
+ /** @var Form $form */
+ $formErrors = isset($this->_errors[$form->name])
+ ? $this->_errors[$form->name] : null;
+ $htmlOutput .= $this->formDisplayTemplate->displayFieldsetTop(
+ Descriptions::get("Form_{$form->name}"),
+ Descriptions::get("Form_{$form->name}", 'desc'),
+ $formErrors,
+ ['id' => $form->name]
+ );
+
+ foreach ($form->fields as $field => $path) {
+ $workPath = array_search($path, $this->_systemPaths);
+ $translatedPath = $this->_translatedPaths[$workPath];
+ // always true/false for user preferences display
+ // otherwise null
+ $userPrefsAllow = isset($this->_userprefsKeys[$path])
+ ? ! isset($this->_userprefsDisallow[$path])
+ : null;
+ // display input
+ $htmlOutput .= $this->_displayFieldInput(
+ $form,
+ $field,
+ $path,
+ $workPath,
+ $translatedPath,
+ $showRestoreDefault,
+ $userPrefsAllow,
+ $jsDefault
+ );
+ // register JS validators for this field
+ if (isset($validators[$path])) {
+ $this->formDisplayTemplate->addJsValidate($translatedPath, $validators[$path], $js);
+ }
+ }
+ $htmlOutput .= $this->formDisplayTemplate->displayFieldsetBottom($showButtons);
+ }
+ return $htmlOutput;
+ }
+
+ /**
+ * Outputs HTML for forms
+ *
+ * @param bool $tabbedForm if true, use a form with tabs
+ * @param bool $showRestoreDefault whether show "restore default" button
+ * besides the input field
+ * @param bool $showButtons whether show submit and reset button
+ * @param string $formAction action attribute for the form
+ * @param array|null $hiddenFields array of form hidden fields (key: field
+ * name)
+ *
+ * @return string HTML for forms
+ */
+ public function getDisplay(
+ $tabbedForm = false,
+ $showRestoreDefault = false,
+ $showButtons = true,
+ $formAction = null,
+ $hiddenFields = null
+ ) {
+ static $jsLangSent = false;
+
+ $htmlOutput = '';
+
+ $js = [];
+ $jsDefault = [];
+
+ $htmlOutput .= $this->formDisplayTemplate->displayFormTop($formAction, 'post', $hiddenFields);
+
+ if ($tabbedForm) {
+ $tabs = [];
+ foreach ($this->_forms as $form) {
+ $tabs[$form->name] = Descriptions::get("Form_$form->name");
+ }
+ $htmlOutput .= $this->formDisplayTemplate->displayTabsTop($tabs);
+ }
+
+ // validate only when we aren't displaying a "new server" form
+ $isNewServer = false;
+ foreach ($this->_forms as $form) {
+ /** @var Form $form */
+ if ($form->index === 0) {
+ $isNewServer = true;
+ break;
+ }
+ }
+ if (! $isNewServer) {
+ $this->_validate();
+ }
+
+ // user preferences
+ $this->_loadUserprefsInfo();
+
+ // display forms
+ $htmlOutput .= $this->_displayForms(
+ $showRestoreDefault,
+ $jsDefault,
+ $js,
+ $showButtons
+ );
+
+ if ($tabbedForm) {
+ $htmlOutput .= $this->formDisplayTemplate->displayTabsBottom();
+ }
+ $htmlOutput .= $this->formDisplayTemplate->displayFormBottom();
+
+ // if not already done, send strings used for validation to JavaScript
+ if (! $jsLangSent) {
+ $jsLangSent = true;
+ $jsLang = [];
+ foreach ($this->_jsLangStrings as $strName => $strValue) {
+ $jsLang[] = "'$strName': '" . Sanitize::jsFormat($strValue, false) . '\'';
+ }
+ $js[] = "$.extend(Messages, {\n\t"
+ . implode(",\n\t", $jsLang) . '})';
+ }
+
+ $js[] = "$.extend(defaultValues, {\n\t"
+ . implode(",\n\t", $jsDefault) . '})';
+ $htmlOutput .= $this->formDisplayTemplate->displayJavascript($js);
+
+ return $htmlOutput;
+ }
+
+ /**
+ * Prepares data for input field display and outputs HTML code
+ *
+ * @param Form $form Form object
+ * @param string $field field name as it appears in $form
+ * @param string $systemPath field path, eg. Servers/1/verbose
+ * @param string $workPath work path, eg. Servers/4/verbose
+ * @param string $translatedPath work path changed so that it can be
+ * used as XHTML id
+ * @param bool $showRestoreDefault whether show "restore default" button
+ * besides the input field
+ * @param bool|null $userPrefsAllow whether user preferences are enabled
+ * for this field (null - no support,
+ * true/false - enabled/disabled)
+ * @param array $jsDefault array which stores JavaScript code
+ * to be displayed
+ *
+ * @return string|null HTML for input field
+ */
+ private function _displayFieldInput(
+ Form $form,
+ $field,
+ $systemPath,
+ $workPath,
+ $translatedPath,
+ $showRestoreDefault,
+ $userPrefsAllow,
+ array &$jsDefault
+ ) {
+ $name = Descriptions::get($systemPath);
+ $description = Descriptions::get($systemPath, 'desc');
+
+ $value = $this->_configFile->get($workPath);
+ $valueDefault = $this->_configFile->getDefault($systemPath);
+ $valueIsDefault = false;
+ if ($value === null || $value === $valueDefault) {
+ $value = $valueDefault;
+ $valueIsDefault = true;
+ }
+
+ $opts = [
+ 'doc' => $this->getDocLink($systemPath),
+ 'show_restore_default' => $showRestoreDefault,
+ 'userprefs_allow' => $userPrefsAllow,
+ 'userprefs_comment' => Descriptions::get($systemPath, 'cmt'),
+ ];
+ if (isset($form->default[$systemPath])) {
+ $opts['setvalue'] = (string) $form->default[$systemPath];
+ }
+
+ if (isset($this->_errors[$workPath])) {
+ $opts['errors'] = $this->_errors[$workPath];
+ }
+
+ $type = '';
+ switch ($form->getOptionType($field)) {
+ case 'string':
+ $type = 'text';
+ break;
+ case 'short_string':
+ $type = 'short_text';
+ break;
+ case 'double':
+ case 'integer':
+ $type = 'number_text';
+ break;
+ case 'boolean':
+ $type = 'checkbox';
+ break;
+ case 'select':
+ $type = 'select';
+ $opts['values'] = $form->getOptionValueList($form->fields[$field]);
+ break;
+ case 'array':
+ $type = 'list';
+ $value = (array) $value;
+ $valueDefault = (array) $valueDefault;
+ break;
+ case 'group':
+ // :group:end is changed to :group:end:{unique id} in Form class
+ $htmlOutput = '';
+ if (mb_substr($field, 7, 4) != 'end:') {
+ $htmlOutput .= $this->formDisplayTemplate->displayGroupHeader(
+ mb_substr($field, 7)
+ );
+ } else {
+ $this->formDisplayTemplate->displayGroupFooter();
+ }
+ return $htmlOutput;
+ case 'NULL':
+ trigger_error("Field $systemPath has no type", E_USER_WARNING);
+ return null;
+ }
+
+ // detect password fields
+ if ($type === 'text'
+ && (mb_substr($translatedPath, -9) === '-password'
+ || mb_substr($translatedPath, -4) === 'pass'
+ || mb_substr($translatedPath, -4) === 'Pass')
+ ) {
+ $type = 'password';
+ }
+
+ // TrustedProxies requires changes before displaying
+ if ($systemPath == 'TrustedProxies') {
+ foreach ($value as $ip => &$v) {
+ if (! preg_match('/^-\d+$/', $ip)) {
+ $v = $ip . ': ' . $v;
+ }
+ }
+ }
+ $this->_setComments($systemPath, $opts);
+
+ // send default value to form's JS
+ $jsLine = '\'' . $translatedPath . '\': ';
+ switch ($type) {
+ case 'text':
+ case 'short_text':
+ case 'number_text':
+ case 'password':
+ $jsLine .= '\'' . Sanitize::escapeJsString($valueDefault) . '\'';
+ break;
+ case 'checkbox':
+ $jsLine .= $valueDefault ? 'true' : 'false';
+ break;
+ case 'select':
+ $valueDefaultJs = is_bool($valueDefault)
+ ? (int) $valueDefault
+ : $valueDefault;
+ $jsLine .= '[\'' . Sanitize::escapeJsString($valueDefaultJs) . '\']';
+ break;
+ case 'list':
+ $jsLine .= '\'' . Sanitize::escapeJsString(implode("\n", $valueDefault))
+ . '\'';
+ break;
+ }
+ $jsDefault[] = $jsLine;
+
+ return $this->formDisplayTemplate->displayInput(
+ $translatedPath,
+ $name,
+ $type,
+ $value,
+ $description,
+ $valueIsDefault,
+ $opts
+ );
+ }
+
+ /**
+ * Displays errors
+ *
+ * @return string|null HTML for errors
+ */
+ public function displayErrors()
+ {
+ $this->_validate();
+ if (count($this->_errors) === 0) {
+ return null;
+ }
+
+ $htmlOutput = '';
+
+ foreach ($this->_errors as $systemPath => $errorList) {
+ if (isset($this->_systemPaths[$systemPath])) {
+ $name = Descriptions::get($this->_systemPaths[$systemPath]);
+ } else {
+ $name = Descriptions::get('Form_' . $systemPath);
+ }
+ $htmlOutput .= $this->formDisplayTemplate->displayErrors($name, $errorList);
+ }
+
+ return $htmlOutput;
+ }
+
+ /**
+ * Reverts erroneous fields to their default values
+ *
+ * @return void
+ */
+ public function fixErrors()
+ {
+ $this->_validate();
+ if (count($this->_errors) === 0) {
+ return;
+ }
+
+ $cf = $this->_configFile;
+ foreach (array_keys($this->_errors) as $workPath) {
+ if (! isset($this->_systemPaths[$workPath])) {
+ continue;
+ }
+ $canonicalPath = $this->_systemPaths[$workPath];
+ $cf->set($workPath, $cf->getDefault($canonicalPath));
+ }
+ }
+
+ /**
+ * Validates select field and casts $value to correct type
+ *
+ * @param string $value Current value
+ * @param array $allowed List of allowed values
+ *
+ * @return bool
+ */
+ private function _validateSelect(&$value, array $allowed)
+ {
+ $valueCmp = 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($valueCmp) xor is_numeric($vk)))
+ || $vk === $value
+ ) {
+ // keep boolean value as boolean
+ if (! is_bool($value)) {
+ settype($value, gettype($vk));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Validates and saves form data to session
+ *
+ * @param array|string $forms array of form names
+ * @param bool $allowPartialSave allows for partial form saving on
+ * failed validation
+ *
+ * @return boolean true on success (no errors and all saved)
+ */
+ public function save($forms, $allowPartialSave = true)
+ {
+ $result = true;
+ $forms = (array) $forms;
+
+ $values = [];
+ $toSave = [];
+ $isSetupScript = $GLOBALS['PMA_Config']->get('is_setup');
+ if ($isSetupScript) {
+ $this->_loadUserprefsInfo();
+ }
+
+ $this->_errors = [];
+ foreach ($forms as $formName) {
+ /** @var Form $form */
+ if (isset($this->_forms[$formName])) {
+ $form = $this->_forms[$formName];
+ } else {
+ continue;
+ }
+ // get current server id
+ $changeIndex = $form->index === 0
+ ? $this->_configFile->getServerCount() + 1
+ : false;
+ // grab POST values
+ foreach ($form->fields as $field => $systemPath) {
+ $workPath = array_search($systemPath, $this->_systemPaths);
+ $key = $this->_translatedPaths[$workPath];
+ $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 ($type == 'boolean') {
+ $_POST[$key] = false;
+ } else {
+ $this->_errors[$form->name][] = sprintf(
+ __('Missing data for %s'),
+ '<i>' . Descriptions::get($systemPath) . '</i>'
+ );
+ $result = false;
+ continue;
+ }
+ }
+
+ // user preferences allow/disallow
+ if ($isSetupScript
+ && isset($this->_userprefsKeys[$systemPath])
+ ) {
+ if (isset($this->_userprefsDisallow[$systemPath])
+ && isset($_POST[$key . '-userprefs-allow'])
+ ) {
+ unset($this->_userprefsDisallow[$systemPath]);
+ } elseif (! isset($_POST[$key . '-userprefs-allow'])) {
+ $this->_userprefsDisallow[$systemPath] = true;
+ }
+ }
+
+ // cast variables to correct type
+ switch ($type) {
+ case 'double':
+ $_POST[$key] = Util::requestString($_POST[$key]);
+ settype($_POST[$key], 'float');
+ break;
+ case 'boolean':
+ case 'integer':
+ if ($_POST[$key] !== '') {
+ $_POST[$key] = Util::requestString($_POST[$key]);
+ settype($_POST[$key], $type);
+ }
+ break;
+ case 'select':
+ $successfullyValidated = $this->_validateSelect(
+ $_POST[$key],
+ $form->getOptionValueList($systemPath)
+ );
+ if (! $successfullyValidated) {
+ $this->_errors[$workPath][] = __('Incorrect value!');
+ $result = false;
+ // "continue" for the $form->fields foreach-loop
+ continue 2;
+ }
+ break;
+ case 'string':
+ case 'short_string':
+ $_POST[$key] = Util::requestString($_POST[$key]);
+ break;
+ case 'array':
+ // eliminate empty values and ensure we have an array
+ $postValues = is_array($_POST[$key])
+ ? $_POST[$key]
+ : explode("\n", $_POST[$key]);
+ $_POST[$key] = [];
+ $this->_fillPostArrayParameters($postValues, $key);
+ break;
+ }
+
+ // now we have value with proper type
+ $values[$systemPath] = $_POST[$key];
+ if ($changeIndex !== false) {
+ $workPath = str_replace(
+ "Servers/$form->index/",
+ "Servers/$changeIndex/",
+ $workPath
+ );
+ }
+ $toSave[$workPath] = $systemPath;
+ }
+ }
+
+ // save forms
+ if (! $allowPartialSave && ! empty($this->_errors)) {
+ // don't look for non-critical errors
+ $this->_validate();
+ return $result;
+ }
+
+ foreach ($toSave as $workPath => $path) {
+ // TrustedProxies requires changes before saving
+ if ($path == 'TrustedProxies') {
+ $proxies = [];
+ $i = 0;
+ foreach ($values[$path] as $value) {
+ $matches = [];
+ $match = preg_match(
+ "/^(.+):(?:[ ]?)(\\w+)$/",
+ $value,
+ $matches
+ );
+ if ($match) {
+ // correct 'IP: HTTP header' pair
+ $ip = trim($matches[1]);
+ $proxies[$ip] = trim($matches[2]);
+ } else {
+ // save also incorrect values
+ $proxies["-$i"] = $value;
+ $i++;
+ }
+ }
+ $values[$path] = $proxies;
+ }
+ $this->_configFile->set($workPath, $values[$path], $path);
+ }
+ if ($isSetupScript) {
+ $this->_configFile->set(
+ 'UserprefsDisallow',
+ array_keys($this->_userprefsDisallow)
+ );
+ }
+
+ // don't look for non-critical errors
+ $this->_validate();
+
+ return $result;
+ }
+
+ /**
+ * Tells whether form validation failed
+ *
+ * @return boolean
+ */
+ public function hasErrors()
+ {
+ return count($this->_errors) > 0;
+ }
+
+
+ /**
+ * Returns link to documentation
+ *
+ * @param string $path Path to documentation
+ *
+ * @return string
+ */
+ public function getDocLink($path)
+ {
+ $test = mb_substr($path, 0, 6);
+ if ($test == 'Import' || $test == 'Export') {
+ return '';
+ }
+ return Util::getDocuLink(
+ 'config',
+ 'cfg_' . $this->_getOptName($path)
+ );
+ }
+
+ /**
+ * Changes path so it can be used in URLs
+ *
+ * @param string $path Path
+ *
+ * @return string
+ */
+ private function _getOptName($path)
+ {
+ return str_replace(['Servers/1/', '/'], ['Servers/', '_'], $path);
+ }
+
+ /**
+ * Fills out {@link userprefs_keys} and {@link userprefs_disallow}
+ *
+ * @return void
+ */
+ private function _loadUserprefsInfo()
+ {
+ if ($this->_userprefsKeys !== null) {
+ return;
+ }
+
+ $this->_userprefsKeys = array_flip(UserFormList::getFields());
+ // read real config for user preferences display
+ $userPrefsDisallow = $GLOBALS['PMA_Config']->get('is_setup')
+ ? $this->_configFile->get('UserprefsDisallow', [])
+ : $GLOBALS['cfg']['UserprefsDisallow'];
+ $this->_userprefsDisallow = array_flip($userPrefsDisallow);
+ }
+
+ /**
+ * Sets field comments and warnings based on current environment
+ *
+ * @param string $systemPath Path to settings
+ * @param array $opts Chosen options
+ *
+ * @return void
+ */
+ private function _setComments($systemPath, array &$opts)
+ {
+ // RecodingEngine - mark unavailable types
+ if ($systemPath == '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'
+ );
+ }
+ /* mbstring is always there thanks to polyfill */
+ $opts['comment'] = $comment;
+ $opts['comment_warning'] = true;
+ }
+ // ZipDump, GZipDump, BZipDump - check function availability
+ if ($systemPath == 'ZipDump'
+ || $systemPath == 'GZipDump'
+ || $systemPath == 'BZipDump'
+ ) {
+ $comment = '';
+ $funcs = [
+ 'ZipDump' => [
+ 'zip_open',
+ 'gzcompress',
+ ],
+ 'GZipDump' => [
+ 'gzopen',
+ 'gzencode',
+ ],
+ 'BZipDump' => [
+ 'bzopen',
+ 'bzcompress',
+ ],
+ ];
+ if (! function_exists($funcs[$systemPath][0])) {
+ $comment = sprintf(
+ __(
+ 'Compressed import will not work due to missing function %s.'
+ ),
+ $funcs[$systemPath][0]
+ );
+ }
+ if (! function_exists($funcs[$systemPath][1])) {
+ $comment .= ($comment ? '; ' : '') . sprintf(
+ __(
+ 'Compressed export will not work due to missing function %s.'
+ ),
+ $funcs[$systemPath][1]
+ );
+ }
+ $opts['comment'] = $comment;
+ $opts['comment_warning'] = true;
+ }
+ if (! $GLOBALS['PMA_Config']->get('is_setup')) {
+ if ($systemPath == 'MaxDbList' || $systemPath == 'MaxTableList'
+ || $systemPath == 'QueryHistoryMax'
+ ) {
+ $opts['comment'] = sprintf(
+ __('maximum %s'),
+ $GLOBALS['cfg'][$systemPath]
+ );
+ }
+ }
+ }
+
+ /**
+ * Copy items of an array to $_POST variable
+ *
+ * @param array $postValues List of parameters
+ * @param string $key Array key
+ *
+ * @return void
+ */
+ private function _fillPostArrayParameters(array $postValues, $key)
+ {
+ foreach ($postValues as $v) {
+ $v = Util::requestString($v);
+ if ($v !== '') {
+ $_POST[$key][] = $v;
+ }
+ }
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/FormDisplayTemplate.php b/srcs/phpmyadmin/libraries/classes/Config/FormDisplayTemplate.php
new file mode 100644
index 0000000..07663b8
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/FormDisplayTemplate.php
@@ -0,0 +1,526 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Form templates
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config;
+use PhpMyAdmin\Sanitize;
+use PhpMyAdmin\Template;
+use PhpMyAdmin\Url;
+use PhpMyAdmin\Util;
+
+/**
+ * PhpMyAdmin\Config\FormDisplayTemplate class
+ *
+ * @package PhpMyAdmin
+ */
+class FormDisplayTemplate
+{
+ /**
+ * @var int
+ */
+ public $group;
+
+ /**
+ * @var Config
+ */
+ protected $config;
+
+ /**
+ * @var Template
+ */
+ public $template;
+
+ /**
+ * FormDisplayTemplate constructor.
+ *
+ * @param Config $config Config instance
+ */
+ public function __construct(Config $config)
+ {
+ $this->config = $config;
+ $this->template = new Template();
+ }
+
+ /**
+ * Displays top part of the form
+ *
+ * @param string $action default: $_SERVER['REQUEST_URI']
+ * @param string $method 'post' or 'get'
+ * @param array|null $hiddenFields array of form hidden fields (key: field name)
+ *
+ * @return string
+ */
+ public function displayFormTop(
+ $action = null,
+ $method = 'post',
+ $hiddenFields = null
+ ): string {
+ static $hasCheckPageRefresh = false;
+
+ if ($action === null) {
+ $action = $_SERVER['REQUEST_URI'];
+ }
+ if ($method != 'post') {
+ $method = 'get';
+ }
+ $htmlOutput = '<form method="' . $method . '" action="'
+ . htmlspecialchars($action) . '" class="config-form disableAjax">';
+ $htmlOutput .= '<input type="hidden" name="tab_hash" value="">';
+ // we do validation on page refresh when browser remembers field values,
+ // add a field with known value which will be used for checks
+ if (! $hasCheckPageRefresh) {
+ $hasCheckPageRefresh = true;
+ $htmlOutput .= '<input type="hidden" name="check_page_refresh" '
+ . ' id="check_page_refresh" value="">' . "\n";
+ }
+ $htmlOutput .= Url::getHiddenInputs('', '', 0, 'server') . "\n";
+ $htmlOutput .= Url::getHiddenFields((array) $hiddenFields, '', true);
+ return $htmlOutput;
+ }
+
+ /**
+ * Displays form tabs which are given by an array indexed by fieldset id
+ * ({@link self::displayFieldsetTop}), with values being tab titles.
+ *
+ * @param array $tabs tab names
+ *
+ * @return string
+ */
+ public function displayTabsTop(array $tabs): string
+ {
+ $items = [];
+ foreach ($tabs as $tabId => $tabName) {
+ $items[] = [
+ 'content' => htmlspecialchars($tabName),
+ 'url' => [
+ 'href' => '#' . $tabId,
+ ],
+ ];
+ }
+
+ $htmlOutput = $this->template->render('list/unordered', [
+ 'class' => 'tabs responsivetable',
+ 'items' => $items,
+ ]);
+ $htmlOutput .= '<br>';
+ $htmlOutput .= '<div class="tabs_contents">';
+ return $htmlOutput;
+ }
+
+ /**
+ * Displays top part of a fieldset
+ *
+ * @param string $title title of fieldset
+ * @param string $description description shown on top of fieldset
+ * @param array|null $errors error messages to display
+ * @param array $attributes optional extra attributes of fieldset
+ *
+ * @return string
+ */
+ public function displayFieldsetTop(
+ $title = '',
+ $description = '',
+ $errors = null,
+ array $attributes = []
+ ): string {
+ $this->group = 0;
+
+ $attributes = array_merge(['class' => 'optbox'], $attributes);
+
+ return $this->template->render('config/form_display/fieldset_top', [
+ 'attributes' => $attributes,
+ 'title' => $title,
+ 'description' => $description,
+ 'errors' => $errors,
+ ]);
+ }
+
+ /**
+ * Displays input field
+ *
+ * $opts keys:
+ * o doc - (string) documentation link
+ * o errors - error array
+ * o setvalue - (string) shows button allowing to set predefined value
+ * o show_restore_default - (boolean) whether show "restore default" button
+ * o userprefs_allow - whether user preferences are enabled for this field
+ * (null - no support, true/false - enabled/disabled)
+ * o userprefs_comment - (string) field comment
+ * o values - key - value pairs for <select> fields
+ * o values_escaped - (boolean) tells whether values array is already escaped
+ * (defaults to false)
+ * o values_disabled - (array)list of disabled values (keys from values)
+ * o comment - (string) tooltip comment
+ * o comment_warning - (bool) whether this comments warns about something
+ *
+ * @param string $path config option path
+ * @param string $name config option name
+ * @param string $type type of config option
+ * @param mixed $value current value
+ * @param string $description verbose description
+ * @param bool $valueIsDefault whether value is default
+ * @param array|null $opts see above description
+ *
+ * @return string
+ */
+ public function displayInput(
+ $path,
+ $name,
+ $type,
+ $value,
+ $description = '',
+ $valueIsDefault = true,
+ $opts = null
+ ): string {
+ static $icons; // An array of IMG tags used further below in the function
+
+ if (defined('TESTSUITE')) {
+ $icons = null;
+ }
+
+ $isSetupScript = $this->config->get('is_setup');
+ if ($icons === null) { // if the static variables have not been initialised
+ $icons = [];
+ // Icon definitions:
+ // The same indexes will be used in the $icons array.
+ // The first element contains the filename and the second
+ // element is used for the "alt" and "title" attributes.
+ $iconInit = [
+ 'edit' => [
+ 'b_edit',
+ '',
+ ],
+ 'help' => [
+ 'b_help',
+ __('Documentation'),
+ ],
+ 'reload' => [
+ 's_reload',
+ '',
+ ],
+ 'tblops' => [
+ 'b_tblops',
+ '',
+ ],
+ ];
+ if ($isSetupScript) {
+ // When called from the setup script, we don't have access to the
+ // sprite-aware getImage() function because the PMA_theme class
+ // has not been loaded, so we generate the img tags manually.
+ foreach ($iconInit as $k => $v) {
+ $title = '';
+ if (! empty($v[1])) {
+ $title = ' title="' . $v[1] . '"';
+ }
+ $icons[$k] = sprintf(
+ '<img alt="%s" src="%s"%s>',
+ $v[1],
+ "../themes/pmahomme/img/{$v[0]}.png",
+ $title
+ );
+ }
+ } else {
+ // In this case we just use getImage() because it's available
+ foreach ($iconInit as $k => $v) {
+ $icons[$k] = Util::getImage(
+ $v[0],
+ $v[1]
+ );
+ }
+ }
+ }
+ $hasErrors = isset($opts['errors']) && ! empty($opts['errors']);
+ $optionIsDisabled = ! $isSetupScript && isset($opts['userprefs_allow'])
+ && ! $opts['userprefs_allow'];
+ $nameId = 'name="' . htmlspecialchars($path) . '" id="'
+ . htmlspecialchars($path) . '"';
+ $fieldClass = $type == 'checkbox' ? 'checkbox' : '';
+ if (! $valueIsDefault) {
+ $fieldClass .= ($fieldClass == '' ? '' : ' ')
+ . ($hasErrors ? 'custom field-error' : 'custom');
+ }
+ $fieldClass = $fieldClass ? ' class="' . $fieldClass . '"' : '';
+ $trClass = $this->group > 0
+ ? 'group-field group-field-' . $this->group
+ : '';
+ if (isset($opts['setvalue']) && $opts['setvalue'] == ':group') {
+ unset($opts['setvalue']);
+ $this->group++;
+ $trClass = 'group-header-field group-header-' . $this->group;
+ }
+ if ($optionIsDisabled) {
+ $trClass .= ($trClass ? ' ' : '') . 'disabled-field';
+ }
+ $trClass = $trClass ? ' class="' . $trClass . '"' : '';
+
+ $htmlOutput = '<tr' . $trClass . '>';
+ $htmlOutput .= '<th>';
+ $htmlOutput .= '<label for="' . htmlspecialchars($path) . '">' . htmlspecialchars_decode($name)
+ . '</label>';
+
+ if (! empty($opts['doc'])) {
+ $htmlOutput .= '<span class="doc">';
+ $htmlOutput .= '<a href="' . $opts['doc']
+ . '" target="documentation">' . $icons['help'] . '</a>';
+ $htmlOutput .= "\n";
+ $htmlOutput .= '</span>';
+ }
+
+ if ($optionIsDisabled) {
+ $htmlOutput .= '<span class="disabled-notice" title="';
+ $htmlOutput .= __(
+ 'This setting is disabled, it will not be applied to your configuration.'
+ );
+ $htmlOutput .= '">' . __('Disabled') . "</span>";
+ }
+
+ if (! empty($description)) {
+ $htmlOutput .= '<small>' . $description . '</small>';
+ }
+
+ $htmlOutput .= '</th>';
+ $htmlOutput .= '<td>';
+
+ switch ($type) {
+ case 'text':
+ $htmlOutput .= '<input type="text" class="all85" ' . $nameId . $fieldClass
+ . ' value="' . htmlspecialchars($value) . '">';
+ break;
+ case 'password':
+ $htmlOutput .= '<input type="password" class="all85" ' . $nameId . $fieldClass
+ . ' value="' . htmlspecialchars($value) . '">';
+ break;
+ case 'short_text':
+ // As seen in the reporting server (#15042) we sometimes receive
+ // an array here. No clue about its origin nor content, so let's avoid
+ // a notice on htmlspecialchars().
+ if (! is_array($value)) {
+ $htmlOutput .= '<input type="text" size="25" ' . $nameId
+ . $fieldClass . ' value="' . htmlspecialchars($value)
+ . '">';
+ }
+ break;
+ case 'number_text':
+ $htmlOutput .= '<input type="number" ' . $nameId . $fieldClass
+ . ' value="' . htmlspecialchars((string) $value) . '">';
+ break;
+ case 'checkbox':
+ $htmlOutput .= '<span' . $fieldClass . '><input type="checkbox" ' . $nameId
+ . ($value ? ' checked="checked"' : '') . '></span>';
+ break;
+ case 'select':
+ $htmlOutput .= '<select class="all85" ' . $nameId . $fieldClass . '>';
+ $escape = ! (isset($opts['values_escaped']) && $opts['values_escaped']);
+ $valuesDisabled = isset($opts['values_disabled'])
+ ? array_flip($opts['values_disabled']) : [];
+ foreach ($opts['values'] as $optValueKey => $optValue) {
+ // set names for boolean values
+ if (is_bool($optValue)) {
+ $optValue = mb_strtolower(
+ $optValue ? __('Yes') : __('No')
+ );
+ }
+ // escape if necessary
+ if ($escape) {
+ $display = htmlspecialchars((string) $optValue);
+ $displayValue = htmlspecialchars((string) $optValueKey);
+ } else {
+ $display = $optValue;
+ $displayValue = $optValueKey;
+ }
+ // compare with selected value
+ // boolean values are cast to integers when used as array keys
+ $selected = is_bool($value)
+ ? (int) $value === $optValueKey
+ : $optValueKey === $value;
+ $htmlOutput .= '<option value="' . $displayValue . '"';
+ if ($selected) {
+ $htmlOutput .= ' selected="selected"';
+ }
+ if (isset($valuesDisabled[$optValueKey])) {
+ $htmlOutput .= ' disabled="disabled"';
+ }
+ $htmlOutput .= '>' . $display . '</option>';
+ }
+ $htmlOutput .= '</select>';
+ break;
+ case 'list':
+ $htmlOutput .= '<textarea cols="35" rows="5" ' . $nameId . $fieldClass
+ . '>' . htmlspecialchars(implode("\n", $value)) . '</textarea>';
+ break;
+ }
+ if ($isSetupScript
+ && isset($opts['userprefs_comment'])
+ && $opts['userprefs_comment']
+ ) {
+ $htmlOutput .= '<a class="userprefs-comment" title="'
+ . htmlspecialchars($opts['userprefs_comment']) . '">'
+ . $icons['tblops'] . '</a>';
+ }
+ if (isset($opts['setvalue']) && $opts['setvalue']) {
+ $htmlOutput .= '<a class="set-value hide" href="#'
+ . htmlspecialchars("$path={$opts['setvalue']}") . '" title="'
+ . sprintf(__('Set value: %s'), htmlspecialchars($opts['setvalue']))
+ . '">' . $icons['edit'] . '</a>';
+ }
+ if (isset($opts['show_restore_default']) && $opts['show_restore_default']) {
+ $htmlOutput .= '<a class="restore-default hide" href="#' . $path . '" title="'
+ . __('Restore default value') . '">' . $icons['reload'] . '</a>';
+ }
+ // this must match with displayErrors() in scripts/config.js
+ if ($hasErrors) {
+ $htmlOutput .= "\n <dl class=\"inline_errors\">";
+ foreach ($opts['errors'] as $error) {
+ $htmlOutput .= '<dd>' . htmlspecialchars($error) . '</dd>';
+ }
+ $htmlOutput .= '</dl>';
+ }
+ $htmlOutput .= '</td>';
+ if ($isSetupScript && isset($opts['userprefs_allow'])) {
+ $htmlOutput .= '<td class="userprefs-allow" title="' .
+ __('Allow users to customize this value') . '">';
+ $htmlOutput .= '<input type="checkbox" name="' . $path
+ . '-userprefs-allow" ';
+ if ($opts['userprefs_allow']) {
+ $htmlOutput .= 'checked="checked"';
+ }
+ $htmlOutput .= '>';
+ $htmlOutput .= '</td>';
+ } elseif ($isSetupScript) {
+ $htmlOutput .= '<td>&nbsp;</td>';
+ }
+ $htmlOutput .= '</tr>';
+ return $htmlOutput;
+ }
+
+ /**
+ * Display group header
+ *
+ * @param string $headerText Text of header
+ *
+ * @return string
+ */
+ public function displayGroupHeader(string $headerText): string
+ {
+ $this->group++;
+ if ($headerText === '') {
+ return '';
+ }
+ $colspan = $this->config->get('is_setup') ? 3 : 2;
+
+ return $this->template->render('config/form_display/group_header', [
+ 'group' => $this->group,
+ 'colspan' => $colspan,
+ 'header_text' => $headerText,
+ ]);
+ }
+
+ /**
+ * Display group footer
+ *
+ * @return void
+ */
+ public function displayGroupFooter(): void
+ {
+ $this->group--;
+ }
+
+ /**
+ * Displays bottom part of a fieldset
+ *
+ * @param bool $showButtons Whether show submit and reset button
+ *
+ * @return string
+ */
+ public function displayFieldsetBottom(bool $showButtons = true): string
+ {
+ return $this->template->render('config/form_display/fieldset_bottom', [
+ 'show_buttons' => $showButtons,
+ 'is_setup' => $this->config->get('is_setup'),
+ ]);
+ }
+
+ /**
+ * Closes form tabs
+ *
+ * @return string
+ */
+ public function displayTabsBottom(): string
+ {
+ return $this->template->render('config/form_display/tabs_bottom');
+ }
+
+ /**
+ * Displays bottom part of the form
+ *
+ * @return string
+ */
+ public function displayFormBottom(): string
+ {
+ return $this->template->render('config/form_display/form_bottom');
+ }
+
+ /**
+ * Appends JS validation code to $js_array
+ *
+ * @param string $fieldId ID of field to validate
+ * @param string|array $validators validators callback
+ * @param array $jsArray will be updated with javascript code
+ *
+ * @return void
+ */
+ public function addJsValidate($fieldId, $validators, array &$jsArray): void
+ {
+ foreach ((array) $validators as $validator) {
+ $validator = (array) $validator;
+ $vName = array_shift($validator);
+ $vArgs = [];
+ foreach ($validator as $arg) {
+ $vArgs[] = Sanitize::escapeJsString($arg);
+ }
+ $vArgs = $vArgs ? ", ['" . implode("', '", $vArgs) . "']" : '';
+ $jsArray[] = "registerFieldValidator('$fieldId', '$vName', true$vArgs)";
+ }
+ }
+
+ /**
+ * Displays JavaScript code
+ *
+ * @param array $jsArray lines of javascript code
+ *
+ * @return string
+ */
+ public function displayJavascript(array $jsArray): string
+ {
+ if (empty($jsArray)) {
+ return '';
+ }
+
+ return $this->template->render('javascript/display', [
+ 'js_array' => $jsArray,
+ ]);
+ }
+
+ /**
+ * Displays error list
+ *
+ * @param string $name Name of item with errors
+ * @param array $errorList List of errors to show
+ *
+ * @return string HTML for errors
+ */
+ public function displayErrors($name, array $errorList): string
+ {
+ return $this->template->render('config/form_display/errors', [
+ 'name' => $name,
+ 'error_list' => $errorList,
+ ]);
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/BaseForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/BaseForm.php
new file mode 100644
index 0000000..2049070
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/BaseForm.php
@@ -0,0 +1,89 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Base class for preferences.
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms;
+
+use PhpMyAdmin\Config\ConfigFile;
+use PhpMyAdmin\Config\FormDisplay;
+
+/**
+ * Base form for user preferences
+ *
+ * @package PhpMyAdmin
+ */
+abstract class BaseForm extends FormDisplay
+{
+ /**
+ * Constructor
+ *
+ * @param ConfigFile $cf Config file instance
+ * @param int|null $serverId 0 if new server, validation; >= 1 if editing a server
+ */
+ public function __construct(ConfigFile $cf, $serverId = null)
+ {
+ parent::__construct($cf);
+ foreach (static::getForms() as $formName => $form) {
+ $this->registerForm($formName, $form, $serverId);
+ }
+ }
+
+ /**
+ * List of available forms, each form is described as an array of fields to display.
+ * Fields MUST have their counterparts in the $cfg array.
+ *
+ * To define form field, use the notation below:
+ * $forms['Form group']['Form name'] = array('Option/path');
+ *
+ * You can assign default values set by special button ("set value: ..."), eg.:
+ * 'Servers/1/pmadb' => 'phpmyadmin'
+ *
+ * To group options, use:
+ * ':group:' . __('group name') // just define a group
+ * or
+ * 'option' => ':group' // group starting from this option
+ * End group blocks with:
+ * ':group:end'
+ *
+ * @todo This should be abstract, but that does not work in PHP 5
+ *
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [];
+ }
+
+ /**
+ * Returns list of fields used in the form.
+ *
+ * @return string[]
+ */
+ public static function getFields()
+ {
+ $names = [];
+ foreach (static::getForms() as $form) {
+ foreach ($form as $k => $v) {
+ $names[] = is_int($k) ? $v : $k;
+ }
+ }
+ return $names;
+ }
+
+ /**
+ * Returns name of the form
+ *
+ * @todo This should be abstract, but that does not work in PHP 5
+ *
+ * @return string
+ */
+ public static function getName()
+ {
+ return '';
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/BaseFormList.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/BaseFormList.php
new file mode 100644
index 0000000..f4a5d32
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/BaseFormList.php
@@ -0,0 +1,150 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms;
+
+use PhpMyAdmin\Config\ConfigFile;
+
+/**
+ * Class BaseFormList
+ * @package PhpMyAdmin\Config\Forms
+ */
+class BaseFormList
+{
+ /**
+ * List of all forms
+ */
+ protected static $all = [];
+
+ /**
+ * @var string
+ */
+ protected static $ns = 'PhpMyAdmin\\Config\\Forms\\';
+
+ /**
+ * @var array
+ */
+ private $_forms;
+
+ /**
+ * @return array
+ */
+ public static function getAll()
+ {
+ return static::$all;
+ }
+
+ /**
+ * @param string $name Name
+ * @return bool
+ */
+ public static function isValid($name)
+ {
+ return in_array($name, static::$all);
+ }
+
+ /**
+ * @param string $name Name
+ * @return null|string
+ */
+ public static function get($name)
+ {
+ if (static::isValid($name)) {
+ return static::$ns . $name . 'Form';
+ }
+ return null;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param ConfigFile $cf Config file instance
+ */
+ public function __construct(ConfigFile $cf)
+ {
+ $this->_forms = [];
+ foreach (static::$all as $form) {
+ $class = static::get($form);
+ $this->_forms[] = new $class($cf);
+ }
+ }
+
+ /**
+ * Processes forms, returns true on successful save
+ *
+ * @param bool $allowPartialSave allows for partial form saving
+ * on failed validation
+ * @param bool $checkFormSubmit whether check for $_POST['submit_save']
+ *
+ * @return boolean whether processing was successful
+ */
+ public function process($allowPartialSave = true, $checkFormSubmit = true)
+ {
+ $ret = true;
+ foreach ($this->_forms as $form) {
+ $ret = $ret && $form->process($allowPartialSave, $checkFormSubmit);
+ }
+ return $ret;
+ }
+
+ /**
+ * Displays errors
+ *
+ * @return string HTML for errors
+ */
+ public function displayErrors()
+ {
+ $ret = '';
+ foreach ($this->_forms as $form) {
+ $ret .= $form->displayErrors();
+ }
+ return $ret;
+ }
+
+ /**
+ * Reverts erroneous fields to their default values
+ *
+ * @return void
+ */
+ public function fixErrors()
+ {
+ foreach ($this->_forms as $form) {
+ $form->fixErrors();
+ }
+ }
+
+ /**
+ * Tells whether form validation failed
+ *
+ * @return boolean
+ */
+ public function hasErrors()
+ {
+ $ret = false;
+ foreach ($this->_forms as $form) {
+ $ret = $ret || $form->hasErrors();
+ }
+ return $ret;
+ }
+
+ /**
+ * Returns list of fields used in the form.
+ *
+ * @return string[]
+ */
+ public static function getFields()
+ {
+ $names = [];
+ foreach (static::$all as $form) {
+ $class = static::get($form);
+ $names = array_merge($names, $class::getFields());
+ }
+ return $names;
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/BrowseForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/BrowseForm.php
new file mode 100644
index 0000000..eee578a
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/BrowseForm.php
@@ -0,0 +1,30 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+use PhpMyAdmin\Config\Forms\User\MainForm;
+
+/**
+ * Class BrowseForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class BrowseForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Browse' => MainForm::getForms()['Browse'],
+ ];
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/DbStructureForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/DbStructureForm.php
new file mode 100644
index 0000000..4f9a8e4
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/DbStructureForm.php
@@ -0,0 +1,30 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+use PhpMyAdmin\Config\Forms\User\MainForm;
+
+/**
+ * Class DbStructureForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class DbStructureForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'DbStructure' => MainForm::getForms()['DbStructure'],
+ ];
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/EditForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/EditForm.php
new file mode 100644
index 0000000..ad2fd46
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/EditForm.php
@@ -0,0 +1,32 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+use PhpMyAdmin\Config\Forms\User\FeaturesForm;
+use PhpMyAdmin\Config\Forms\User\MainForm;
+
+/**
+ * Class EditForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class EditForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Edit' => MainForm::getForms()['Edit'],
+ 'Text_fields' => FeaturesForm::getForms()['Text_fields'],
+ ];
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ExportForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ExportForm.php
new file mode 100644
index 0000000..584b2fd
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ExportForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+/**
+ * Class ExportForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class ExportForm extends \PhpMyAdmin\Config\Forms\User\ExportForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ImportForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ImportForm.php
new file mode 100644
index 0000000..78e429a
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/ImportForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+/**
+ * Class ImportForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class ImportForm extends \PhpMyAdmin\Config\Forms\User\ImportForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/NaviForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/NaviForm.php
new file mode 100644
index 0000000..02350eb
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/NaviForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+/**
+ * Class NaviForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class NaviForm extends \PhpMyAdmin\Config\Forms\User\NaviForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/PageFormList.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/PageFormList.php
new file mode 100644
index 0000000..f4cae3d
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/PageFormList.php
@@ -0,0 +1,37 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Page preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+use PhpMyAdmin\Config\Forms\BaseFormList;
+
+/**
+ * Class PageFormList
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class PageFormList extends BaseFormList
+{
+ /**
+ * @var array
+ */
+ protected static $all = [
+ 'Browse',
+ 'DbStructure',
+ 'Edit',
+ 'Export',
+ 'Import',
+ 'Navi',
+ 'Sql',
+ 'TableStructure',
+ ];
+ /**
+ * @var string
+ */
+ protected static $ns = '\\PhpMyAdmin\\Config\\Forms\\Page\\';
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/SqlForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/SqlForm.php
new file mode 100644
index 0000000..4ed85ff
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/SqlForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+/**
+ * Class SqlForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class SqlForm extends \PhpMyAdmin\Config\Forms\User\SqlForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/TableStructureForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/TableStructureForm.php
new file mode 100644
index 0000000..05af064
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Page/TableStructureForm.php
@@ -0,0 +1,30 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Page;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+use PhpMyAdmin\Config\Forms\User\MainForm;
+
+/**
+ * Class TableStructureForm
+ * @package PhpMyAdmin\Config\Forms\Page
+ */
+class TableStructureForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'TableStructure' => MainForm::getForms()['TableStructure'],
+ ];
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ConfigForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ConfigForm.php
new file mode 100644
index 0000000..6fd4515
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ConfigForm.php
@@ -0,0 +1,32 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class ConfigForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class ConfigForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Config' => [
+ 'DefaultLang',
+ 'ServerDefault',
+ ],
+ ];
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ExportForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ExportForm.php
new file mode 100644
index 0000000..adf7ce4
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ExportForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+/**
+ * Class ExportForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class ExportForm extends \PhpMyAdmin\Config\Forms\User\ExportForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/FeaturesForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/FeaturesForm.php
new file mode 100644
index 0000000..54f6cf2
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/FeaturesForm.php
@@ -0,0 +1,77 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+/**
+ * Class FeaturesForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class FeaturesForm extends \PhpMyAdmin\Config\Forms\User\FeaturesForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ // phpcs:disable Squiz.Arrays.ArrayDeclaration.KeySpecified,Squiz.Arrays.ArrayDeclaration.NoKeySpecified
+ $result = parent::getForms();
+ /* Remove only_db/hide_db, we have proper Server form in setup */
+ $result['Databases'] = array_diff(
+ $result['Databases'],
+ [
+ 'Servers/1/only_db',
+ 'Servers/1/hide_db',
+ ]
+ );
+ /* Following are not available to user */
+ $result['Import_export'] = [
+ 'UploadDir',
+ 'SaveDir',
+ 'RecodingEngine' => ':group',
+ 'IconvExtraParams',
+ ':group:end',
+ 'ZipDump',
+ 'GZipDump',
+ 'BZipDump',
+ 'CompressOnFly',
+ ];
+ $result['Security'] = [
+ 'blowfish_secret',
+ 'CheckConfigurationPermissions',
+ 'TrustedProxies',
+ 'AllowUserDropDatabase',
+ 'AllowArbitraryServer',
+ 'ArbitraryServerRegexp',
+ 'LoginCookieRecall',
+ 'LoginCookieStore',
+ 'LoginCookieDeleteAll',
+ 'CaptchaLoginPublicKey',
+ 'CaptchaLoginPrivateKey',
+ ];
+ $result['Developer'] = [
+ 'UserprefsDeveloperTab',
+ 'DBG/sql',
+ ];
+ $result['Other_core_settings'] = [
+ 'OBGzip',
+ 'PersistentConnections',
+ 'ExecTimeLimit',
+ 'MemoryLimit',
+ 'UseDbSearch',
+ 'ProxyUrl',
+ 'ProxyUser',
+ 'ProxyPass',
+ 'AllowThirdPartyFraming',
+ 'ZeroConf',
+ ];
+ return $result;
+ // phpcs:enable
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ImportForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ImportForm.php
new file mode 100644
index 0000000..06adf35
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ImportForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+/**
+ * Class ImportForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class ImportForm extends \PhpMyAdmin\Config\Forms\User\ImportForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/MainForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/MainForm.php
new file mode 100644
index 0000000..ebdc1cd
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/MainForm.php
@@ -0,0 +1,29 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+/**
+ * Class MainForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class MainForm extends \PhpMyAdmin\Config\Forms\User\MainForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ $result = parent::getForms();
+ /* Following are not available to user */
+ $result['Startup'][] = 'ShowPhpInfo';
+ $result['Startup'][] = 'ShowChgPassword';
+ return $result;
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/NaviForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/NaviForm.php
new file mode 100644
index 0000000..da1e9ed
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/NaviForm.php
@@ -0,0 +1,18 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+/**
+ * Class NaviForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class NaviForm extends \PhpMyAdmin\Config\Forms\User\NaviForm
+{
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ServersForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ServersForm.php
new file mode 100644
index 0000000..553447f
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/ServersForm.php
@@ -0,0 +1,116 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class ServersForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class ServersForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ // phpcs:disable Squiz.Arrays.ArrayDeclaration.KeySpecified,Squiz.Arrays.ArrayDeclaration.NoKeySpecified
+ return [
+ 'Server' => [
+ 'Servers' => [
+ 1 => [
+ 'verbose',
+ 'host',
+ 'port',
+ 'socket',
+ 'ssl',
+ 'compress',
+ ],
+ ],
+ ],
+ 'Server_auth' => [
+ 'Servers' => [
+ 1 => [
+ 'auth_type',
+ ':group:' . __('Config authentication'),
+ 'user',
+ 'password',
+ ':group:end',
+ ':group:' . __('HTTP authentication'),
+ 'auth_http_realm',
+ ':group:end',
+ ':group:' . __('Signon authentication'),
+ 'SignonSession',
+ 'SignonURL',
+ 'LogoutURL',
+ ],
+ ],
+ ],
+ 'Server_config' => [
+ 'Servers' => [
+ 1 => [
+ 'only_db',
+ 'hide_db',
+ 'AllowRoot',
+ 'AllowNoPassword',
+ 'DisableIS',
+ 'AllowDeny/order',
+ 'AllowDeny/rules',
+ 'SessionTimeZone',
+ ],
+ ],
+ ],
+ 'Server_pmadb' => [
+ 'Servers' => [
+ 1 => [
+ 'pmadb' => 'phpmyadmin',
+ 'controlhost',
+ 'controlport',
+ 'controluser',
+ 'controlpass',
+ 'bookmarktable' => 'pma__bookmark',
+ 'relation' => 'pma__relation',
+ 'userconfig' => 'pma__userconfig',
+ 'users' => 'pma__users',
+ 'usergroups' => 'pma__usergroups',
+ 'navigationhiding' => 'pma__navigationhiding',
+ 'table_info' => 'pma__table_info',
+ 'column_info' => 'pma__column_info',
+ 'history' => 'pma__history',
+ 'recent' => 'pma__recent',
+ 'favorite' => 'pma__favorite',
+ 'table_uiprefs' => 'pma__table_uiprefs',
+ 'tracking' => 'pma__tracking',
+ 'table_coords' => 'pma__table_coords',
+ 'pdf_pages' => 'pma__pdf_pages',
+ 'savedsearches' => 'pma__savedsearches',
+ 'central_columns' => 'pma__central_columns',
+ 'designer_settings' => 'pma__designer_settings',
+ 'export_templates' => 'pma__export_templates',
+ 'MaxTableUiprefs' => 100,
+ ],
+ ],
+ ],
+ 'Server_tracking' => [
+ 'Servers' => [
+ 1 => [
+ 'tracking_version_auto_create',
+ 'tracking_default_statements',
+ 'tracking_add_drop_view',
+ 'tracking_add_drop_table',
+ 'tracking_add_drop_database',
+ ],
+ ],
+ ],
+ ];
+ // phpcs:enable
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SetupFormList.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SetupFormList.php
new file mode 100644
index 0000000..91edb27
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SetupFormList.php
@@ -0,0 +1,37 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Setup preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+use PhpMyAdmin\Config\Forms\BaseFormList;
+
+/**
+ * Class SetupFormList
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class SetupFormList extends BaseFormList
+{
+ /**
+ * @var array
+ */
+ protected static $all = [
+ 'Config',
+ 'Export',
+ 'Features',
+ 'Import',
+ 'Main',
+ 'Navi',
+ 'Servers',
+ 'Sql',
+ ];
+ /**
+ * @var string
+ */
+ protected static $ns = '\\PhpMyAdmin\\Config\\Forms\\Setup\\';
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SqlForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SqlForm.php
new file mode 100644
index 0000000..0cc3a1c
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/Setup/SqlForm.php
@@ -0,0 +1,28 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\Setup;
+
+/**
+ * Class SqlForm
+ * @package PhpMyAdmin\Config\Forms\Setup
+ */
+class SqlForm extends \PhpMyAdmin\Config\Forms\User\SqlForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ $result = parent::getForms();
+ /* Following are not available to user */
+ $result['Sql_queries'][] = 'QueryHistoryDB';
+ return $result;
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/ExportForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/ExportForm.php
new file mode 100644
index 0000000..f2f853c
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/ExportForm.php
@@ -0,0 +1,160 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class ExportForm
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class ExportForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ // phpcs:disable Squiz.Arrays.ArrayDeclaration.KeySpecified,Squiz.Arrays.ArrayDeclaration.NoKeySpecified
+ return [
+ 'Export_defaults' => [
+ 'Export/method',
+ ':group:' . __('Quick'),
+ 'Export/quick_export_onserver',
+ 'Export/quick_export_onserver_overwrite',
+ ':group:end',
+ ':group:' . __('Custom'),
+ 'Export/format',
+ 'Export/compression',
+ 'Export/charset',
+ 'Export/lock_tables',
+ 'Export/as_separate_files',
+ 'Export/asfile' => ':group',
+ 'Export/onserver',
+ 'Export/onserver_overwrite',
+ ':group:end',
+ 'Export/file_template_table',
+ 'Export/file_template_database',
+ 'Export/file_template_server',
+ ],
+ 'Sql' => [
+ 'Export/sql_include_comments' => ':group',
+ 'Export/sql_dates',
+ 'Export/sql_relation',
+ 'Export/sql_mime',
+ ':group:end',
+ 'Export/sql_use_transaction',
+ 'Export/sql_disable_fk',
+ 'Export/sql_views_as_tables',
+ 'Export/sql_metadata',
+ 'Export/sql_compatibility',
+ 'Export/sql_structure_or_data',
+ ':group:' . __('Structure'),
+ 'Export/sql_drop_database',
+ 'Export/sql_create_database',
+ 'Export/sql_drop_table',
+ 'Export/sql_create_table' => ':group',
+ 'Export/sql_if_not_exists',
+ 'Export/sql_auto_increment',
+ ':group:end',
+ 'Export/sql_create_view' => ':group',
+ 'Export/sql_view_current_user',
+ 'Export/sql_or_replace_view',
+ ':group:end',
+ 'Export/sql_procedure_function',
+ 'Export/sql_create_trigger',
+ 'Export/sql_backquotes',
+ ':group:end',
+ ':group:' . __('Data'),
+ 'Export/sql_delayed',
+ 'Export/sql_ignore',
+ 'Export/sql_type',
+ 'Export/sql_insert_syntax',
+ 'Export/sql_max_query_size',
+ 'Export/sql_hex_for_binary',
+ 'Export/sql_utc_time',
+ ],
+ 'CodeGen' => [
+ 'Export/codegen_format',
+ ],
+ 'Csv' => [
+ ':group:' . __('CSV'),
+ 'Export/csv_separator',
+ 'Export/csv_enclosed',
+ 'Export/csv_escaped',
+ 'Export/csv_terminated',
+ 'Export/csv_null',
+ 'Export/csv_removeCRLF',
+ 'Export/csv_columns',
+ ':group:end',
+ ':group:' . __('CSV for MS Excel'),
+ 'Export/excel_null',
+ 'Export/excel_removeCRLF',
+ 'Export/excel_columns',
+ 'Export/excel_edition',
+ ],
+ 'Latex' => [
+ 'Export/latex_caption',
+ 'Export/latex_structure_or_data',
+ ':group:' . __('Structure'),
+ 'Export/latex_structure_caption',
+ 'Export/latex_structure_continued_caption',
+ 'Export/latex_structure_label',
+ 'Export/latex_relation',
+ 'Export/latex_comments',
+ 'Export/latex_mime',
+ ':group:end',
+ ':group:' . __('Data'),
+ 'Export/latex_columns',
+ 'Export/latex_data_caption',
+ 'Export/latex_data_continued_caption',
+ 'Export/latex_data_label',
+ 'Export/latex_null',
+ ],
+ 'Microsoft_Office' => [
+ ':group:' . __('Microsoft Word 2000'),
+ 'Export/htmlword_structure_or_data',
+ 'Export/htmlword_null',
+ 'Export/htmlword_columns',
+ ],
+ 'Open_Document' => [
+ ':group:' . __('OpenDocument Spreadsheet'),
+ 'Export/ods_columns',
+ 'Export/ods_null',
+ ':group:end',
+ ':group:' . __('OpenDocument Text'),
+ 'Export/odt_structure_or_data',
+ ':group:' . __('Structure'),
+ 'Export/odt_relation',
+ 'Export/odt_comments',
+ 'Export/odt_mime',
+ ':group:end',
+ ':group:' . __('Data'),
+ 'Export/odt_columns',
+ 'Export/odt_null',
+ ],
+ 'Texy' => [
+ 'Export/texytext_structure_or_data',
+ ':group:' . __('Data'),
+ 'Export/texytext_null',
+ 'Export/texytext_columns',
+ ],
+ ];
+ // phpcs:enable
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName()
+ {
+ return __('Export');
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/FeaturesForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/FeaturesForm.php
new file mode 100644
index 0000000..58d3c24
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/FeaturesForm.php
@@ -0,0 +1,95 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class FeaturesForm
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class FeaturesForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ $result = [
+ 'General' => [
+ 'VersionCheck',
+ 'NaturalOrder',
+ 'InitialSlidersState',
+ 'SkipLockedTables',
+ 'DisableMultiTableMaintenance',
+ 'ShowHint',
+ 'SendErrorReports',
+ 'ConsoleEnterExecutes',
+ 'DisableShortcutKeys',
+ ],
+ 'Databases' => [
+ 'Servers/1/only_db', // saves to Server/only_db
+ 'Servers/1/hide_db', // saves to Server/hide_db
+ 'MaxDbList',
+ 'MaxTableList',
+ 'DefaultConnectionCollation',
+ ],
+ 'Text_fields' => [
+ 'CharEditing',
+ 'MinSizeForInputField',
+ 'MaxSizeForInputField',
+ 'CharTextareaCols',
+ 'CharTextareaRows',
+ 'TextareaCols',
+ 'TextareaRows',
+ 'LongtextDoubleTextarea',
+ ],
+ 'Page_titles' => [
+ 'TitleDefault',
+ 'TitleTable',
+ 'TitleDatabase',
+ 'TitleServer',
+ ],
+ 'Warnings' => [
+ 'PmaNoRelation_DisableWarning',
+ 'SuhosinDisableWarning',
+ 'LoginCookieValidityDisableWarning',
+ 'ReservedWordDisableWarning',
+ ],
+ 'Console' => [
+ 'Console/Mode',
+ 'Console/StartHistory',
+ 'Console/AlwaysExpand',
+ 'Console/CurrentQuery',
+ 'Console/EnterExecutes',
+ 'Console/DarkTheme',
+ 'Console/Height',
+ 'Console/GroupQueries',
+ 'Console/OrderBy',
+ 'Console/Order',
+ ],
+ ];
+ // skip Developer form if no setting is available
+ if ($GLOBALS['cfg']['UserprefsDeveloperTab']) {
+ $result['Developer'] = [
+ 'DBG/sql',
+ ];
+ }
+ return $result;
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName()
+ {
+ return __('Features');
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/ImportForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/ImportForm.php
new file mode 100644
index 0000000..c447567
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/ImportForm.php
@@ -0,0 +1,73 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class ImportForm
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class ImportForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Import_defaults' => [
+ 'Import/format',
+ 'Import/charset',
+ 'Import/allow_interrupt',
+ 'Import/skip_queries',
+ 'enable_drag_drop_import',
+ ],
+ 'Sql' => [
+ 'Import/sql_compatibility',
+ 'Import/sql_no_auto_value_on_zero',
+ 'Import/sql_read_as_multibytes',
+ ],
+ 'Csv' => [
+ ':group:' . __('CSV'),
+ 'Import/csv_replace',
+ 'Import/csv_ignore',
+ 'Import/csv_terminated',
+ 'Import/csv_enclosed',
+ 'Import/csv_escaped',
+ 'Import/csv_col_names',
+ ':group:end',
+ ':group:' . __('CSV using LOAD DATA'),
+ 'Import/ldi_replace',
+ 'Import/ldi_ignore',
+ 'Import/ldi_terminated',
+ 'Import/ldi_enclosed',
+ 'Import/ldi_escaped',
+ 'Import/ldi_local_option',
+ ],
+ 'Open_Document' => [
+ ':group:' . __('OpenDocument Spreadsheet'),
+ 'Import/ods_col_names',
+ 'Import/ods_empty_rows',
+ 'Import/ods_recognize_percentages',
+ 'Import/ods_recognize_currency',
+ ],
+
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName()
+ {
+ return __('Import');
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/MainForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/MainForm.php
new file mode 100644
index 0000000..907bbaa
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/MainForm.php
@@ -0,0 +1,98 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class MainForm
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class MainForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Startup' => [
+ 'ShowCreateDb',
+ 'ShowStats',
+ 'ShowServerInfo',
+ ],
+ 'DbStructure' => [
+ 'ShowDbStructureCharset',
+ 'ShowDbStructureComment',
+ 'ShowDbStructureCreation',
+ 'ShowDbStructureLastUpdate',
+ 'ShowDbStructureLastCheck',
+ ],
+ 'TableStructure' => [
+ 'HideStructureActions',
+ 'ShowColumnComments',
+ ':group:' . __('Default transformations'),
+ 'DefaultTransformations/Hex',
+ 'DefaultTransformations/Substring',
+ 'DefaultTransformations/Bool2Text',
+ 'DefaultTransformations/External',
+ 'DefaultTransformations/PreApPend',
+ 'DefaultTransformations/DateFormat',
+ 'DefaultTransformations/Inline',
+ 'DefaultTransformations/TextImageLink',
+ 'DefaultTransformations/TextLink',
+ ':group:end',
+ ],
+ 'Browse' => [
+ 'TableNavigationLinksMode',
+ 'ActionLinksMode',
+ 'ShowAll',
+ 'MaxRows',
+ 'Order',
+ 'BrowsePointerEnable',
+ 'BrowseMarkerEnable',
+ 'GridEditing',
+ 'SaveCellsAtOnce',
+ 'RepeatCells',
+ 'LimitChars',
+ 'RowActionLinks',
+ 'RowActionLinksWithoutUnique',
+ 'TablePrimaryKeyOrder',
+ 'RememberSorting',
+ 'RelationalDisplay',
+ ],
+ 'Edit' => [
+ 'ProtectBinary',
+ 'ShowFunctionFields',
+ 'ShowFieldTypesInDataEditView',
+ 'InsertRows',
+ 'ForeignKeyDropdownOrder',
+ 'ForeignKeyMaxLimit',
+ ],
+ 'Tabs' => [
+ 'TabsMode',
+ 'DefaultTabServer',
+ 'DefaultTabDatabase',
+ 'DefaultTabTable',
+ ],
+ 'DisplayRelationalSchema' => [
+ 'PDFDefaultPageSize',
+ ],
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName()
+ {
+ return __('Main panel');
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/NaviForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/NaviForm.php
new file mode 100644
index 0000000..e2d373c
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/NaviForm.php
@@ -0,0 +1,74 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class NaviForm
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class NaviForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Navi_panel' => [
+ 'ShowDatabasesNavigationAsTree',
+ 'NavigationLinkWithMainPanel',
+ 'NavigationDisplayLogo',
+ 'NavigationLogoLink',
+ 'NavigationLogoLinkWindow',
+ 'NavigationTreePointerEnable',
+ 'FirstLevelNavigationItems',
+ 'NavigationTreeDisplayItemFilterMinimum',
+ 'NumRecentTables',
+ 'NumFavoriteTables',
+ 'NavigationWidth',
+ ],
+ 'Navi_tree' => [
+ 'MaxNavigationItems',
+ 'NavigationTreeEnableGrouping',
+ 'NavigationTreeEnableExpansion',
+ 'NavigationTreeShowTables',
+ 'NavigationTreeShowViews',
+ 'NavigationTreeShowFunctions',
+ 'NavigationTreeShowProcedures',
+ 'NavigationTreeShowEvents',
+ 'NavigationTreeAutoexpandSingleDb',
+ ],
+ 'Navi_servers' => [
+ 'NavigationDisplayServers',
+ 'DisplayServersList',
+ ],
+ 'Navi_databases' => [
+ 'NavigationTreeDisplayDbFilterMinimum',
+ 'NavigationTreeDbSeparator',
+ ],
+ 'Navi_tables' => [
+ 'NavigationTreeDefaultTabTable',
+ 'NavigationTreeDefaultTabTable2',
+ 'NavigationTreeTableSeparator',
+ 'NavigationTreeTableLevel',
+ ],
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName()
+ {
+ return __('Navigation panel');
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/SqlForm.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/SqlForm.php
new file mode 100644
index 0000000..3d8ac7a
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/SqlForm.php
@@ -0,0 +1,54 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseForm;
+
+/**
+ * Class SqlForm
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class SqlForm extends BaseForm
+{
+ /**
+ * @return array
+ */
+ public static function getForms()
+ {
+ return [
+ 'Sql_queries' => [
+ 'ShowSQL',
+ 'Confirm',
+ 'QueryHistoryMax',
+ 'IgnoreMultiSubmitErrors',
+ 'MaxCharactersInDisplayedSQL',
+ 'RetainQueryBox',
+ 'CodemirrorEnable',
+ 'LintEnable',
+ 'EnableAutocompleteForTablesAndColumns',
+ 'DefaultForeignKeyChecks',
+ ],
+ 'Sql_box' => [
+ 'SQLQuery/Edit',
+ 'SQLQuery/Explain',
+ 'SQLQuery/ShowAsPHP',
+ 'SQLQuery/Refresh',
+ ],
+ ];
+ }
+
+ /**
+ * @return string
+ */
+ public static function getName()
+ {
+ return __('SQL queries');
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Forms/User/UserFormList.php b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/UserFormList.php
new file mode 100644
index 0000000..92294ed
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Forms/User/UserFormList.php
@@ -0,0 +1,35 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * User preferences form
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config\Forms\User;
+
+use PhpMyAdmin\Config\Forms\BaseFormList;
+
+/**
+ * Class UserFormList
+ * @package PhpMyAdmin\Config\Forms\User
+ */
+class UserFormList extends BaseFormList
+{
+ /**
+ * @var array
+ */
+ protected static $all = [
+ 'Features',
+ 'Sql',
+ 'Navi',
+ 'Main',
+ 'Export',
+ 'Import',
+ ];
+ /**
+ * @var string
+ */
+ protected static $ns = '\\PhpMyAdmin\\Config\\Forms\\User\\';
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/PageSettings.php b/srcs/phpmyadmin/libraries/classes/Config/PageSettings.php
new file mode 100644
index 0000000..57232e1
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/PageSettings.php
@@ -0,0 +1,233 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Page-related settings
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config\ConfigFile;
+use PhpMyAdmin\Config\FormDisplay;
+use PhpMyAdmin\Config\Forms\Page\PageFormList;
+use PhpMyAdmin\Core;
+use PhpMyAdmin\Message;
+use PhpMyAdmin\Response;
+use PhpMyAdmin\UserPreferences;
+
+/**
+ * Page-related settings
+ *
+ * @package PhpMyAdmin
+ */
+class PageSettings
+{
+
+ /**
+ * Contains id of the form element
+ * @var string
+ */
+ private $_elemId = 'page_settings_modal';
+
+ /**
+ * Name of the group to show
+ * @var string
+ */
+ private $_groupName = '';
+
+ /**
+ * Contains HTML of errors
+ * @var string
+ */
+ private $_errorHTML = '';
+
+ /**
+ * Contains HTML of settings
+ * @var string
+ */
+ private $_HTML = '';
+
+ /**
+ * @var UserPreferences
+ */
+ private $userPreferences;
+
+ /**
+ * Constructor
+ *
+ * @param string $formGroupName The name of config form group to display
+ * @param string $elemId Id of the div containing settings
+ */
+ public function __construct($formGroupName, $elemId = null)
+ {
+ $this->userPreferences = new UserPreferences();
+
+ $formClass = PageFormList::get($formGroupName);
+ if ($formClass === null) {
+ return;
+ }
+
+ if (isset($_REQUEST['printview']) && $_REQUEST['printview'] == '1') {
+ return;
+ }
+
+ if (! empty($elemId)) {
+ $this->_elemId = $elemId;
+ }
+ $this->_groupName = $formGroupName;
+
+ $cf = new ConfigFile($GLOBALS['PMA_Config']->base_settings);
+ $this->userPreferences->pageInit($cf);
+
+ $formDisplay = new $formClass($cf);
+
+ // Process form
+ $error = null;
+ if (isset($_POST['submit_save'])
+ && $_POST['submit_save'] == $formGroupName
+ ) {
+ $this->_processPageSettings($formDisplay, $cf, $error);
+ }
+
+ // Display forms
+ $this->_HTML = $this->_getPageSettingsDisplay($formDisplay, $error);
+ }
+
+ /**
+ * Process response to form
+ *
+ * @param FormDisplay $formDisplay Form
+ * @param ConfigFile $cf Configuration file
+ * @param Message|null $error Error message
+ *
+ * @return void
+ */
+ private function _processPageSettings(&$formDisplay, &$cf, &$error)
+ {
+ if ($formDisplay->process(false) && ! $formDisplay->hasErrors()) {
+ // save settings
+ $result = $this->userPreferences->save($cf->getConfigArray());
+ if ($result === true) {
+ // reload page
+ $response = Response::getInstance();
+ Core::sendHeaderLocation(
+ $response->getFooter()->getSelfUrl()
+ );
+ exit;
+ } else {
+ $error = $result;
+ }
+ }
+ }
+
+ /**
+ * Store errors in _errorHTML
+ *
+ * @param FormDisplay $formDisplay Form
+ * @param Message|null $error Error message
+ *
+ * @return void
+ */
+ private function _storeError(&$formDisplay, &$error)
+ {
+ $retval = '';
+ if ($error) {
+ $retval .= $error->getDisplay();
+ }
+ if ($formDisplay->hasErrors()) {
+ // form has errors
+ $retval .= '<div class="error config-form">'
+ . '<b>' . __(
+ 'Cannot save settings, submitted configuration form contains '
+ . 'errors!'
+ ) . '</b>'
+ . $formDisplay->displayErrors()
+ . '</div>';
+ }
+ $this->_errorHTML = $retval;
+ }
+
+ /**
+ * Display page-related settings
+ *
+ * @param FormDisplay $formDisplay Form
+ * @param Message $error Error message
+ *
+ * @return string
+ */
+ private function _getPageSettingsDisplay(&$formDisplay, &$error)
+ {
+ $response = Response::getInstance();
+
+ $retval = '';
+
+ $this->_storeError($formDisplay, $error);
+
+ $retval .= '<div id="' . $this->_elemId . '">';
+ $retval .= '<div class="page_settings">';
+ $retval .= $formDisplay->getDisplay(
+ true,
+ true,
+ false,
+ $response->getFooter()->getSelfUrl(),
+ [
+ 'submit_save' => $this->_groupName,
+ ]
+ );
+ $retval .= '</div>';
+ $retval .= '</div>';
+
+ return $retval;
+ }
+
+ /**
+ * Get HTML output
+ *
+ * @return string
+ */
+ public function getHTML()
+ {
+ return $this->_HTML;
+ }
+
+ /**
+ * Get error HTML output
+ *
+ * @return string
+ */
+ public function getErrorHTML()
+ {
+ return $this->_errorHTML;
+ }
+
+ /**
+ * Group to show for Page-related settings
+ * @param string $formGroupName The name of config form group to display
+ * @return PageSettings
+ */
+ public static function showGroup($formGroupName)
+ {
+ $object = new PageSettings($formGroupName);
+
+ $response = Response::getInstance();
+ $response->addHTML($object->getErrorHTML());
+ $response->addHTML($object->getHTML());
+
+ return $object;
+ }
+
+ /**
+ * Get HTML for navigation settings
+ * @return string
+ */
+ public static function getNaviSettings()
+ {
+ $object = new PageSettings('Navi', 'pma_navigation_settings');
+
+ $response = Response::getInstance();
+ $response->addHTML($object->getErrorHTML());
+ return $object->getHTML();
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/ServerConfigChecks.php b/srcs/phpmyadmin/libraries/classes/Config/ServerConfigChecks.php
new file mode 100644
index 0000000..1b0ac9d
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/ServerConfigChecks.php
@@ -0,0 +1,583 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Server config checks management
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config\ConfigFile;
+use PhpMyAdmin\Config\Descriptions;
+use PhpMyAdmin\Core;
+use PhpMyAdmin\Sanitize;
+use PhpMyAdmin\Setup\Index as SetupIndex;
+use PhpMyAdmin\Url;
+use PhpMyAdmin\Util;
+
+/**
+ * Performs various compatibility, security and consistency checks on current config
+ *
+ * Outputs results to message list, must be called between SetupIndex::messagesBegin()
+ * and SetupIndex::messagesEnd()
+ *
+ * @package PhpMyAdmin
+ */
+class ServerConfigChecks
+{
+ /**
+ * @var ConfigFile configurations being checked
+ */
+ protected $cfg;
+
+ /**
+ * Constructor.
+ *
+ * @param ConfigFile $cfg Configuration
+ */
+ public function __construct(ConfigFile $cfg)
+ {
+ $this->cfg = $cfg;
+ }
+
+ /**
+ * Perform config checks
+ *
+ * @return void
+ */
+ public function performConfigChecks()
+ {
+ $blowfishSecret = $this->cfg->get('blowfish_secret');
+ $blowfishSecretSet = false;
+ $cookieAuthUsed = false;
+
+ list($cookieAuthUsed, $blowfishSecret, $blowfishSecretSet)
+ = $this->performConfigChecksServers(
+ $cookieAuthUsed,
+ $blowfishSecret,
+ $blowfishSecretSet
+ );
+
+ $this->performConfigChecksCookieAuthUsed(
+ $cookieAuthUsed,
+ $blowfishSecretSet,
+ $blowfishSecret
+ );
+
+ //
+ // $cfg['AllowArbitraryServer']
+ // should be disabled
+ //
+ if ($this->cfg->getValue('AllowArbitraryServer')) {
+ $sAllowArbitraryServerWarn = sprintf(
+ __(
+ 'This %soption%s should be disabled as it allows attackers to '
+ . 'bruteforce login to any MySQL server. If you feel this is necessary, '
+ . 'use %srestrict login to MySQL server%s or %strusted proxies list%s. '
+ . 'However, IP-based protection with trusted proxies list may not be '
+ . 'reliable if your IP belongs to an ISP where thousands of users, '
+ . 'including you, are connected to.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]',
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]',
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]'
+ );
+ SetupIndex::messagesSet(
+ 'notice',
+ 'AllowArbitraryServer',
+ Descriptions::get('AllowArbitraryServer'),
+ Sanitize::sanitizeMessage($sAllowArbitraryServerWarn)
+ );
+ }
+
+ $this->performConfigChecksLoginCookie();
+
+ $sDirectoryNotice = __(
+ '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.'
+ );
+
+ //
+ // $cfg['SaveDir']
+ // should not be world-accessible
+ //
+ if ($this->cfg->getValue('SaveDir') != '') {
+ SetupIndex::messagesSet(
+ 'notice',
+ 'SaveDir',
+ Descriptions::get('SaveDir'),
+ Sanitize::sanitizeMessage($sDirectoryNotice)
+ );
+ }
+
+ //
+ // $cfg['TempDir']
+ // should not be world-accessible
+ //
+ if ($this->cfg->getValue('TempDir') != '') {
+ SetupIndex::messagesSet(
+ 'notice',
+ 'TempDir',
+ Descriptions::get('TempDir'),
+ Sanitize::sanitizeMessage($sDirectoryNotice)
+ );
+ }
+
+ $this->performConfigChecksZips();
+ }
+
+ /**
+ * Check config of servers
+ *
+ * @param boolean $cookieAuthUsed Cookie auth is used
+ * @param string $blowfishSecret Blowfish secret
+ * @param boolean $blowfishSecretSet Blowfish secret set
+ *
+ * @return array
+ */
+ protected function performConfigChecksServers(
+ $cookieAuthUsed,
+ $blowfishSecret,
+ $blowfishSecretSet
+ ) {
+ $serverCnt = $this->cfg->getServerCount();
+ for ($i = 1; $i <= $serverCnt; $i++) {
+ $cookieAuthServer
+ = ($this->cfg->getValue("Servers/$i/auth_type") == 'cookie');
+ $cookieAuthUsed |= $cookieAuthServer;
+ $serverName = $this->performConfigChecksServersGetServerName(
+ $this->cfg->getServerName($i),
+ $i
+ );
+ $serverName = htmlspecialchars($serverName);
+
+ list($blowfishSecret, $blowfishSecretSet)
+ = $this->performConfigChecksServersSetBlowfishSecret(
+ $blowfishSecret,
+ $cookieAuthServer,
+ $blowfishSecretSet
+ );
+
+ //
+ // $cfg['Servers'][$i]['ssl']
+ // should be enabled if possible
+ //
+ if (! $this->cfg->getValue("Servers/$i/ssl")) {
+ $title = Descriptions::get('Servers/1/ssl') . " ($serverName)";
+ SetupIndex::messagesSet(
+ 'notice',
+ "Servers/$i/ssl",
+ $title,
+ __(
+ 'You should use SSL connections if your database server '
+ . 'supports it.'
+ )
+ );
+ }
+ $sSecurityInfoMsg = Sanitize::sanitizeMessage(sprintf(
+ __(
+ 'If you feel this is necessary, use additional protection settings - '
+ . '%1$shost authentication%2$s settings and %3$strusted proxies list%4%s. '
+ . 'However, IP-based protection may not be reliable if your IP belongs '
+ . 'to an ISP where thousands of users, including you, are connected to.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'servers', 'mode' => 'edit', 'id' => $i]) . '#tab_Server_config]',
+ '[/a]',
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]'
+ ));
+
+ //
+ // $cfg['Servers'][$i]['auth_type']
+ // warn about full user credentials if 'auth_type' is 'config'
+ //
+ if ($this->cfg->getValue("Servers/$i/auth_type") == 'config'
+ && $this->cfg->getValue("Servers/$i/user") != ''
+ && $this->cfg->getValue("Servers/$i/password") != ''
+ ) {
+ $title = Descriptions::get('Servers/1/auth_type')
+ . " ($serverName)";
+ SetupIndex::messagesSet(
+ 'notice',
+ "Servers/$i/auth_type",
+ $title,
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ '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 %1$sauthentication '
+ . 'type%2$s to [kbd]cookie[/kbd] or [kbd]http[/kbd].'
+ ),
+ '[a@' . Url::getCommon(['page' => 'servers', 'mode' => 'edit', 'id' => $i]) . '#tab_Server]',
+ '[/a]'
+ ))
+ . ' ' . $sSecurityInfoMsg
+ );
+ }
+
+ //
+ // $cfg['Servers'][$i]['AllowRoot']
+ // $cfg['Servers'][$i]['AllowNoPassword']
+ // serious security flaw
+ //
+ if ($this->cfg->getValue("Servers/$i/AllowRoot")
+ && $this->cfg->getValue("Servers/$i/AllowNoPassword")
+ ) {
+ $title = Descriptions::get('Servers/1/AllowNoPassword')
+ . " ($serverName)";
+ SetupIndex::messagesSet(
+ 'notice',
+ "Servers/$i/AllowNoPassword",
+ $title,
+ __('You allow for connecting to the server without a password.')
+ . ' ' . $sSecurityInfoMsg
+ );
+ }
+ }
+ return [
+ $cookieAuthUsed,
+ $blowfishSecret,
+ $blowfishSecretSet,
+ ];
+ }
+
+ /**
+ * Set blowfish secret
+ *
+ * @param string $blowfishSecret Blowfish secret
+ * @param boolean $cookieAuthServer Cookie auth is used
+ * @param boolean $blowfishSecretSet Blowfish secret set
+ *
+ * @return array
+ */
+ protected function performConfigChecksServersSetBlowfishSecret(
+ $blowfishSecret,
+ $cookieAuthServer,
+ $blowfishSecretSet
+ ) {
+ if ($cookieAuthServer && $blowfishSecret === null) {
+ $blowfishSecretSet = true;
+ $this->cfg->set('blowfish_secret', Util::generateRandom(32));
+ }
+ return [
+ $blowfishSecret,
+ $blowfishSecretSet,
+ ];
+ }
+
+ /**
+ * Define server name
+ *
+ * @param string $serverName Server name
+ * @param int $serverId Server id
+ *
+ * @return string Server name
+ */
+ protected function performConfigChecksServersGetServerName(
+ $serverName,
+ $serverId
+ ) {
+ if ($serverName == 'localhost') {
+ $serverName .= " [$serverId]";
+ return $serverName;
+ }
+ return $serverName;
+ }
+
+ /**
+ * Perform config checks for zip part.
+ *
+ * @return void
+ */
+ protected function performConfigChecksZips()
+ {
+ $this->performConfigChecksServerGZipdump();
+ $this->performConfigChecksServerBZipdump();
+ $this->performConfigChecksServersZipdump();
+ }
+
+ /**
+ * Perform config checks for zip part.
+ *
+ * @return void
+ */
+ protected function performConfigChecksServersZipdump()
+ {
+ //
+ // $cfg['ZipDump']
+ // requires zip_open in import
+ //
+ if ($this->cfg->getValue('ZipDump') && ! $this->functionExists('zip_open')) {
+ SetupIndex::messagesSet(
+ 'error',
+ 'ZipDump_import',
+ Descriptions::get('ZipDump'),
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ '%sZip decompression%s requires functions (%s) which are unavailable '
+ . 'on this system.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
+ '[/a]',
+ 'zip_open'
+ ))
+ );
+ }
+
+ //
+ // $cfg['ZipDump']
+ // requires gzcompress in export
+ //
+ if ($this->cfg->getValue('ZipDump') && ! $this->functionExists('gzcompress')) {
+ SetupIndex::messagesSet(
+ 'error',
+ 'ZipDump_export',
+ Descriptions::get('ZipDump'),
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ '%sZip compression%s requires functions (%s) which are unavailable on '
+ . 'this system.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
+ '[/a]',
+ 'gzcompress'
+ ))
+ );
+ }
+ }
+
+ /**
+ * Check config of servers
+ *
+ * @param boolean $cookieAuthUsed Cookie auth is used
+ * @param boolean $blowfishSecretSet Blowfish secret set
+ * @param string $blowfishSecret Blowfish secret
+ *
+ * @return void
+ */
+ protected function performConfigChecksCookieAuthUsed(
+ $cookieAuthUsed,
+ $blowfishSecretSet,
+ $blowfishSecret
+ ) {
+ //
+ // $cfg['blowfish_secret']
+ // it's required for 'cookie' authentication
+ //
+ if ($cookieAuthUsed) {
+ if ($blowfishSecretSet) {
+ // 'cookie' auth used, blowfish_secret was generated
+ SetupIndex::messagesSet(
+ 'notice',
+ 'blowfish_secret_created',
+ Descriptions::get('blowfish_secret'),
+ Sanitize::sanitizeMessage(__(
+ 'You didn\'t have blowfish secret set and have enabled '
+ . '[kbd]cookie[/kbd] authentication, so a key was automatically '
+ . 'generated for you. It is used to encrypt cookies; you don\'t need to '
+ . 'remember it.'
+ ))
+ );
+ } else {
+ $blowfishWarnings = [];
+ // check length
+ if (strlen($blowfishSecret) < 32) {
+ // too short key
+ $blowfishWarnings[] = __(
+ 'Key is too short, it should have at least 32 characters.'
+ );
+ }
+ // check used characters
+ $hasDigits = (bool) preg_match('/\d/', $blowfishSecret);
+ $hasChars = (bool) preg_match('/\S/', $blowfishSecret);
+ $hasNonword = (bool) preg_match('/\W/', $blowfishSecret);
+ if (! $hasDigits || ! $hasChars || ! $hasNonword) {
+ $blowfishWarnings[] = Sanitize::sanitizeMessage(
+ __(
+ 'Key should contain letters, numbers [em]and[/em] '
+ . 'special characters.'
+ )
+ );
+ }
+ if (! empty($blowfishWarnings)) {
+ SetupIndex::messagesSet(
+ 'error',
+ 'blowfish_warnings' . count($blowfishWarnings),
+ Descriptions::get('blowfish_secret'),
+ implode('<br>', $blowfishWarnings)
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Check configuration for login cookie
+ *
+ * @return void
+ */
+ protected function performConfigChecksLoginCookie()
+ {
+ //
+ // $cfg['LoginCookieValidity']
+ // value greater than session.gc_maxlifetime will cause
+ // random session invalidation after that time
+ $loginCookieValidity = $this->cfg->getValue('LoginCookieValidity');
+ if ($loginCookieValidity > ini_get('session.gc_maxlifetime')
+ ) {
+ SetupIndex::messagesSet(
+ 'error',
+ 'LoginCookieValidity',
+ Descriptions::get('LoginCookieValidity'),
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ '%1$sLogin cookie validity%2$s greater than %3$ssession.gc_maxlifetime%4$s may '
+ . 'cause random session invalidation (currently session.gc_maxlifetime '
+ . 'is %5$d).'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]',
+ '[a@' . Core::getPHPDocLink('session.configuration.php#ini.session.gc-maxlifetime') . ']',
+ '[/a]',
+ ini_get('session.gc_maxlifetime')
+ ))
+ );
+ }
+
+ //
+ // $cfg['LoginCookieValidity']
+ // should be at most 1800 (30 min)
+ //
+ if ($loginCookieValidity > 1800) {
+ SetupIndex::messagesSet(
+ 'notice',
+ 'LoginCookieValidity',
+ Descriptions::get('LoginCookieValidity'),
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ '%sLogin cookie validity%s should be set to 1800 seconds (30 minutes) '
+ . 'at most. Values larger than 1800 may pose a security risk such as '
+ . 'impersonation.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]'
+ ))
+ );
+ }
+
+ //
+ // $cfg['LoginCookieValidity']
+ // $cfg['LoginCookieStore']
+ // LoginCookieValidity must be less or equal to LoginCookieStore
+ //
+ if (($this->cfg->getValue('LoginCookieStore') != 0)
+ && ($loginCookieValidity > $this->cfg->getValue('LoginCookieStore'))
+ ) {
+ SetupIndex::messagesSet(
+ 'error',
+ 'LoginCookieValidity',
+ Descriptions::get('LoginCookieValidity'),
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ 'If using [kbd]cookie[/kbd] authentication and %sLogin cookie store%s '
+ . 'is not 0, %sLogin cookie validity%s must be set to a value less or '
+ . 'equal to it.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]',
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Security]',
+ '[/a]'
+ ))
+ );
+ }
+ }
+
+ /**
+ * Check GZipDump configuration
+ *
+ * @return void
+ */
+ protected function performConfigChecksServerBZipdump()
+ {
+ //
+ // $cfg['BZipDump']
+ // requires bzip2 functions
+ //
+ if ($this->cfg->getValue('BZipDump')
+ && (! $this->functionExists('bzopen') || ! $this->functionExists('bzcompress'))
+ ) {
+ $functions = $this->functionExists('bzopen')
+ ? '' :
+ 'bzopen';
+ $functions .= $this->functionExists('bzcompress')
+ ? ''
+ : ($functions ? ', ' : '') . 'bzcompress';
+ SetupIndex::messagesSet(
+ 'error',
+ 'BZipDump',
+ Descriptions::get('BZipDump'),
+ Sanitize::sanitizeMessage(
+ sprintf(
+ __(
+ '%1$sBzip2 compression and decompression%2$s requires functions (%3$s) which '
+ . 'are unavailable on this system.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
+ '[/a]',
+ $functions
+ )
+ )
+ );
+ }
+ }
+
+ /**
+ * Check GZipDump configuration
+ *
+ * @return void
+ */
+ protected function performConfigChecksServerGZipdump()
+ {
+ //
+ // $cfg['GZipDump']
+ // requires zlib functions
+ //
+ if ($this->cfg->getValue('GZipDump')
+ && (! $this->functionExists('gzopen') || ! $this->functionExists('gzencode'))
+ ) {
+ SetupIndex::messagesSet(
+ 'error',
+ 'GZipDump',
+ Descriptions::get('GZipDump'),
+ Sanitize::sanitizeMessage(sprintf(
+ __(
+ '%1$sGZip compression and decompression%2$s requires functions (%3$s) which '
+ . 'are unavailable on this system.'
+ ),
+ '[a@' . Url::getCommon(['page' => 'form', 'formset' => 'Features']) . '#tab_Import_export]',
+ '[/a]',
+ 'gzencode'
+ ))
+ );
+ }
+ }
+
+ /**
+ * Wrapper around function_exists to allow mock in test
+ *
+ * @param string $name Function name
+ *
+ * @return boolean
+ */
+ protected function functionExists($name)
+ {
+ return function_exists($name);
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/SpecialSchemaLinks.php b/srcs/phpmyadmin/libraries/classes/Config/SpecialSchemaLinks.php
new file mode 100644
index 0000000..e0af136
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/SpecialSchemaLinks.php
@@ -0,0 +1,478 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Links configuration for MySQL system tables
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Util;
+
+/**
+ * Class SpecialSchemaLinks
+ * @package PhpMyAdmin\Config
+ */
+class SpecialSchemaLinks
+{
+ /**
+ * This array represent the details for generating links inside
+ * special schemas like mysql, information_schema etc.
+ * Major element represent a schema.
+ * All the strings in this array represented in lower case
+ *
+ * Array structure ex:
+ * array(
+ * // Database name is the major element
+ * 'mysql' => array(
+ * // Table name
+ * 'db' => array(
+ * // Column name
+ * 'user' => array(
+ * // Main url param (can be an array where represent sql)
+ * 'link_param' => 'username',
+ * // Other url params
+ * 'link_dependancy_params' => array(
+ * 0 => array(
+ * // URL parameter name
+ * // (can be array where url param has static value)
+ * 'param_info' => 'hostname',
+ * // Column name related to url param
+ * 'column_name' => 'host'
+ * )
+ * ),
+ * // Page to link
+ * 'default_page' => './server_privileges.php'
+ * )
+ * )
+ * )
+ * );
+ *
+ * @return array
+ */
+ public static function get(): array
+ {
+ global $cfg;
+
+ $defaultPage = './' . Util::getScriptNameForOption(
+ $cfg['DefaultTabTable'],
+ 'table'
+ );
+
+ return [
+ 'mysql' => [
+ 'columns_priv' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'Db',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'column_name' => [
+ 'link_param' => 'field',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'Db',
+ ],
+ 1 => [
+ 'param_info' => 'table',
+ 'column_name' => 'Table_name',
+ ],
+ ],
+ 'default_page' => './tbl_structure.php?change_column=1',
+ ],
+ ],
+ 'db' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ ],
+ 'event' => [
+ 'name' => [
+ 'link_param' => 'item_name',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'db',
+ ],
+ ],
+ 'default_page' => './db_events.php?edit_item=1',
+ ],
+
+ ],
+ 'innodb_index_stats' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'database_name',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'index_name' => [
+ 'link_param' => 'index',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'database_name',
+ ],
+ 1 => [
+ 'param_info' => 'table',
+ 'column_name' => 'table_name',
+ ],
+ ],
+ 'default_page' => './tbl_structure.php',
+ ],
+ ],
+ 'innodb_table_stats' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'database_name',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'proc' => [
+ 'name' => [
+ 'link_param' => 'item_name',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'db',
+ ],
+ 1 => [
+ 'param_info' => 'item_type',
+ 'column_name' => 'type',
+ ],
+ ],
+ 'default_page' => './db_routines.php?edit_item=1',
+ ],
+ 'specific_name' => [
+ 'link_param' => 'item_name',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'db',
+ ],
+ 1 => [
+ 'param_info' => 'item_type',
+ 'column_name' => 'type',
+ ],
+ ],
+ 'default_page' => './db_routines.php?edit_item=1',
+ ],
+ ],
+ 'proc_priv' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'Host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ 'routine_name' => [
+ 'link_param' => 'item_name',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'Db',
+ ],
+ 1 => [
+ 'param_info' => 'item_type',
+ 'column_name' => 'Routine_type',
+ ],
+ ],
+ 'default_page' => './db_routines.php?edit_item=1',
+ ],
+ ],
+ 'proxies_priv' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'Host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ ],
+ 'tables_priv' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'Host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'Db',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'user' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ ],
+ ],
+ 'information_schema' => [
+ 'columns' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'column_name' => [
+ 'link_param' => 'field',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ 1 => [
+ 'param_info' => 'table',
+ 'column_name' => 'table_name',
+ ],
+ ],
+ 'default_page' => './tbl_structure.php?change_column=1',
+ ],
+ ],
+ 'key_column_usage' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'constraint_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'column_name' => [
+ 'link_param' => 'field',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ 1 => [
+ 'param_info' => 'table',
+ 'column_name' => 'table_name',
+ ],
+ ],
+ 'default_page' => './tbl_structure.php?change_column=1',
+ ],
+ 'referenced_table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'referenced_table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'referenced_column_name' => [
+ 'link_param' => 'field',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'referenced_table_schema',
+ ],
+ 1 => [
+ 'param_info' => 'table',
+ 'column_name' => 'referenced_table_name',
+ ],
+ ],
+ 'default_page' => './tbl_structure.php?change_column=1',
+ ],
+ ],
+ 'partitions' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'processlist' => [
+ 'user' => [
+ 'link_param' => 'username',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'hostname',
+ 'column_name' => 'host',
+ ],
+ ],
+ 'default_page' => './server_privileges.php',
+ ],
+ ],
+ 'referential_constraints' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'constraint_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'referenced_table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'constraint_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'routines' => [
+ 'routine_name' => [
+ 'link_param' => 'item_name',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'routine_schema',
+ ],
+ 1 => [
+ 'param_info' => 'item_type',
+ 'column_name' => 'routine_type',
+ ],
+ ],
+ 'default_page' => './db_routines.php',
+ ],
+ ],
+ 'schemata' => [
+ 'schema_name' => [
+ 'link_param' => 'db',
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'statistics' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ 'column_name' => [
+ 'link_param' => 'field',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ 1 => [
+ 'param_info' => 'table',
+ 'column_name' => 'table_name',
+ ],
+ ],
+ 'default_page' => './tbl_structure.php?change_column=1',
+ ],
+ ],
+ 'tables' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'table_constraints' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ 'views' => [
+ 'table_name' => [
+ 'link_param' => 'table',
+ 'link_dependancy_params' => [
+ 0 => [
+ 'param_info' => 'db',
+ 'column_name' => 'table_schema',
+ ],
+ ],
+ 'default_page' => $defaultPage,
+ ],
+ ],
+ ],
+ ];
+ }
+}
diff --git a/srcs/phpmyadmin/libraries/classes/Config/Validator.php b/srcs/phpmyadmin/libraries/classes/Config/Validator.php
new file mode 100644
index 0000000..8294a90
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Config/Validator.php
@@ -0,0 +1,594 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Form validation for configuration editor
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin\Config;
+
+use PhpMyAdmin\Config\ConfigFile;
+use PhpMyAdmin\Core;
+use PhpMyAdmin\DatabaseInterface;
+use PhpMyAdmin\Util;
+use function mysql_close;
+use function mysql_connect;
+use function mysqli_close;
+use function mysqli_connect;
+
+/**
+ * Validation class for various validation functions
+ *
+ * Validation function takes two argument: id for which it is called
+ * and array of fields' values (usually values for entire formset, as defined
+ * in forms.inc.php).
+ * The function must always return an array with an error (or error array)
+ * assigned to a form element (formset name or field path). Even if there are
+ * no errors, key must be set with an empty value.
+ *
+ * Validation functions are assigned in $cfg_db['_validators'] (config.values.php).
+ *
+ * @package PhpMyAdmin
+ */
+class Validator
+{
+ /**
+ * Returns validator list
+ *
+ * @param ConfigFile $cf Config file instance
+ *
+ * @return array
+ */
+ public static function getValidators(ConfigFile $cf)
+ {
+ static $validators = null;
+
+ if ($validators !== null) {
+ return $validators;
+ }
+
+ $validators = $cf->getDbEntry('_validators', []);
+ if ($GLOBALS['PMA_Config']->get('is_setup')) {
+ return $validators;
+ }
+
+ // not in setup script: load additional validators for user
+ // preferences we need original config values not overwritten
+ // by user preferences, creating a new PhpMyAdmin\Config instance is a
+ // better idea than hacking into its code
+ $uvs = $cf->getDbEntry('_userValidators', []);
+ foreach ($uvs as $field => $uvList) {
+ $uvList = (array) $uvList;
+ foreach ($uvList as &$uv) {
+ if (! is_array($uv)) {
+ continue;
+ }
+ for ($i = 1, $nb = count($uv); $i < $nb; $i++) {
+ if (mb_substr($uv[$i], 0, 6) == 'value:') {
+ $uv[$i] = Core::arrayRead(
+ mb_substr($uv[$i], 6),
+ $GLOBALS['PMA_Config']->base_settings
+ );
+ }
+ }
+ }
+ $validators[$field] = isset($validators[$field])
+ ? array_merge((array) $validators[$field], $uvList)
+ : $uvList;
+ }
+ return $validators;
+ }
+
+ /**
+ * Runs validation $validator_id on values $values and returns error list.
+ *
+ * Return values:
+ * o array, keys - field path or formset id, values - array of errors
+ * when $isPostSource is true values is an empty array to allow for error list
+ * cleanup in HTML document
+ * o false - when no validators match name(s) given by $validator_id
+ *
+ * @param ConfigFile $cf Config file instance
+ * @param string|array $validatorId ID of validator(s) to run
+ * @param array $values Values to validate
+ * @param bool $isPostSource tells whether $values are directly from
+ * POST request
+ *
+ * @return bool|array
+ */
+ public static function validate(
+ ConfigFile $cf,
+ $validatorId,
+ array &$values,
+ $isPostSource
+ ) {
+ // find validators
+ $validatorId = (array) $validatorId;
+ $validators = static::getValidators($cf);
+ $vids = [];
+ foreach ($validatorId as &$vid) {
+ $vid = $cf->getCanonicalPath($vid);
+ if (isset($validators[$vid])) {
+ $vids[] = $vid;
+ }
+ }
+ if (empty($vids)) {
+ return false;
+ }
+
+ // create argument list with canonical paths and remember path mapping
+ $arguments = [];
+ $keyMap = [];
+ foreach ($values as $k => $v) {
+ $k2 = $isPostSource ? str_replace('-', '/', $k) : $k;
+ $k2 = mb_strpos($k2, '/')
+ ? $cf->getCanonicalPath($k2)
+ : $k2;
+ $keyMap[$k2] = $k;
+ $arguments[$k2] = $v;
+ }
+
+ // validate
+ $result = [];
+ foreach ($vids as $vid) {
+ // call appropriate validation functions
+ foreach ((array) $validators[$vid] as $validator) {
+ $vdef = (array) $validator;
+ $vname = array_shift($vdef);
+ $vname = 'PhpMyAdmin\Config\Validator::' . $vname;
+ $args = array_merge([$vid, &$arguments], $vdef);
+ $r = call_user_func_array($vname, $args);
+
+ // merge results
+ if (! is_array($r)) {
+ continue;
+ }
+
+ foreach ($r as $key => $errorList) {
+ // skip empty values if $isPostSource is false
+ if (! $isPostSource && empty($errorList)) {
+ continue;
+ }
+ if (! isset($result[$key])) {
+ $result[$key] = [];
+ }
+ $result[$key] = array_merge(
+ $result[$key],
+ (array) $errorList
+ );
+ }
+ }
+ }
+
+ // restore original paths
+ $newResult = [];
+ foreach ($result as $k => $v) {
+ $k2 = isset($keyMap[$k]) ? $keyMap[$k] : $k;
+ if (is_array($v)) {
+ $newResult[$k2] = array_map('htmlspecialchars', $v);
+ } else {
+ $newResult[$k2] = htmlspecialchars($v);
+ }
+ }
+ return empty($newResult) ? true : $newResult;
+ }
+
+ /**
+ * Test database connection
+ *
+ * @param string $host host name
+ * @param string $port tcp port to use
+ * @param string $socket socket to use
+ * @param string $user username to use
+ * @param string $pass password to use
+ * @param string $errorKey key to use in return array
+ *
+ * @return bool|array
+ */
+ public static function testDBConnection(
+ $host,
+ $port,
+ $socket,
+ $user,
+ $pass = null,
+ $errorKey = 'Server'
+ ) {
+ if ($GLOBALS['cfg']['DBG']['demo']) {
+ // Connection test disabled on the demo server!
+ return true;
+ }
+
+ $error = null;
+ $host = Core::sanitizeMySQLHost($host);
+
+ error_clear_last();
+
+ if (DatabaseInterface::checkDbExtension('mysqli')) {
+ $socket = empty($socket) ? null : $socket;
+ $port = empty($port) ? null : $port;
+ $extension = 'mysqli';
+ } else {
+ $socket = empty($socket) ? null : ':' . ($socket[0] == '/' ? '' : '/') . $socket;
+ $port = empty($port) ? null : ':' . $port;
+ $extension = 'mysql';
+ }
+
+ if ($extension == 'mysql') {
+ $conn = @mysql_connect($host . $port . $socket, $user, $pass);
+ if (! $conn) {
+ $error = __('Could not connect to the database server!');
+ } else {
+ mysql_close($conn);
+ }
+ } else {
+ $conn = @mysqli_connect($host, $user, $pass, null, $port, $socket);
+ if (! $conn) {
+ $error = __('Could not connect to the database server!');
+ } else {
+ mysqli_close($conn);
+ }
+ }
+ if ($error !== null) {
+ $lastError = error_get_last();
+ if ($lastError !== null) {
+ $error .= ' - ' . $lastError['message'];
+ }
+ }
+ return $error === null ? true : [$errorKey => $error];
+ }
+
+ /**
+ * Validate server config
+ *
+ * @param string $path path to config, not used
+ * keep this parameter since the method is invoked using
+ * reflection along with other similar methods
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateServer($path, array $values)
+ {
+ $result = [
+ 'Server' => '',
+ 'Servers/1/user' => '',
+ 'Servers/1/SignonSession' => '',
+ 'Servers/1/SignonURL' => '',
+ ];
+ $error = false;
+ if (empty($values['Servers/1/auth_type'])) {
+ $values['Servers/1/auth_type'] = '';
+ $result['Servers/1/auth_type'] = __('Invalid authentication type!');
+ $error = true;
+ }
+ if ($values['Servers/1/auth_type'] == 'config'
+ && empty($values['Servers/1/user'])
+ ) {
+ $result['Servers/1/user'] = __(
+ 'Empty username while using [kbd]config[/kbd] authentication method!'
+ );
+ $error = true;
+ }
+ if ($values['Servers/1/auth_type'] == 'signon'
+ && empty($values['Servers/1/SignonSession'])
+ ) {
+ $result['Servers/1/SignonSession'] = __(
+ 'Empty signon session name '
+ . 'while using [kbd]signon[/kbd] authentication method!'
+ );
+ $error = true;
+ }
+ if ($values['Servers/1/auth_type'] == 'signon'
+ && empty($values['Servers/1/SignonURL'])
+ ) {
+ $result['Servers/1/SignonURL'] = __(
+ 'Empty signon URL while using [kbd]signon[/kbd] authentication '
+ . 'method!'
+ );
+ $error = true;
+ }
+
+ if (! $error && $values['Servers/1/auth_type'] == 'config') {
+ $password = '';
+ if (! empty($values['Servers/1/password'])) {
+ $password = $values['Servers/1/password'];
+ }
+ $test = static::testDBConnection(
+ empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
+ empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
+ empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
+ empty($values['Servers/1/user']) ? '' : $values['Servers/1/user'],
+ $password,
+ 'Server'
+ );
+
+ if ($test !== true) {
+ $result = array_merge($result, $test);
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Validate pmadb config
+ *
+ * @param string $path path to config, not used
+ * keep this parameter since the method is invoked using
+ * reflection along with other similar methods
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validatePMAStorage($path, array $values)
+ {
+ $result = [
+ 'Server_pmadb' => '',
+ 'Servers/1/controluser' => '',
+ 'Servers/1/controlpass' => '',
+ ];
+ $error = false;
+
+ if (empty($values['Servers/1/pmadb'])) {
+ return $result;
+ }
+
+ $result = [];
+ if (empty($values['Servers/1/controluser'])) {
+ $result['Servers/1/controluser'] = __(
+ 'Empty phpMyAdmin control user while using phpMyAdmin configuration '
+ . 'storage!'
+ );
+ $error = true;
+ }
+ if (empty($values['Servers/1/controlpass'])) {
+ $result['Servers/1/controlpass'] = __(
+ 'Empty phpMyAdmin control user password while using phpMyAdmin '
+ . 'configuration storage!'
+ );
+ $error = true;
+ }
+ if (! $error) {
+ $test = static::testDBConnection(
+ empty($values['Servers/1/host']) ? '' : $values['Servers/1/host'],
+ empty($values['Servers/1/port']) ? '' : $values['Servers/1/port'],
+ empty($values['Servers/1/socket']) ? '' : $values['Servers/1/socket'],
+ empty($values['Servers/1/controluser']) ? '' : $values['Servers/1/controluser'],
+ empty($values['Servers/1/controlpass']) ? '' : $values['Servers/1/controlpass'],
+ 'Server_pmadb'
+ );
+ if ($test !== true) {
+ $result = array_merge($result, $test);
+ }
+ }
+ return $result;
+ }
+
+
+ /**
+ * Validates regular expression
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateRegex($path, array $values)
+ {
+ $result = [$path => ''];
+
+ if (empty($values[$path])) {
+ return $result;
+ }
+
+ error_clear_last();
+
+ $matches = [];
+ // in libraries/ListDatabase.php _checkHideDatabase(),
+ // a '/' is used as the delimiter for hide_db
+ @preg_match('/' . Util::requestString($values[$path]) . '/', '', $matches);
+
+ $currentError = error_get_last();
+
+ if ($currentError !== null) {
+ $error = preg_replace('/^preg_match\(\): /', '', $currentError['message']);
+ return [$path => $error];
+ }
+
+ return $result;
+ }
+
+ /**
+ * Validates TrustedProxies field
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateTrustedProxies($path, array $values)
+ {
+ $result = [$path => []];
+
+ if (empty($values[$path])) {
+ return $result;
+ }
+
+ if (is_array($values[$path]) || is_object($values[$path])) {
+ // value already processed by FormDisplay::save
+ $lines = [];
+ foreach ($values[$path] as $ip => $v) {
+ $v = Util::requestString($v);
+ $lines[] = preg_match('/^-\d+$/', $ip)
+ ? $v
+ : $ip . ': ' . $v;
+ }
+ } else {
+ // AJAX validation
+ $lines = explode("\n", $values[$path]);
+ }
+ foreach ($lines as $line) {
+ $line = trim($line);
+ $matches = [];
+ // we catch anything that may (or may not) be an IP
+ if (! preg_match("/^(.+):(?:[ ]?)\\w+$/", $line, $matches)) {
+ $result[$path][] = __('Incorrect value:') . ' '
+ . htmlspecialchars($line);
+ continue;
+ }
+ // now let's check whether we really have an IP address
+ if (filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false
+ && filter_var($matches[1], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false
+ ) {
+ $ip = htmlspecialchars(trim($matches[1]));
+ $result[$path][] = sprintf(__('Incorrect IP address: %s'), $ip);
+ continue;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Tests integer value
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ * @param bool $allowNegative allow negative values
+ * @param bool $allowZero allow zero
+ * @param int $maxValue max allowed value
+ * @param string $errorString error message string
+ *
+ * @return string empty string if test is successful
+ */
+ public static function validateNumber(
+ $path,
+ array $values,
+ $allowNegative,
+ $allowZero,
+ $maxValue,
+ $errorString
+ ) {
+ if (empty($values[$path])) {
+ return '';
+ }
+
+ $value = Util::requestString($values[$path]);
+
+ if (intval($value) != $value
+ || (! $allowNegative && $value < 0)
+ || (! $allowZero && $value == 0)
+ || $value > $maxValue
+ ) {
+ return $errorString;
+ }
+
+ return '';
+ }
+
+ /**
+ * Validates port number
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validatePortNumber($path, array $values)
+ {
+ return [
+ $path => static::validateNumber(
+ $path,
+ $values,
+ false,
+ false,
+ 65535,
+ __('Not a valid port number!')
+ ),
+ ];
+ }
+
+ /**
+ * Validates positive number
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validatePositiveNumber($path, array $values)
+ {
+ return [
+ $path => static::validateNumber(
+ $path,
+ $values,
+ false,
+ false,
+ PHP_INT_MAX,
+ __('Not a positive number!')
+ ),
+ ];
+ }
+
+ /**
+ * Validates non-negative number
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ *
+ * @return array
+ */
+ public static function validateNonNegativeNumber($path, array $values)
+ {
+ return [
+ $path => static::validateNumber(
+ $path,
+ $values,
+ false,
+ true,
+ PHP_INT_MAX,
+ __('Not a non-negative number!')
+ ),
+ ];
+ }
+
+ /**
+ * Validates value according to given regular expression
+ * Pattern and modifiers must be a valid for PCRE <b>and</b> JavaScript RegExp
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ * @param string $regex regular expression to match
+ *
+ * @return array|string
+ */
+ public static function validateByRegex($path, array $values, $regex)
+ {
+ if (! isset($values[$path])) {
+ return '';
+ }
+ $result = preg_match($regex, Util::requestString($values[$path]));
+ return [$path => $result ? '' : __('Incorrect value!')];
+ }
+
+ /**
+ * Validates upper bound for numeric inputs
+ *
+ * @param string $path path to config
+ * @param array $values config values
+ * @param int $maxValue maximal allowed value
+ *
+ * @return array
+ */
+ public static function validateUpperBound($path, array $values, $maxValue)
+ {
+ $result = $values[$path] <= $maxValue;
+ return [
+ $path => $result ? '' : sprintf(
+ __('Value must be less than or equal to %s!'),
+ $maxValue
+ ),
+ ];
+ }
+}