Issues (1880)

src/Config/ConfigFile.php (3 issues)

1
<?php
2
/**
3
 * Config file management
4
 */
5
6
declare(strict_types=1);
7
8
namespace PhpMyAdmin\Config;
9
10
use PhpMyAdmin\Config;
11
use PhpMyAdmin\Core;
12
use PhpMyAdmin\Current;
13
14
use function __;
15
use function _pgettext;
16
use function array_diff;
17
use function array_flip;
18
use function array_keys;
19
use function array_merge;
20
use function count;
21
use function is_array;
22
use function preg_replace;
23
24
/**
25
 * Config file management class.
26
 * Stores its data in $_SESSION
27
 */
28
class ConfigFile
29
{
30
    /**
31
     * Stores default phpMyAdmin config
32
     *
33
     * @see Settings
34
     *
35
     * @var mixed[]
36
     */
37
    private array $defaultCfg;
38
39
    /**
40
     * Stores allowed values for non-standard fields
41
     *
42
     * @var array<string, string|mixed[]>
43
     */
44
    private array $cfgDb;
45
46
    /**
47
     * Whether we are currently working in PMA Setup context
48
     */
49
    private bool $isInSetup;
50
51
    /**
52
     * Keys which will be always written to config file
53
     *
54
     * @var mixed[]
55
     */
56
    private array $persistKeys = [];
57
58
    /**
59
     * Changes keys while updating config in {@link updateWithGlobalConfig()}
60
     * or reading by {@link getConfig()} or {@link getConfigArray()}
61
     *
62
     * @var mixed[]
63
     */
64
    private array $cfgUpdateReadMapping = [];
65
66
    /**
67
     * Key filter for {@link set()}
68
     */
69
    private array|null $setFilter = null;
70
71
    /**
72
     * Instance id (key in $_SESSION array, separate for each server -
73
     * ConfigFile{server id})
74
     */
75
    private string $id;
76
77
    /**
78
     * @param mixed[]|null $baseConfig base configuration read from
79
                             {@link PhpMyAdmin\Config::$base_config},
80
                             use only when not in PMA Setup
81
                             Stores original PMA config, not modified by user preferences
82
     */
83 72
    public function __construct(private array|null $baseConfig = null)
84
    {
85
        // load default config values
86 72
        $settings = new Settings([]);
87 72
        $this->defaultCfg = $settings->asArray();
88
89
        // load additional config information
90 72
        $this->cfgDb = $this->getAllowedValues();
91 72
        $this->isInSetup = $baseConfig === null;
92 72
        $this->id = 'ConfigFile' . Current::$server;
93 72
        if (isset($_SESSION[$this->id])) {
94 4
            return;
95
        }
96
97 72
        $_SESSION[$this->id] = [];
98
    }
99
100
    /**
101
     * Sets names of config options which will be placed in config file even if
102
     * they are set to their default values (use only full paths)
103
     *
104
     * @param mixed[] $keys the names of the config options
105
     */
106 8
    public function setPersistKeys(array $keys): void
107
    {
108
        // checking key presence is much faster than searching so move values
109
        // to keys
110 8
        $this->persistKeys = array_flip($keys);
111
    }
112
113
    /**
114
     * Returns flipped array set by {@link setPersistKeys()}
115
     *
116
     * @return mixed[]
117
     */
118
    public function getPersistKeysMap(): array
119
    {
120
        return $this->persistKeys;
121
    }
122
123
    /**
124
     * By default ConfigFile allows setting of all configuration keys, use
125
     * this method to set up a filter on {@link set()} method
126
     *
127
     * @param mixed[]|null $keys array of allowed keys or null to remove filter
128
     */
129 4
    public function setAllowedKeys(array|null $keys): void
130
    {
131 4
        if ($keys === null) {
0 ignored issues
show
The condition $keys === null is always false.
Loading history...
132 4
            $this->setFilter = null;
133
134 4
            return;
135
        }
136
137
        // checking key presence is much faster than searching so move values
138
        // to keys
139 4
        $this->setFilter = array_flip($keys);
140
    }
141
142
    /**
143
     * Sets path mapping for updating config in
144
     * {@link updateWithGlobalConfig()} or reading
145
     * by {@link getConfig()} or {@link getConfigArray()}
146
     *
147
     * @param mixed[] $mapping Contains the mapping of "Server/config options"
148
     *                       to "Server/1/config options"
149
     */
150 4
    public function setCfgUpdateReadMapping(array $mapping): void
151
    {
152 4
        $this->cfgUpdateReadMapping = $mapping;
153
    }
154
155
    /**
156
     * Resets configuration data
157
     */
158 8
    public function resetConfigData(): void
159
    {
160 8
        $_SESSION[$this->id] = [];
161
    }
162
163
    /**
164
     * Sets configuration data (overrides old data)
165
     *
166
     * @param mixed[] $cfg Configuration options
167
     */
168 72
    public function setConfigData(array $cfg): void
169
    {
170 72
        $_SESSION[$this->id] = $cfg;
171
    }
172
173
    /**
174
     * Sets config value
175
     */
176 56
    public function set(string $path, mixed $value, string|null $canonicalPath = null): void
177
    {
178 56
        if ($canonicalPath === null) {
179 52
            $canonicalPath = $this->getCanonicalPath($path);
180
        }
181
182 56
        if ($this->setFilter !== null && ! isset($this->setFilter[$canonicalPath])) {
183 4
            return;
184
        }
185
186
        // if the path isn't protected it may be removed
187 56
        if (isset($this->persistKeys[$canonicalPath])) {
188 4
            Core::arrayWrite($path, $_SESSION[$this->id], $value);
189
190 4
            return;
191
        }
192
193 56
        $defaultValue = $this->getDefault($canonicalPath);
194 56
        if ($this->isInSetup) {
195
            // remove if it has a default value or is empty
196 52
            $removePath = $value === $defaultValue || empty($value) && empty($defaultValue);
197
        } else {
198
            // get original config values not overwritten by user
199
            // preferences to allow for overwriting options set in
200
            // config.inc.php with default values
201 4
            $instanceDefaultValue = Core::arrayRead($canonicalPath, $this->baseConfig);
0 ignored issues
show
It seems like $this->baseConfig can also be of type null; however, parameter $array of PhpMyAdmin\Core::arrayRead() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

201
            $instanceDefaultValue = Core::arrayRead($canonicalPath, /** @scrutinizer ignore-type */ $this->baseConfig);
Loading history...
202
            // remove if it has a default value and base config (config.inc.php)
203
            // uses default value
204 4
            $removePath = $value === $defaultValue && $instanceDefaultValue === $defaultValue;
205
        }
206
207 56
        if ($removePath) {
208 20
            Core::arrayRemove($path, $_SESSION[$this->id]);
209
210 20
            return;
211
        }
212
213 48
        Core::arrayWrite($path, $_SESSION[$this->id], $value);
214
    }
215
216
    /**
217
     * Flattens multidimensional array, changes indices to paths
218
     * (eg. 'key/subkey').
219
     *
220
     * @param mixed[] $array  Multidimensional array
221
     * @param string  $prefix Prefix
222
     *
223
     * @return mixed[]
224
     */
225 36
    private function getFlatArray(array $array, string $prefix = ''): array
226
    {
227 36
        $result = [];
228 36
        foreach ($array as $key => $value) {
229 28
            if (is_array($value) && ! isset($value[0])) {
230 16
                $result += $this->getFlatArray($value, $prefix . $key . '/');
231
            } else {
232 28
                $result[$prefix . $key] = $value;
233
            }
234
        }
235
236 36
        return $result;
237
    }
238
239
    /**
240
     * Returns default config in a flattened array
241
     *
242
     * @return mixed[]
243
     */
244 4
    public function getFlatDefaultConfig(): array
245
    {
246 4
        return $this->getFlatArray($this->defaultCfg);
247
    }
248
249
    /**
250
     * Updates config with values read from given array
251
     * (config will contain differences to defaults from {@see \PhpMyAdmin\Config\Settings}).
252
     *
253
     * @param mixed[] $cfg Configuration
254
     */
255 12
    public function updateWithGlobalConfig(array $cfg): void
256
    {
257
        // load config array and flatten it
258 12
        $flatConfig = $this->getFlatArray($cfg);
259
260
        // save values map for translating a few user preferences paths,
261
        // should be complemented by code reading from generated config
262
        // to perform inverse mapping
263 12
        foreach ($flatConfig as $path => $value) {
264 12
            if (isset($this->cfgUpdateReadMapping[$path])) {
265 4
                $path = $this->cfgUpdateReadMapping[$path];
266
            }
267
268 12
            $this->set($path, $value, $path);
269
        }
270
    }
271
272
    /**
273
     * Returns config value or $default if it's not set
274
     *
275
     * @param string $path    Path of config file
276
     * @param mixed  $default Default values
277
     */
278 16
    public function get(string $path, mixed $default = null): mixed
279
    {
280 16
        return Core::arrayRead($path, $_SESSION[$this->id], $default);
281
    }
282
283
    /**
284
     * Returns default config value or $default it it's not set ie. it doesn't
285
     * exist in {@see \PhpMyAdmin\Config\Settings} ($cfg).
286
     *
287
     * @param string $canonicalPath Canonical path
288
     * @param mixed  $default       Default value
289
     */
290 64
    public function getDefault(string $canonicalPath, mixed $default = null): mixed
291
    {
292 64
        return Core::arrayRead($canonicalPath, $this->defaultCfg, $default);
293
    }
294
295
    /**
296
     * Returns config value, if it's not set uses the default one; returns
297
     * $default if the path isn't set and doesn't contain a default value
298
     *
299
     * @param string $path    Path
300
     * @param mixed  $default Default value
301
     */
302 4
    public function getValue(string $path, mixed $default = null): mixed
303
    {
304 4
        $v = Core::arrayRead($path, $_SESSION[$this->id]);
305 4
        if ($v !== null) {
306 4
            return $v;
307
        }
308
309 4
        $path = $this->getCanonicalPath($path);
310
311 4
        return $this->getDefault($path, $default);
312
    }
313
314
    /**
315
     * Returns canonical path
316
     *
317
     * @param string $path Path
318
     */
319 60
    public function getCanonicalPath(string $path): string
320
    {
321 60
        return preg_replace('#^Servers/([\d]+)/#', 'Servers/1/', $path);
322
    }
323
324
    /**
325
     * Returns config database entry for $path
326
     *
327
     * @param string $path    path of the variable in config db
328
     * @param mixed  $default default value
329
     */
330 4
    public function getDbEntry(string $path, mixed $default = null): mixed
331
    {
332 4
        return Core::arrayRead($path, $this->cfgDb, $default);
333
    }
334
335
    /**
336
     * Returns server count
337
     */
338 4
    public function getServerCount(): int
339
    {
340 4
        return isset($_SESSION[$this->id]['Servers'])
341 4
            ? count($_SESSION[$this->id]['Servers'])
342 4
            : 0;
343
    }
344
345
    /**
346
     * Returns server list
347
     *
348
     * @return mixed[]
349
     */
350 4
    public function getServers(): array
351
    {
352 4
        return $_SESSION[$this->id]['Servers'] ?? [];
353
    }
354
355
    /**
356
     * Returns DSN of given server
357
     *
358
     * @param int $server server index
359
     */
360 4
    public function getServerDSN(int $server): string
361
    {
362 4
        if (! isset($_SESSION[$this->id]['Servers'][$server])) {
363 4
            return '';
364
        }
365
366 4
        $path = 'Servers/' . $server;
367 4
        $dsn = 'mysqli://';
368 4
        if ($this->getValue($path . '/auth_type') === 'config') {
369 4
            $dsn .= $this->getValue($path . '/user');
370 4
            if (! empty($this->getValue($path . '/password'))) {
371 4
                $dsn .= ':***';
372
            }
373
374 4
            $dsn .= '@';
375
        }
376
377 4
        if ($this->getValue($path . '/host') !== 'localhost') {
378 4
            $dsn .= $this->getValue($path . '/host');
379 4
            $port = $this->getValue($path . '/port');
380 4
            if ($port) {
381 4
                $dsn .= ':' . $port;
382
            }
383
        } else {
384 4
            $dsn .= $this->getValue($path . '/socket');
385
        }
386
387 4
        return $dsn;
388
    }
389
390
    /**
391
     * Returns server name
392
     *
393
     * @param int $id server index
394
     */
395 4
    public function getServerName(int $id): string
396
    {
397 4
        if (! isset($_SESSION[$this->id]['Servers'][$id])) {
398 4
            return '';
399
        }
400
401 4
        $verbose = $this->get('Servers/' . $id . '/verbose');
402 4
        if (! empty($verbose)) {
403 4
            return $verbose;
404
        }
405
406 4
        $host = $this->get('Servers/' . $id . '/host');
407
408 4
        return empty($host) ? 'localhost' : $host;
409
    }
410
411
    /**
412
     * Removes server
413
     *
414
     * @param int $server server index
415
     */
416 4
    public function removeServer(int $server): void
417
    {
418 4
        if (! isset($_SESSION[$this->id]['Servers'][$server])) {
419
            return;
420
        }
421
422 4
        $lastServer = $this->getServerCount();
423
424
        /** @infection-ignore-all */
425 4
        for ($i = $server; $i < $lastServer; $i++) {
426 4
            $_SESSION[$this->id]['Servers'][$i] = $_SESSION[$this->id]['Servers'][$i + 1];
427
        }
428
429 4
        unset($_SESSION[$this->id]['Servers'][$lastServer]);
430
431 4
        if (! isset($_SESSION[$this->id]['ServerDefault']) || $_SESSION[$this->id]['ServerDefault'] != $lastServer) {
432 4
            return;
433
        }
434
435 4
        unset($_SESSION[$this->id]['ServerDefault']);
436
    }
437
438
    /**
439
     * Returns configuration array (full, multidimensional format)
440
     *
441
     * @return mixed[]
442
     */
443 40
    public function getConfig(): array
444
    {
445 40
        $c = $_SESSION[$this->id];
446 40
        foreach ($this->cfgUpdateReadMapping as $mapTo => $mapFrom) {
447
            // if the key $c exists in $map_to
448 4
            if (Core::arrayRead($mapTo, $c) === null) {
449 4
                continue;
450
            }
451
452
            Core::arrayWrite($mapTo, $c, Core::arrayRead($mapFrom, $c));
453
            Core::arrayRemove($mapFrom, $c);
454
        }
455
456 40
        return $c;
457
    }
458
459
    /**
460
     * Returns configuration array (flat format)
461
     *
462
     * @return mixed[]
463
     */
464 20
    public function getConfigArray(): array
465
    {
466 20
        $c = $this->getFlatArray($_SESSION[$this->id]);
467
468 20
        $persistKeys = array_diff(
469 20
            array_keys($this->persistKeys),
470 20
            array_keys($c),
471 20
        );
472 20
        foreach ($persistKeys as $k) {
473 8
            $c[$k] = $this->getDefault($this->getCanonicalPath($k));
474
        }
475
476 20
        foreach ($this->cfgUpdateReadMapping as $mapTo => $mapFrom) {
477
            if (! isset($c[$mapFrom])) {
478
                continue;
479
            }
480
481
            $c[$mapTo] = $c[$mapFrom];
482
            unset($c[$mapFrom]);
483
        }
484
485 20
        return $c;
486
    }
487
488
    /**
489
     * Database with allowed values for configuration stored in the $cfg array,
490
     * used by setup script and user preferences to generate forms.
491
     *
492
     * Value meaning:
493
     *   array - select field, array contains allowed values
494
     *   string - type override
495
     *
496
     * @return array<string, string|mixed[]>
497
     */
498 72
    public function getAllowedValues(): array
499
    {
500 72
        $config = Config::getInstance();
0 ignored issues
show
Deprecated Code introduced by
The function PhpMyAdmin\Config::getInstance() has been deprecated: Use dependency injection instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

500
        $config = /** @scrutinizer ignore-deprecated */ Config::getInstance();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
501
502 72
        return [
503 72
            'Servers' => [
504 72
                1 => [
505 72
                    'port' => 'integer',
506 72
                    'auth_type' => ['config', 'http', 'signon', 'cookie'],
507 72
                    'AllowDeny' => ['order' => ['', 'deny,allow', 'allow,deny', 'explicit']],
508 72
                    'only_db' => 'array',
509 72
                ],
510 72
            ],
511 72
            'RecodingEngine' => ['auto', 'iconv', 'mb', 'none'],
512 72
            'OBGzip' => ['auto', true, false],
513 72
            'MemoryLimit' => 'short_string',
514 72
            'NavigationLogoLinkWindow' => ['main', 'new'],
515 72
            'NavigationTreeDefaultTabTable' => [
516
                // fields list
517 72
                'structure' => __('Structure'),
518
                // SQL form
519 72
                'sql' => __('SQL'),
520
                // search page
521 72
                'search' => __('Search'),
522
                // insert row page
523 72
                'insert' => __('Insert'),
524
                // browse page
525 72
                'browse' => __('Browse'),
526 72
            ],
527 72
            'NavigationTreeDefaultTabTable2' => [
528
                //don't display
529 72
                '' => '',
530
                // fields list
531 72
                'structure' => __('Structure'),
532
                // SQL form
533 72
                'sql' => __('SQL'),
534
                // search page
535 72
                'search' => __('Search'),
536
                // insert row page
537 72
                'insert' => __('Insert'),
538
                // browse page
539 72
                'browse' => __('Browse'),
540 72
            ],
541 72
            'NavigationTreeDbSeparator' => 'short_string',
542 72
            'NavigationTreeTableSeparator' => 'short_string',
543 72
            'NavigationWidth' => 'integer',
544 72
            'TableNavigationLinksMode' => ['icons' => __('Icons'), 'text' => __('Text'), 'both' => __('Both')],
545 72
            'MaxRows' => [25, 50, 100, 250, 500],
546 72
            'Order' => ['ASC', 'DESC', 'SMART'],
547 72
            'RowActionLinks' => [
548 72
                'none' => __('Nowhere'),
549 72
                'left' => __('Left'),
550 72
                'right' => __('Right'),
551 72
                'both' => __('Both'),
552 72
            ],
553 72
            'TablePrimaryKeyOrder' => ['NONE' => __('None'), 'ASC' => __('Ascending'), 'DESC' => __('Descending')],
554 72
            'ProtectBinary' => [false, 'blob', 'noblob', 'all'],
555 72
            'CharEditing' => ['input', 'textarea'],
556 72
            'TabsMode' => ['icons' => __('Icons'), 'text' => __('Text'), 'both' => __('Both')],
557 72
            'PDFDefaultPageSize' => [
558 72
                'A3' => 'A3',
559 72
                'A4' => 'A4',
560 72
                'A5' => 'A5',
561 72
                'letter' => 'letter',
562 72
                'legal' => 'legal',
563 72
            ],
564 72
            'ActionLinksMode' => ['icons' => __('Icons'), 'text' => __('Text'), 'both' => __('Both')],
565 72
            'GridEditing' => [
566 72
                'click' => __('Click'),
567 72
                'double-click' => __('Double click'),
568 72
                'disabled' => __('Disabled'),
569 72
            ],
570 72
            'RelationalDisplay' => ['K' => __('key'), 'D' => __('display column')],
571 72
            'DefaultTabServer' => [
572
                // the welcome page (recommended for multiuser setups)
573 72
                'welcome' => __('Welcome'),
574
                // list of databases
575 72
                'databases' => __('Databases'),
576
                // runtime information
577 72
                'status' => __('Status'),
578
                // MySQL server variables
579 72
                'variables' => __('Variables'),
580
                // user management
581 72
                'privileges' => __('Privileges'),
582 72
            ],
583 72
            'DefaultTabDatabase' => [
584
                // tables list
585 72
                'structure' => __('Structure'),
586
                // SQL form
587 72
                'sql' => __('SQL'),
588
                // search query
589 72
                'search' => __('Search'),
590
                // operations on database
591 72
                'operations' => __('Operations'),
592 72
            ],
593 72
            'DefaultTabTable' => [
594
                // fields list
595 72
                'structure' => __('Structure'),
596
                // SQL form
597 72
                'sql' => __('SQL'),
598
                // search page
599 72
                'search' => __('Search'),
600
                // insert row page
601 72
                'insert' => __('Insert'),
602
                // browse page
603 72
                'browse' => __('Browse'),
604 72
            ],
605 72
            'InitialSlidersState' => ['open' => __('Open'), 'closed' => __('Closed'), 'disabled' => __('Disabled')],
606 72
            'FirstDayOfCalendar' => [
607 72
                1 => _pgettext('Week day name', 'Monday'),
608 72
                2 => _pgettext('Week day name', 'Tuesday'),
609 72
                3 => _pgettext('Week day name', 'Wednesday'),
610 72
                4 => _pgettext('Week day name', 'Thursday'),
611 72
                5 => _pgettext('Week day name', 'Friday'),
612 72
                6 => _pgettext('Week day name', 'Saturday'),
613 72
                7 => _pgettext('Week day name', 'Sunday'),
614 72
            ],
615 72
            'SendErrorReports' => [
616 72
                'ask' => __('Ask before sending error reports'),
617 72
                'always' => __('Always send error reports'),
618 72
                'never' => __('Never send error reports'),
619 72
            ],
620 72
            'DefaultForeignKeyChecks' => [
621 72
                'default' => __('Server default'),
622 72
                'enable' => __('Enable'),
623 72
                'disable' => __('Disable'),
624 72
            ],
625
626 72
            'Import' => [
627 72
                'format' => [
628
                    // CSV
629 72
                    'csv',
630
                    // DocSQL
631 72
                    'docsql',
632
                    // CSV using LOAD DATA
633 72
                    'ldi',
634
                    // SQL
635 72
                    'sql',
636 72
                ],
637 72
                'charset' => array_merge([''], $config->settings['AvailableCharsets'] ?? []),
638 72
                'sql_compatibility' => [
639 72
                    'NONE',
640 72
                    'ANSI',
641 72
                    'DB2',
642 72
                    'MAXDB',
643 72
                    'MYSQL323',
644 72
                    'MYSQL40',
645 72
                    'MSSQL',
646 72
                    'ORACLE',
647
                    // removed; in MySQL 5.0.33, this produces exports that
648
                    // can't be read by POSTGRESQL (see our bug #1596328)
649
                    //'POSTGRESQL',
650 72
                    'TRADITIONAL',
651 72
                ],
652 72
                'csv_terminated' => 'short_string',
653 72
                'csv_enclosed' => 'short_string',
654 72
                'csv_escaped' => 'short_string',
655 72
                'ldi_terminated' => 'short_string',
656 72
                'ldi_enclosed' => 'short_string',
657 72
                'ldi_escaped' => 'short_string',
658 72
                'ldi_local_option' => ['auto', true, false],
659 72
            ],
660
661 72
            'Export' => [
662 72
                '_sod_select' => [
663 72
                    'structure' => __('structure'),
664 72
                    'data' => __('data'),
665 72
                    'structure_and_data' => __('structure and data'),
666 72
                ],
667 72
                'method' => [
668 72
                    'quick' => __('Quick - display only the minimal options to configure'),
669 72
                    'custom' => __('Custom - display all possible options to configure'),
670 72
                    'custom-no-form' => __('Custom - like above, but without the quick/custom choice'),
671 72
                ],
672 72
                'format' => [
673 72
                    'codegen',
674 72
                    'csv',
675 72
                    'excel',
676 72
                    'htmlexcel',
677 72
                    'htmlword',
678 72
                    'latex',
679 72
                    'ods',
680 72
                    'odt',
681 72
                    'pdf',
682 72
                    'sql',
683 72
                    'texytext',
684 72
                    'xml',
685 72
                    'yaml',
686 72
                ],
687 72
                'compression' => ['none', 'zip', 'gzip'],
688 72
                'charset' => array_merge([''], $config->settings['AvailableCharsets'] ?? []),
689 72
                'sql_compatibility' => [
690 72
                    'NONE',
691 72
                    'ANSI',
692 72
                    'DB2',
693 72
                    'MAXDB',
694 72
                    'MYSQL323',
695 72
                    'MYSQL40',
696 72
                    'MSSQL',
697 72
                    'ORACLE',
698
                    // removed; in MySQL 5.0.33, this produces exports that
699
                    // can't be read by POSTGRESQL (see our bug #1596328)
700
                    //'POSTGRESQL',
701 72
                    'TRADITIONAL',
702 72
                ],
703 72
                'codegen_format' => ['#', 'NHibernate C# DO', 'NHibernate XML'],
704 72
                'csv_separator' => 'short_string',
705 72
                'csv_terminated' => 'short_string',
706 72
                'csv_enclosed' => 'short_string',
707 72
                'csv_escaped' => 'short_string',
708 72
                'csv_null' => 'short_string',
709 72
                'excel_null' => 'short_string',
710 72
                'excel_edition' => [
711 72
                    'win' => 'Windows',
712 72
                    'mac_excel2003' => 'Excel 2003 / Macintosh',
713 72
                    'mac_excel2008' => 'Excel 2008 / Macintosh',
714 72
                ],
715 72
                'sql_structure_or_data' => [
716 72
                    'structure' => __('structure'),
717 72
                    'data' => __('data'),
718 72
                    'structure_and_data' => __('structure and data'),
719 72
                ],
720 72
                'sql_type' => ['INSERT', 'UPDATE', 'REPLACE'],
721 72
                'sql_insert_syntax' => [
722 72
                    'complete' => __('complete inserts'),
723 72
                    'extended' => __('extended inserts'),
724 72
                    'both' => __('both of the above'),
725 72
                    'none' => __('neither of the above'),
726 72
                ],
727 72
                'htmlword_structure_or_data' => [
728 72
                    'structure' => __('structure'),
729 72
                    'data' => __('data'),
730 72
                    'structure_and_data' => __('structure and data'),
731 72
                ],
732 72
                'htmlword_null' => 'short_string',
733 72
                'ods_null' => 'short_string',
734 72
                'odt_null' => 'short_string',
735 72
                'odt_structure_or_data' => [
736 72
                    'structure' => __('structure'),
737 72
                    'data' => __('data'),
738 72
                    'structure_and_data' => __('structure and data'),
739 72
                ],
740 72
                'texytext_structure_or_data' => [
741 72
                    'structure' => __('structure'),
742 72
                    'data' => __('data'),
743 72
                    'structure_and_data' => __('structure and data'),
744 72
                ],
745 72
                'texytext_null' => 'short_string',
746 72
            ],
747
748 72
            'Console' => [
749 72
                'Mode' => ['info', 'show', 'collapse'],
750 72
                'OrderBy' => ['exec', 'time', 'count'],
751 72
                'Order' => ['asc', 'desc'],
752 72
            ],
753
754
            /**
755
             * Basic validator assignments (functions from libraries/config/Validator.php
756
             * and 'window.validators' object in js/config.js)
757
             * Use only full paths and form ids
758
             */
759 72
            '_validators' => [
760 72
                'Console/Height' => 'validateNonNegativeNumber',
761 72
                'CharTextareaCols' => 'validatePositiveNumber',
762 72
                'CharTextareaRows' => 'validatePositiveNumber',
763 72
                'ExecTimeLimit' => 'validateNonNegativeNumber',
764 72
                'Export/sql_max_query_size' => 'validatePositiveNumber',
765 72
                'FirstLevelNavigationItems' => 'validatePositiveNumber',
766 72
                'ForeignKeyMaxLimit' => 'validatePositiveNumber',
767 72
                'Import/csv_enclosed' => [['validateByRegex', '/^.?$/']],
768 72
                'Import/csv_escaped' => [['validateByRegex', '/^.$/']],
769 72
                'Import/csv_terminated' => [['validateByRegex', '/^.$/']],
770 72
                'Import/ldi_enclosed' => [['validateByRegex', '/^.?$/']],
771 72
                'Import/ldi_escaped' => [['validateByRegex', '/^.$/']],
772 72
                'Import/ldi_terminated' => [['validateByRegex', '/^.$/']],
773 72
                'Import/skip_queries' => 'validateNonNegativeNumber',
774 72
                'InsertRows' => 'validatePositiveNumber',
775 72
                'NumRecentTables' => 'validateNonNegativeNumber',
776 72
                'NumFavoriteTables' => 'validateNonNegativeNumber',
777 72
                'LimitChars' => 'validatePositiveNumber',
778 72
                'LoginCookieValidity' => 'validatePositiveNumber',
779 72
                'LoginCookieStore' => 'validateNonNegativeNumber',
780 72
                'MaxDbList' => 'validatePositiveNumber',
781 72
                'MaxNavigationItems' => 'validatePositiveNumber',
782 72
                'MaxCharactersInDisplayedSQL' => 'validatePositiveNumber',
783 72
                'MaxRows' => 'validatePositiveNumber',
784 72
                'MaxSizeForInputField' => 'validatePositiveNumber',
785 72
                'MinSizeForInputField' => 'validateNonNegativeNumber',
786 72
                'MaxTableList' => 'validatePositiveNumber',
787 72
                'MemoryLimit' => [['validateByRegex', '/^(-1|(\d+(?:[kmg])?))$/i']],
788 72
                'NavigationTreeDisplayItemFilterMinimum' => 'validatePositiveNumber',
789 72
                'NavigationTreeTableLevel' => 'validatePositiveNumber',
790 72
                'NavigationWidth' => 'validateNonNegativeNumber',
791 72
                'QueryHistoryMax' => 'validatePositiveNumber',
792 72
                'RepeatCells' => 'validateNonNegativeNumber',
793 72
                'Server' => 'validateServer',
794 72
                'Server_pmadb' => 'validatePMAStorage',
795 72
                'Servers/1/port' => 'validatePortNumber',
796 72
                'Servers/1/hide_db' => 'validateRegex',
797 72
                'TextareaCols' => 'validatePositiveNumber',
798 72
                'TextareaRows' => 'validatePositiveNumber',
799 72
                'TrustedProxies' => 'validateTrustedProxies',
800 72
            ],
801
802
            /**
803
             * Additional validators used for user preferences
804
             */
805 72
            '_userValidators' => [
806 72
                'MaxDbList' => [['validateUpperBound', 'value:MaxDbList']],
807 72
                'MaxTableList' => [['validateUpperBound', 'value:MaxTableList']],
808 72
                'QueryHistoryMax' => [['validateUpperBound', 'value:QueryHistoryMax']],
809 72
            ],
810 72
        ];
811
    }
812
}
813