Completed
Push — master ( 64741e...904a1b )
by Maurício
09:11
created

libraries/classes/InsertEdit.php (2 issues)

1
<?php
2
/* vim: set expandtab sw=4 ts=4 sts=4: */
3
/**
4
 * set of functions with the insert/edit features in pma
5
 *
6
 * @package PhpMyAdmin
7
 */
8
declare(strict_types=1);
9
10
namespace PhpMyAdmin;
11
12
use PhpMyAdmin\DatabaseInterface;
13
use PhpMyAdmin\FileListing;
14
use PhpMyAdmin\Message;
15
use PhpMyAdmin\Plugins\TransformationsPlugin;
16
use PhpMyAdmin\Relation;
17
use PhpMyAdmin\Response;
18
use PhpMyAdmin\Sanitize;
19
use PhpMyAdmin\Template;
20
use PhpMyAdmin\Transformations;
21
use PhpMyAdmin\Url;
22
use PhpMyAdmin\Util;
23
24
/**
25
 * PhpMyAdmin\InsertEdit class
26
 *
27
 * @package PhpMyAdmin
28
 */
29
class InsertEdit
30
{
31
    /**
32
     * DatabaseInterface instance
33
     *
34
     * @var DatabaseInterface
35
     */
36
    private $dbi;
37
38
    /**
39
     * @var Relation
40
     */
41
    private $relation;
42
43
    /**
44
     * @var Transformations
45
     */
46
    private $transformations;
47
48
    /**
49
     * @var FileListing
50
     */
51
    private $fileListing;
52
53
    /**
54
     * @var Template
55
     */
56
    public $template;
57
58
    /**
59
     * Constructor
60
     *
61
     * @param DatabaseInterface $dbi DatabaseInterface instance
62
     */
63
    public function __construct(DatabaseInterface $dbi)
64
    {
65
        $this->dbi = $dbi;
66
        $this->relation = new Relation($GLOBALS['dbi']);
67
        $this->transformations = new Transformations();
68
        $this->fileListing = new FileListing();
69
        $this->template = new Template();
70
    }
71
72
    /**
73
     * Retrieve form parameters for insert/edit form
74
     *
75
     * @param string     $db                 name of the database
76
     * @param string     $table              name of the table
77
     * @param array|null $where_clauses      where clauses
78
     * @param array      $where_clause_array array of where clauses
79
     * @param string     $err_url            error url
80
     *
81
     * @return array array of insert/edit form parameters
82
     */
83
    public function getFormParametersForInsertForm(
84
        $db,
85
        $table,
86
        ?array $where_clauses,
87
        array $where_clause_array,
88
        $err_url
89
    ) {
90
        $_form_params = [
91
            'db'        => $db,
92
            'table'     => $table,
93
            'goto'      => $GLOBALS['goto'],
94
            'err_url'   => $err_url,
95
            'sql_query' => $_POST['sql_query'],
96
        ];
97
        if (isset($where_clauses)) {
98
            foreach ($where_clause_array as $key_id => $where_clause) {
99
                $_form_params['where_clause[' . $key_id . ']'] = trim($where_clause);
100
            }
101
        }
102
        if (isset($_REQUEST['clause_is_unique'])) {
103
            $_form_params['clause_is_unique'] = $_REQUEST['clause_is_unique'];
104
        }
105
        return $_form_params;
106
    }
107
108
    /**
109
     * Creates array of where clauses
110
     *
111
     * @param array|string|null $where_clause where clause
112
     *
113
     * @return array whereClauseArray array of where clauses
114
     */
115
    private function getWhereClauseArray($where_clause)
116
    {
117
        if (!isset($where_clause)) {
118
            return [];
119
        }
120
121
        if (is_array($where_clause)) {
122
            return $where_clause;
123
        }
124
125
        return [0 => $where_clause];
126
    }
127
128
    /**
129
     * Analysing where clauses array
130
     *
131
     * @param array  $where_clause_array array of where clauses
132
     * @param string $table              name of the table
133
     * @param string $db                 name of the database
134
     *
135
     * @return array $where_clauses, $result, $rows, $found_unique_key
136
     */
137
    private function analyzeWhereClauses(
138
        array $where_clause_array,
139
        $table,
140
        $db
141
    ) {
142
        $rows               = [];
143
        $result             = [];
144
        $where_clauses      = [];
145
        $found_unique_key   = false;
146
        foreach ($where_clause_array as $key_id => $where_clause) {
147
            $local_query     = 'SELECT * FROM '
148
                . Util::backquote($db) . '.'
149
                . Util::backquote($table)
150
                . ' WHERE ' . $where_clause . ';';
151
            $result[$key_id] = $this->dbi->query(
152
                $local_query,
153
                DatabaseInterface::CONNECT_USER,
154
                DatabaseInterface::QUERY_STORE
155
            );
156
            $rows[$key_id] = $this->dbi->fetchAssoc($result[$key_id]);
157
158
            $where_clauses[$key_id] = str_replace('\\', '\\\\', $where_clause);
159
            $has_unique_condition = $this->showEmptyResultMessageOrSetUniqueCondition(
160
                $rows,
161
                $key_id,
162
                $where_clause_array,
163
                $local_query,
164
                $result
165
            );
166
            if ($has_unique_condition) {
167
                $found_unique_key = true;
168
            }
169
        }
170
        return [$where_clauses, $result, $rows, $found_unique_key];
171
    }
172
173
    /**
174
     * Show message for empty result or set the unique_condition
175
     *
176
     * @param array  $rows               MySQL returned rows
177
     * @param string $key_id             ID in current key
178
     * @param array  $where_clause_array array of where clauses
179
     * @param string $local_query        query performed
180
     * @param array  $result             MySQL result handle
181
     *
182
     * @return boolean
183
     */
184
    private function showEmptyResultMessageOrSetUniqueCondition(
185
        array $rows,
186
        $key_id,
187
        array $where_clause_array,
188
        $local_query,
189
        array $result
190
    ) {
191
        $has_unique_condition = false;
192
193
        // No row returned
194
        if (! $rows[$key_id]) {
195
            unset($rows[$key_id], $where_clause_array[$key_id]);
196
            Response::getInstance()->addHtml(
197
                Util::getMessage(
198
                    __('MySQL returned an empty result set (i.e. zero rows).'),
199
                    $local_query
200
                )
201
            );
202
            /**
203
             * @todo not sure what should be done at this point, but we must not
204
             * exit if we want the message to be displayed
205
             */
206
        } else {// end if (no row returned)
207
            $meta = $this->dbi->getFieldsMeta($result[$key_id]);
208
209
            list($unique_condition, $tmp_clause_is_unique)
210
                = Util::getUniqueCondition(
211
                    $result[$key_id], // handle
212
                    count($meta), // fields_cnt
213
                    $meta, // fields_meta
214
                    $rows[$key_id], // row
215
                    true, // force_unique
216
                    false, // restrict_to_table
217
                    null // analyzed_sql_results
218
                );
219
220
            if (! empty($unique_condition)) {
221
                $has_unique_condition = true;
222
            }
223
            unset($unique_condition, $tmp_clause_is_unique);
224
        }
225
        return $has_unique_condition;
226
    }
227
228
    /**
229
     * No primary key given, just load first row
230
     *
231
     * @param string $table name of the table
232
     * @param string $db    name of the database
233
     *
234
     * @return array containing $result and $rows arrays
235
     */
236
    private function loadFirstRow($table, $db)
237
    {
238
        $result = $this->dbi->query(
239
            'SELECT * FROM ' . Util::backquote($db)
240
            . '.' . Util::backquote($table) . ' LIMIT 1;',
241
            DatabaseInterface::CONNECT_USER,
242
            DatabaseInterface::QUERY_STORE
243
        );
244
        $rows = array_fill(0, $GLOBALS['cfg']['InsertRows'], false);
245
        return [$result, $rows];
246
    }
247
248
    /**
249
     * Add some url parameters
250
     *
251
     * @param array       $url_params         containing $db and $table as url parameters
252
     * @param array       $where_clause_array where clauses array
253
     * @param string|null $where_clause       where clause
254
     *
255
     * @return array Add some url parameters to $url_params array and return it
256
     */
257
    public function urlParamsInEditMode(
258
        array $url_params,
259
        array $where_clause_array,
260
        ?string $where_clause
261
    ) {
262
        if (isset($where_clause)) {
263
            foreach ($where_clause_array as $where_clause) {
264
                $url_params['where_clause'] = trim($where_clause);
265
            }
266
        }
267
        if (! empty($_POST['sql_query'])) {
268
            $url_params['sql_query'] = $_POST['sql_query'];
269
        }
270
        return $url_params;
271
    }
272
273
    /**
274
     * Show type information or function selectors in Insert/Edit
275
     *
276
     * @param string  $which      function|type
277
     * @param array   $url_params containing url parameters
278
     * @param boolean $is_show    whether to show the element in $which
279
     *
280
     * @return string an HTML snippet
281
     */
282
    public function showTypeOrFunction($which, array $url_params, $is_show)
283
    {
284
        $params = [];
285
286
        switch ($which) {
287
            case 'function':
288
                $params['ShowFunctionFields'] = ($is_show ? 0 : 1);
289
                $params['ShowFieldTypesInDataEditView']
290
                = $GLOBALS['cfg']['ShowFieldTypesInDataEditView'];
291
                break;
292
            case 'type':
293
                $params['ShowFieldTypesInDataEditView'] = ($is_show ? 0 : 1);
294
                $params['ShowFunctionFields']
295
                = $GLOBALS['cfg']['ShowFunctionFields'];
296
                break;
297
        }
298
299
        $params['goto'] = 'sql.php';
300
        $this_url_params = array_merge($url_params, $params);
301
302
        if (! $is_show) {
303
            return ' : <a href="tbl_change.php'
304
                . Url::getCommon($this_url_params) . '">'
305
                . $this->showTypeOrFunctionLabel($which)
306
                . '</a>';
307
        }
308
        return '<th><a href="tbl_change.php'
309
            . Url::getCommon($this_url_params)
310
            . '" title="' . __('Hide') . '">'
311
            . $this->showTypeOrFunctionLabel($which)
312
            . '</a></th>';
313
    }
314
315
    /**
316
     * Show type information or function selectors labels in Insert/Edit
317
     *
318
     * @param string $which function|type
319
     *
320
     * @return string an HTML snippet
321
     */
322
    private function showTypeOrFunctionLabel($which)
323
    {
324
        switch ($which) {
325
            case 'function':
326
                return __('Function');
327
            case 'type':
328
                return __('Type');
329
        }
330
331
        return null;
332
    }
333
334
     /**
335
      * Analyze the table column array
336
      *
337
      * @param array   $column         description of column in given table
338
      * @param array   $comments_map   comments for every column that has a comment
339
      * @param boolean $timestamp_seen whether a timestamp has been seen
340
      *
341
      * @return array                   description of column in given table
342
      */
343
    private function analyzeTableColumnsArray(
344
        array $column,
345
        array $comments_map,
346
        $timestamp_seen
347
    ) {
348
        $column['Field_html']    = htmlspecialchars($column['Field']);
349
        $column['Field_md5']     = md5($column['Field']);
350
        // True_Type contains only the type (stops at first bracket)
351
        $column['True_Type']     = preg_replace('@\(.*@s', '', $column['Type']);
352
        $column['len'] = preg_match('@float|double@', $column['Type']) ? 100 : -1;
353
        $column['Field_title']   = $this->getColumnTitle($column, $comments_map);
354
        $column['is_binary']     = $this->isColumn(
355
            $column,
356
            ['binary', 'varbinary']
357
        );
358
        $column['is_blob']       = $this->isColumn(
359
            $column,
360
            ['blob', 'tinyblob', 'mediumblob', 'longblob']
361
        );
362
        $column['is_char']       = $this->isColumn(
363
            $column,
364
            ['char', 'varchar']
365
        );
366
367
        list($column['pma_type'], $column['wrap'], $column['first_timestamp'])
368
            = $this->getEnumSetAndTimestampColumns($column, $timestamp_seen);
369
370
        return $column;
371
    }
372
373
     /**
374
      * Retrieve the column title
375
      *
376
      * @param array $column       description of column in given table
377
      * @param array $comments_map comments for every column that has a comment
378
      *
379
      * @return string              column title
380
      */
381
    private function getColumnTitle(array $column, array $comments_map)
382
    {
383
        if (isset($comments_map[$column['Field']])) {
384
            return '<span style="border-bottom: 1px dashed black;" title="'
385
                . htmlspecialchars($comments_map[$column['Field']]) . '">'
386
                . $column['Field_html'] . '</span>';
387
        }
388
389
        return $column['Field_html'];
390
    }
391
392
     /**
393
      * check whether the column is of a certain type
394
      * the goal is to ensure that types such as "enum('one','two','binary',..)"
395
      * or "enum('one','two','varbinary',..)" are not categorized as binary
396
      *
397
      * @param array $column description of column in given table
398
      * @param array $types  the types to verify
399
      *
400
      * @return boolean whether the column's type if one of the $types
401
      */
402
    public function isColumn(array $column, array $types)
403
    {
404
        foreach ($types as $one_type) {
405
            if (mb_stripos($column['Type'], $one_type) === 0) {
406
                return true;
407
            }
408
        }
409
        return false;
410
    }
411
412
    /**
413
     * Retrieve set, enum, timestamp table columns
414
     *
415
     * @param array   $column         description of column in given table
416
     * @param boolean $timestamp_seen whether a timestamp has been seen
417
     *
418
     * @return array $column['pma_type'], $column['wrap'], $column['first_timestamp']
419
     */
420
    private function getEnumSetAndTimestampColumns(array $column, $timestamp_seen)
421
    {
422
        $column['first_timestamp'] = false;
423
        switch ($column['True_Type']) {
424
            case 'set':
425
                $column['pma_type'] = 'set';
426
                $column['wrap']  = '';
427
                break;
428
            case 'enum':
429
                $column['pma_type'] = 'enum';
430
                $column['wrap']  = '';
431
                break;
432
            case 'timestamp':
433
                if (! $timestamp_seen) {   // can only occur once per table
434
                    $column['first_timestamp'] = true;
435
                }
436
                $column['pma_type'] = $column['Type'];
437
                $column['wrap']  = ' nowrap';
438
                break;
439
440
            default:
441
                $column['pma_type'] = $column['Type'];
442
                $column['wrap']  = ' nowrap';
443
                break;
444
        }
445
        return [$column['pma_type'], $column['wrap'], $column['first_timestamp']];
446
    }
447
448
    /**
449
     * The function column
450
     * We don't want binary data to be destroyed
451
     * Note: from the MySQL manual: "BINARY doesn't affect how the column is
452
     *       stored or retrieved" so it does not mean that the contents is binary
453
     *
454
     * @param array   $column                description of column in given table
455
     * @param boolean $is_upload             upload or no
456
     * @param string  $column_name_appendix  the name attribute
457
     * @param string  $onChangeClause        onchange clause for fields
458
     * @param array   $no_support_types      list of datatypes that are not (yet)
459
     *                                       handled by PMA
460
     * @param integer $tabindex_for_function +3000
461
     * @param integer $tabindex              tab index
462
     * @param integer $idindex               id index
463
     * @param boolean $insert_mode           insert mode or edit mode
464
     * @param boolean $readOnly              is column read only or not
465
     * @param array   $foreignData           foreign key data
466
     *
467
     * @return string                           an html snippet
468
     */
469
    private function getFunctionColumn(
470
        array $column,
471
        $is_upload,
472
        $column_name_appendix,
473
        $onChangeClause,
474
        array $no_support_types,
475
        $tabindex_for_function,
476
        $tabindex,
477
        $idindex,
478
        $insert_mode,
479
        $readOnly,
480
        array $foreignData
481
    ) {
482
        $html_output = '';
483
        if (($GLOBALS['cfg']['ProtectBinary'] === 'blob'
484
            && $column['is_blob'] && !$is_upload)
485
            || ($GLOBALS['cfg']['ProtectBinary'] === 'all'
486
            && $column['is_binary'])
487
            || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob'
488
            && $column['is_binary'])
489
        ) {
490
            $html_output .= '<td class="center">' . __('Binary') . '</td>' . "\n";
491
        } elseif ($readOnly
492
            || mb_strstr($column['True_Type'], 'enum')
493
            || mb_strstr($column['True_Type'], 'set')
494
            || in_array($column['pma_type'], $no_support_types)
495
        ) {
496
            $html_output .= '<td class="center">--</td>' . "\n";
497
        } else {
498
            $html_output .= '<td>' . "\n";
499
500
            $html_output .= '<select name="funcs' . $column_name_appendix . '"'
501
                . ' ' . $onChangeClause
502
                . ' tabindex="' . ($tabindex + $tabindex_for_function) . '"'
503
                . ' id="field_' . $idindex . '_1">';
504
            $html_output .= Util::getFunctionsForField(
505
                $column,
506
                $insert_mode,
507
                $foreignData
508
            ) . "\n";
509
510
            $html_output .= '</select>' . "\n";
511
            $html_output .= '</td>' . "\n";
512
        }
513
        return $html_output;
514
    }
515
516
    /**
517
     * The null column
518
     *
519
     * @param array   $column               description of column in given table
520
     * @param string  $column_name_appendix the name attribute
521
     * @param boolean $real_null_value      is column value null or not null
522
     * @param integer $tabindex             tab index
523
     * @param integer $tabindex_for_null    +6000
524
     * @param integer $idindex              id index
525
     * @param string  $vkey                 [multi_edit]['row_id']
526
     * @param array   $foreigners           keys into foreign fields
527
     * @param array   $foreignData          data about the foreign keys
528
     * @param boolean $readOnly             is column read only or not
529
     *
530
     * @return string                       an html snippet
531
     */
532
    private function getNullColumn(
533
        array $column,
534
        $column_name_appendix,
535
        $real_null_value,
536
        $tabindex,
537
        $tabindex_for_null,
538
        $idindex,
539
        $vkey,
540
        array $foreigners,
541
        array $foreignData,
542
        $readOnly
543
    ) {
544
        if ($column['Null'] != 'YES' || $readOnly) {
545
            return "<td></td>\n";
546
        }
547
        $html_output = '';
548
        $html_output .= '<td>' . "\n";
549
        $html_output .= '<input type="hidden" name="fields_null_prev'
550
            . $column_name_appendix . '"';
551
        if ($real_null_value && !$column['first_timestamp']) {
552
            $html_output .= ' value="on"';
553
        }
554
        $html_output .= ' />' . "\n";
555
556
        $html_output .= '<input type="checkbox" class="checkbox_null" tabindex="'
557
            . ($tabindex + $tabindex_for_null) . '"'
558
            . ' name="fields_null' . $column_name_appendix . '"';
559
        if ($real_null_value) {
560
            $html_output .= ' checked="checked"';
561
        }
562
        $html_output .= ' id="field_' . ($idindex) . '_2" />';
563
564
        // nullify_code is needed by the js nullify() function
565
        $nullify_code = $this->getNullifyCodeForNullColumn(
566
            $column,
567
            $foreigners,
568
            $foreignData
569
        );
570
        // to be able to generate calls to nullify() in jQuery
571
        $html_output .= '<input type="hidden" class="nullify_code" name="nullify_code'
572
            . $column_name_appendix . '" value="' . $nullify_code . '" />';
573
        $html_output .= '<input type="hidden" class="hashed_field" name="hashed_field'
574
            . $column_name_appendix . '" value="' . $column['Field_md5'] . '" />';
575
        $html_output .= '<input type="hidden" class="multi_edit" name="multi_edit'
576
            . $column_name_appendix . '" value="' . Sanitize::escapeJsString($vkey) . '" />';
577
        $html_output .= '</td>' . "\n";
578
579
        return $html_output;
580
    }
581
582
    /**
583
     * Retrieve the nullify code for the null column
584
     *
585
     * @param array $column      description of column in given table
586
     * @param array $foreigners  keys into foreign fields
587
     * @param array $foreignData data about the foreign keys
588
     *
589
     * @return integer
590
     */
591
    private function getNullifyCodeForNullColumn(
592
        array $column,
593
        array $foreigners,
594
        array $foreignData
595
    ) {
596
        $foreigner = $this->relation->searchColumnInForeigners($foreigners, $column['Field']);
597
        if (mb_strstr($column['True_Type'], 'enum')) {
598
            if (mb_strlen((string) $column['Type']) > 20) {
599
                $nullify_code = '1';
600
            } else {
601
                $nullify_code = '2';
602
            }
603
        } elseif (mb_strstr($column['True_Type'], 'set')) {
604
            $nullify_code = '3';
605
        } elseif (!empty($foreigners)
606
            && !empty($foreigner)
607
            && $foreignData['foreign_link'] == false
608
        ) {
609
            // foreign key in a drop-down
610
            $nullify_code = '4';
611
        } elseif (!empty($foreigners)
612
            && !empty($foreigner)
613
            && $foreignData['foreign_link'] == true
614
        ) {
615
            // foreign key with a browsing icon
616
            $nullify_code = '6';
617
        } else {
618
            $nullify_code = '5';
619
        }
620
        return $nullify_code;
621
    }
622
623
    /**
624
     * Get the HTML elements for value column in insert form
625
     * (here, "column" is used in the sense of HTML column in HTML table)
626
     *
627
     * @param array   $column                description of column in given table
628
     * @param string  $backup_field          hidden input field
629
     * @param string  $column_name_appendix  the name attribute
630
     * @param string  $onChangeClause        onchange clause for fields
631
     * @param integer $tabindex              tab index
632
     * @param integer $tabindex_for_value    offset for the values tabindex
633
     * @param integer $idindex               id index
634
     * @param string  $data                  description of the column field
635
     * @param string  $special_chars         special characters
636
     * @param array   $foreignData           data about the foreign keys
637
     * @param array   $paramTableDbArray     array containing $table and $db
638
     * @param integer $rownumber             the row number
639
     * @param array   $titles                An HTML IMG tag for a particular icon from
640
     *                                       a theme, which may be an actual file or
641
     *                                       an icon from a sprite
642
     * @param string  $text_dir              text direction
643
     * @param string  $special_chars_encoded replaced char if the string starts
644
     *                                       with a \r\n pair (0x0d0a) add an extra \n
645
     * @param string  $vkey                  [multi_edit]['row_id']
646
     * @param boolean $is_upload             is upload or not
647
     * @param integer $biggest_max_file_size 0 integer
648
     * @param string  $default_char_editing  default char editing mode which is stored
649
     *                                       in the config.inc.php script
650
     * @param array   $no_support_types      list of datatypes that are not (yet)
651
     *                                       handled by PMA
652
     * @param array   $gis_data_types        list of GIS data types
653
     * @param array   $extracted_columnspec  associative array containing type,
654
     *                                       spec_in_brackets and possibly
655
     *                                       enum_set_values (another array)
656
     * @param boolean $readOnly              is column read only or not
657
     *
658
     * @return string an html snippet
659
     */
660
    private function getValueColumn(
661
        array $column,
662
        $backup_field,
663
        $column_name_appendix,
664
        $onChangeClause,
665
        $tabindex,
666
        $tabindex_for_value,
667
        $idindex,
668
        $data,
669
        $special_chars,
670
        array $foreignData,
671
        array $paramTableDbArray,
672
        $rownumber,
673
        array $titles,
674
        $text_dir,
675
        $special_chars_encoded,
676
        $vkey,
677
        $is_upload,
678
        $biggest_max_file_size,
679
        $default_char_editing,
680
        array $no_support_types,
681
        array $gis_data_types,
682
        array $extracted_columnspec,
683
        $readOnly
684
    ) {
685
        // HTML5 data-* attribute data-type
686
        $data_type = $this->dbi->types->getTypeClass($column['True_Type']);
687
        $html_output = '';
688
689
        if ($foreignData['foreign_link'] == true) {
690
            $html_output .= $this->getForeignLink(
691
                $column,
692
                $backup_field,
693
                $column_name_appendix,
694
                $onChangeClause,
695
                $tabindex,
696
                $tabindex_for_value,
697
                $idindex,
698
                $data,
699
                $paramTableDbArray,
700
                $rownumber,
701
                $titles,
702
                $readOnly
703
            );
704
        } elseif (is_array($foreignData['disp_row'])) {
705
            $html_output .= $this->dispRowForeignData(
706
                $column,
707
                $backup_field,
708
                $column_name_appendix,
709
                $onChangeClause,
710
                $tabindex,
711
                $tabindex_for_value,
712
                $idindex,
713
                $data,
714
                $foreignData,
715
                $readOnly
716
            );
717
        } elseif ($GLOBALS['cfg']['LongtextDoubleTextarea']
718
            && mb_strstr($column['pma_type'], 'longtext')
719
        ) {
720
            $html_output .= $this->getTextarea(
721
                $column,
722
                $backup_field,
723
                $column_name_appendix,
724
                $onChangeClause,
725
                $tabindex,
726
                $tabindex_for_value,
727
                $idindex,
728
                $text_dir,
729
                $special_chars_encoded,
730
                $data_type,
731
                $readOnly
732
            );
733
        } elseif (mb_strstr($column['pma_type'], 'text')) {
734
            $html_output .= $this->getTextarea(
735
                $column,
736
                $backup_field,
737
                $column_name_appendix,
738
                $onChangeClause,
739
                $tabindex,
740
                $tabindex_for_value,
741
                $idindex,
742
                $text_dir,
743
                $special_chars_encoded,
744
                $data_type,
745
                $readOnly
746
            );
747
            $html_output .= "\n";
748
            if (mb_strlen($special_chars) > 32000) {
749
                $html_output .= "</td>\n";
750
                $html_output .= '<td>' . __(
751
                    'Because of its length,<br /> this column might not be editable.'
752
                );
753
            }
754
        } elseif ($column['pma_type'] == 'enum') {
755
            $html_output .= $this->getPmaTypeEnum(
756
                $column,
757
                $backup_field,
758
                $column_name_appendix,
759
                $extracted_columnspec,
760
                $onChangeClause,
761
                $tabindex,
762
                $tabindex_for_value,
763
                $idindex,
764
                $data,
765
                $readOnly
766
            );
767
        } elseif ($column['pma_type'] == 'set') {
768
            $html_output .= $this->getPmaTypeSet(
769
                $column,
770
                $extracted_columnspec,
771
                $backup_field,
772
                $column_name_appendix,
773
                $onChangeClause,
774
                $tabindex,
775
                $tabindex_for_value,
776
                $idindex,
777
                $data,
778
                $readOnly
779
            );
780
        } elseif ($column['is_binary'] || $column['is_blob']) {
781
            $html_output .= $this->getBinaryAndBlobColumn(
782
                $column,
783
                $data,
784
                $special_chars,
785
                $biggest_max_file_size,
786
                $backup_field,
787
                $column_name_appendix,
788
                $onChangeClause,
789
                $tabindex,
790
                $tabindex_for_value,
791
                $idindex,
792
                $text_dir,
793
                $special_chars_encoded,
794
                $vkey,
795
                $is_upload,
796
                $readOnly
797
            );
798
        } elseif (! in_array($column['pma_type'], $no_support_types)) {
799
            $html_output .= $this->getValueColumnForOtherDatatypes(
800
                $column,
801
                $default_char_editing,
802
                $backup_field,
803
                $column_name_appendix,
804
                $onChangeClause,
805
                $tabindex,
806
                $special_chars,
807
                $tabindex_for_value,
808
                $idindex,
809
                $text_dir,
810
                $special_chars_encoded,
811
                $data,
812
                $extracted_columnspec,
813
                $readOnly
814
            );
815
        }
816
817
        if (in_array($column['pma_type'], $gis_data_types)) {
818
            $html_output .= $this->getHtmlForGisDataTypes();
819
        }
820
821
        return $html_output;
822
    }
823
824
    /**
825
     * Get HTML for foreign link in insert form
826
     *
827
     * @param array   $column               description of column in given table
828
     * @param string  $backup_field         hidden input field
829
     * @param string  $column_name_appendix the name attribute
830
     * @param string  $onChangeClause       onchange clause for fields
831
     * @param integer $tabindex             tab index
832
     * @param integer $tabindex_for_value   offset for the values tabindex
833
     * @param integer $idindex              id index
834
     * @param string  $data                 data to edit
835
     * @param array   $paramTableDbArray    array containing $table and $db
836
     * @param integer $rownumber            the row number
837
     * @param array   $titles               An HTML IMG tag for a particular icon from
838
     *                                      a theme, which may be an actual file or
839
     *                                      an icon from a sprite
840
     * @param boolean $readOnly             is column read only or not
841
     *
842
     * @return string                       an html snippet
843
     */
844
    private function getForeignLink(
845
        array $column,
846
        $backup_field,
847
        $column_name_appendix,
848
        $onChangeClause,
849
        $tabindex,
850
        $tabindex_for_value,
851
        $idindex,
852
        $data,
853
        array $paramTableDbArray,
854
        $rownumber,
855
        array $titles,
856
        $readOnly
857
    ) {
858
        list($table, $db) = $paramTableDbArray;
859
        $html_output = '';
860
        $html_output .= $backup_field . "\n";
861
862
        $html_output .= '<input type="hidden" name="fields_type'
863
            . $column_name_appendix . '" value="foreign" />';
864
865
        $html_output .= '<input type="text" name="fields' . $column_name_appendix . '" '
866
            . 'class="textfield" '
867
            . $onChangeClause . ' '
868
            . ($readOnly ? 'readonly="readonly" ' : '')
869
            . 'tabindex="' . ($tabindex + $tabindex_for_value) . '" '
870
            . 'id="field_' . ($idindex) . '_3" '
871
            . 'value="' . htmlspecialchars($data) . '" />';
872
873
        $html_output .= '<a class="ajax browse_foreign" href="browse_foreigners.php'
874
            . Url::getCommon(
875
                [
876
                    'db' => $db,
877
                    'table' => $table,
878
                    'field' => $column['Field'],
879
                    'rownumber' => $rownumber,
880
                    'data'      => $data
881
                ]
882
            ) . '">'
883
            . str_replace("'", "\'", $titles['Browse']) . '</a>';
884
        return $html_output;
885
    }
886
887
    /**
888
     * Get HTML to display foreign data
889
     *
890
     * @param array   $column               description of column in given table
891
     * @param string  $backup_field         hidden input field
892
     * @param string  $column_name_appendix the name attribute
893
     * @param string  $onChangeClause       onchange clause for fields
894
     * @param integer $tabindex             tab index
895
     * @param integer $tabindex_for_value   offset for the values tabindex
896
     * @param integer $idindex              id index
897
     * @param string  $data                 data to edit
898
     * @param array   $foreignData          data about the foreign keys
899
     * @param boolean $readOnly             is display read only or not
900
     *
901
     * @return string                       an html snippet
902
     */
903
    private function dispRowForeignData(
904
        $column,
905
        $backup_field,
906
        $column_name_appendix,
907
        $onChangeClause,
908
        $tabindex,
909
        $tabindex_for_value,
910
        $idindex,
911
        $data,
912
        array $foreignData,
913
        $readOnly
914
    ) {
915
        $html_output = '';
916
        $html_output .= $backup_field . "\n";
917
        $html_output .= '<input type="hidden"'
918
            . ' name="fields_type' . $column_name_appendix . '"';
919
        if ($column['is_binary']) {
920
            $html_output .= ' value="hex" />';
921
        } else {
922
            $html_output .= ' value="foreign" />';
923
        }
924
925
        $html_output .= '<select name="fields' . $column_name_appendix . '"'
926
            . ' ' . $onChangeClause
927
            . ' class="textfield"'
928
            . ($readOnly ? ' disabled' : '')
929
            . ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
930
            . ' id="field_' . $idindex . '_3">';
931
        $html_output .= $this->relation->foreignDropdown(
932
            $foreignData['disp_row'],
933
            $foreignData['foreign_field'],
934
            $foreignData['foreign_display'],
935
            $data,
936
            $GLOBALS['cfg']['ForeignKeyMaxLimit']
937
        );
938
        $html_output .= '</select>';
939
940
        //Add hidden input, as disabled <select> input does not included in POST.
941
        if ($readOnly) {
942
            $html_output .= '<input name="fields' . $column_name_appendix . '"'
943
                . ' type="hidden" value="' . htmlspecialchars($data) . '">';
944
        }
945
946
        return $html_output;
947
    }
948
949
    /**
950
     * Get HTML textarea for insert form
951
     *
952
     * @param array   $column                column information
953
     * @param string  $backup_field          hidden input field
954
     * @param string  $column_name_appendix  the name attribute
955
     * @param string  $onChangeClause        onchange clause for fields
956
     * @param integer $tabindex              tab index
957
     * @param integer $tabindex_for_value    offset for the values tabindex
958
     * @param integer $idindex               id index
959
     * @param string  $text_dir              text direction
960
     * @param string  $special_chars_encoded replaced char if the string starts
961
     *                                       with a \r\n pair (0x0d0a) add an extra \n
962
     * @param string  $data_type             the html5 data-* attribute type
963
     * @param boolean $readOnly              is column read only or not
964
     *
965
     * @return string                       an html snippet
966
     */
967
    private function getTextarea(
968
        array $column,
969
        $backup_field,
970
        $column_name_appendix,
971
        $onChangeClause,
972
        $tabindex,
973
        $tabindex_for_value,
974
        $idindex,
975
        $text_dir,
976
        $special_chars_encoded,
977
        $data_type,
978
        $readOnly
979
    ) {
980
        $the_class = '';
981
        $textAreaRows = $GLOBALS['cfg']['TextareaRows'];
982
        $textareaCols = $GLOBALS['cfg']['TextareaCols'];
983
984
        if ($column['is_char']) {
985
            /**
986
             * @todo clarify the meaning of the "textfield" class and explain
987
             *       why character columns have the "char" class instead
988
             */
989
            $the_class = 'char charField';
990
            $textAreaRows = max($GLOBALS['cfg']['CharTextareaRows'], 7);
991
            $textareaCols = $GLOBALS['cfg']['CharTextareaCols'];
992
            $extracted_columnspec = Util::extractColumnSpec(
993
                $column['Type']
994
            );
995
            $maxlength = $extracted_columnspec['spec_in_brackets'];
996
        } elseif ($GLOBALS['cfg']['LongtextDoubleTextarea']
997
            && mb_strstr($column['pma_type'], 'longtext')
998
        ) {
999
            $textAreaRows = $GLOBALS['cfg']['TextareaRows'] * 2;
1000
            $textareaCols = $GLOBALS['cfg']['TextareaCols'] * 2;
1001
        }
1002
        $html_output = $backup_field . "\n"
1003
            . '<textarea name="fields' . $column_name_appendix . '"'
1004
            . ' class="' . $the_class . '"'
1005
            . ($readOnly ? ' readonly="readonly"' : '')
1006
            . (isset($maxlength) ? ' data-maxlength="' . $maxlength . '"' : '')
1007
            . ' rows="' . $textAreaRows . '"'
1008
            . ' cols="' . $textareaCols . '"'
1009
            . ' dir="' . $text_dir . '"'
1010
            . ' id="field_' . ($idindex) . '_3"'
1011
            . (! empty($onChangeClause) ? ' ' . $onChangeClause : '')
1012
            . ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
1013
            . ' data-type="' . $data_type . '">'
1014
            . $special_chars_encoded
1015
            . '</textarea>';
1016
1017
        return $html_output;
1018
    }
1019
1020
    /**
1021
     * Get HTML for enum type
1022
     *
1023
     * @param array   $column               description of column in given table
1024
     * @param string  $backup_field         hidden input field
1025
     * @param string  $column_name_appendix the name attribute
1026
     * @param array   $extracted_columnspec associative array containing type,
1027
     *                                      spec_in_brackets and possibly
1028
     *                                      enum_set_values (another array)
1029
     * @param string  $onChangeClause       onchange clause for fields
1030
     * @param integer $tabindex             tab index
1031
     * @param integer $tabindex_for_value   offset for the values tabindex
1032
     * @param integer $idindex              id index
1033
     * @param mixed   $data                 data to edit
1034
     * @param boolean $readOnly             is column read only or not
1035
     *
1036
     * @return string an html snippet
1037
     */
1038
    private function getPmaTypeEnum(
1039
        array $column,
1040
        $backup_field,
1041
        $column_name_appendix,
1042
        array $extracted_columnspec,
1043
        $onChangeClause,
1044
        $tabindex,
1045
        $tabindex_for_value,
1046
        $idindex,
1047
        $data,
1048
        $readOnly
1049
    ) {
1050
        $html_output = '';
1051
        if (! isset($column['values'])) {
1052
            $column['values'] = $this->getColumnEnumValues(
1053
                $column,
1054
                $extracted_columnspec
1055
            );
1056
        }
1057
        $column_enum_values = $column['values'];
1058
        $html_output .= '<input type="hidden" name="fields_type'
1059
            . $column_name_appendix . '" value="enum" />';
1060
        $html_output .= "\n" . '            ' . $backup_field . "\n";
1061
        if (mb_strlen($column['Type']) > 20) {
1062
            $html_output .= $this->getDropDownDependingOnLength(
1063
                $column,
1064
                $column_name_appendix,
1065
                $onChangeClause,
1066
                $tabindex,
1067
                $tabindex_for_value,
1068
                $idindex,
1069
                $data,
1070
                $column_enum_values,
1071
                $readOnly
1072
            );
1073
        } else {
1074
            $html_output .= $this->getRadioButtonDependingOnLength(
1075
                $column_name_appendix,
1076
                $onChangeClause,
1077
                $tabindex,
1078
                $column,
1079
                $tabindex_for_value,
1080
                $idindex,
1081
                $data,
1082
                $column_enum_values,
1083
                $readOnly
1084
            );
1085
        }
1086
        return $html_output;
1087
    }
1088
1089
    /**
1090
     * Get column values
1091
     *
1092
     * @param array $column               description of column in given table
1093
     * @param array $extracted_columnspec associative array containing type,
1094
     *                                    spec_in_brackets and possibly enum_set_values
1095
     *                                    (another array)
1096
     *
1097
     * @return array column values as an associative array
1098
     */
1099
    private function getColumnEnumValues(array $column, array $extracted_columnspec)
1100
    {
1101
        $column['values'] = [];
1102
        foreach ($extracted_columnspec['enum_set_values'] as $val) {
1103
            $column['values'][] = [
1104
                'plain' => $val,
1105
                'html'  => htmlspecialchars($val),
1106
            ];
1107
        }
1108
        return $column['values'];
1109
    }
1110
1111
    /**
1112
     * Get HTML drop down for more than 20 string length
1113
     *
1114
     * @param array   $column               description of column in given table
1115
     * @param string  $column_name_appendix the name attribute
1116
     * @param string  $onChangeClause       onchange clause for fields
1117
     * @param integer $tabindex             tab index
1118
     * @param integer $tabindex_for_value   offset for the values tabindex
1119
     * @param integer $idindex              id index
1120
     * @param string  $data                 data to edit
1121
     * @param array   $column_enum_values   $column['values']
1122
     * @param boolean $readOnly             is column read only or not
1123
     *
1124
     * @return string                       an html snippet
1125
     */
1126
    private function getDropDownDependingOnLength(
1127
        array $column,
1128
        $column_name_appendix,
1129
        $onChangeClause,
1130
        $tabindex,
1131
        $tabindex_for_value,
1132
        $idindex,
1133
        $data,
1134
        array $column_enum_values,
1135
        $readOnly
1136
    ) {
1137
        $html_output = '<select name="fields' . $column_name_appendix . '"'
1138
            . ' ' . $onChangeClause
1139
            . ' class="textfield"'
1140
            . ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
1141
            . ($readOnly ? ' disabled' : '')
1142
            . ' id="field_' . ($idindex) . '_3">';
1143
        $html_output .= '<option value="">&nbsp;</option>' . "\n";
1144
1145
        $selected_html = '';
1146
        foreach ($column_enum_values as $enum_value) {
1147
            $html_output .= '<option value="' . $enum_value['html'] . '"';
1148
            if ($data == $enum_value['plain']
1149
                || ($data == ''
1150
                && (! isset($_REQUEST['where_clause']) || $column['Null'] != 'YES')
1151
                && isset($column['Default'])
1152
                && $enum_value['plain'] == $column['Default'])
1153
            ) {
1154
                $html_output .= ' selected="selected"';
1155
                $selected_html = $enum_value['html'];
1156
            }
1157
            $html_output .= '>' . $enum_value['html'] . '</option>' . "\n";
1158
        }
1159
        $html_output .= '</select>';
1160
1161
        //Add hidden input, as disabled <select> input does not included in POST.
1162
        if ($readOnly) {
1163
            $html_output .= '<input name="fields' . $column_name_appendix . '"'
1164
                . ' type="hidden" value="' . $selected_html . '">';
1165
        }
1166
        return $html_output;
1167
    }
1168
1169
    /**
1170
     * Get HTML radio button for less than 20 string length
1171
     *
1172
     * @param string  $column_name_appendix the name attribute
1173
     * @param string  $onChangeClause       onchange clause for fields
1174
     * @param integer $tabindex             tab index
1175
     * @param array   $column               description of column in given table
1176
     * @param integer $tabindex_for_value   offset for the values tabindex
1177
     * @param integer $idindex              id index
1178
     * @param string  $data                 data to edit
1179
     * @param array   $column_enum_values   $column['values']
1180
     * @param boolean $readOnly             is column read only or not
1181
     *
1182
     * @return string                       an html snippet
1183
     */
1184
    private function getRadioButtonDependingOnLength(
1185
        $column_name_appendix,
1186
        $onChangeClause,
1187
        $tabindex,
1188
        array $column,
1189
        $tabindex_for_value,
1190
        $idindex,
1191
        $data,
1192
        array $column_enum_values,
1193
        $readOnly
1194
    ) {
1195
        $j = 0;
1196
        $html_output = '';
1197
        foreach ($column_enum_values as $enum_value) {
1198
            $html_output .= '            '
1199
                . '<input type="radio" name="fields' . $column_name_appendix . '"'
1200
                . ' class="textfield"'
1201
                . ' value="' . $enum_value['html'] . '"'
1202
                . ' id="field_' . ($idindex) . '_3_' . $j . '"'
1203
                . ' ' . $onChangeClause;
1204
            if ($data == $enum_value['plain']
1205
                || ($data == ''
1206
                && (! isset($_REQUEST['where_clause']) || $column['Null'] != 'YES')
1207
                && isset($column['Default'])
1208
                && $enum_value['plain'] == $column['Default'])
1209
            ) {
1210
                $html_output .= ' checked="checked"';
1211
            } elseif ($readOnly) {
1212
                $html_output .= ' disabled';
1213
            }
1214
            $html_output .= ' tabindex="' . ($tabindex + $tabindex_for_value) . '" />';
1215
            $html_output .= '<label for="field_' . $idindex . '_3_' . $j . '">'
1216
                . $enum_value['html'] . '</label>' . "\n";
1217
            $j++;
1218
        }
1219
        return $html_output;
1220
    }
1221
1222
    /**
1223
     * Get the HTML for 'set' pma type
1224
     *
1225
     * @param array   $column               description of column in given table
1226
     * @param array   $extracted_columnspec associative array containing type,
1227
     *                                      spec_in_brackets and possibly
1228
     *                                      enum_set_values (another array)
1229
     * @param string  $backup_field         hidden input field
1230
     * @param string  $column_name_appendix the name attribute
1231
     * @param string  $onChangeClause       onchange clause for fields
1232
     * @param integer $tabindex             tab index
1233
     * @param integer $tabindex_for_value   offset for the values tabindex
1234
     * @param integer $idindex              id index
1235
     * @param string  $data                 description of the column field
1236
     * @param boolean $readOnly             is column read only or not
1237
     *
1238
     * @return string                       an html snippet
1239
     */
1240
    private function getPmaTypeSet(
1241
        array $column,
1242
        array $extracted_columnspec,
1243
        $backup_field,
1244
        $column_name_appendix,
1245
        $onChangeClause,
1246
        $tabindex,
1247
        $tabindex_for_value,
1248
        $idindex,
1249
        $data,
1250
        $readOnly
1251
    ) {
1252
        list($column_set_values, $select_size) = $this->getColumnSetValueAndSelectSize(
1253
            $column,
1254
            $extracted_columnspec
1255
        );
1256
        $vset = array_flip(explode(',', $data));
1257
        $html_output = $backup_field . "\n";
1258
        $html_output .= '<input type="hidden" name="fields_type'
1259
            . $column_name_appendix . '" value="set" />';
1260
        $html_output .= '<select name="fields' . $column_name_appendix . '[]' . '"'
1261
            . ' class="textfield"'
1262
            . ($readOnly ? ' disabled' : '')
1263
            . ' size="' . $select_size . '"'
1264
            . ' multiple="multiple"'
1265
            . ' ' . $onChangeClause
1266
            . ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
1267
            . ' id="field_' . ($idindex) . '_3">';
1268
1269
        $selected_html = '';
1270
        foreach ($column_set_values as $column_set_value) {
1271
            $html_output .= '<option value="' . $column_set_value['html'] . '"';
1272
            if (isset($vset[$column_set_value['plain']])) {
1273
                $html_output .= ' selected="selected"';
1274
                $selected_html = $column_set_value['html'];
1275
            }
1276
            $html_output .= '>' . $column_set_value['html'] . '</option>' . "\n";
1277
        }
1278
        $html_output .= '</select>';
1279
1280
        //Add hidden input, as disabled <select> input does not included in POST.
1281
        if ($readOnly) {
1282
            $html_output .= '<input name="fields' . $column_name_appendix . '[]' . '"'
1283
                . ' type="hidden" value="' . $selected_html . '">';
1284
        }
1285
        return $html_output;
1286
    }
1287
1288
    /**
1289
     * Retrieve column 'set' value and select size
1290
     *
1291
     * @param array $column               description of column in given table
1292
     * @param array $extracted_columnspec associative array containing type,
1293
     *                                    spec_in_brackets and possibly enum_set_values
1294
     *                                    (another array)
1295
     *
1296
     * @return array $column['values'], $column['select_size']
1297
     */
1298
    private function getColumnSetValueAndSelectSize(
1299
        array $column,
1300
        array $extracted_columnspec
1301
    ) {
1302
        if (! isset($column['values'])) {
1303
            $column['values'] = [];
1304
            foreach ($extracted_columnspec['enum_set_values'] as $val) {
1305
                $column['values'][] = [
1306
                    'plain' => $val,
1307
                    'html'  => htmlspecialchars($val),
1308
                ];
1309
            }
1310
            $column['select_size'] = min(4, count($column['values']));
1311
        }
1312
        return [$column['values'], $column['select_size']];
1313
    }
1314
1315
    /**
1316
     * Get HTML for binary and blob column
1317
     *
1318
     * @param array       $column                description of column in given table
1319
     * @param string|null $data                  data to edit
1320
     * @param string      $special_chars         special characters
1321
     * @param integer     $biggest_max_file_size biggest max file size for uploading
1322
     * @param string      $backup_field          hidden input field
1323
     * @param string      $column_name_appendix  the name attribute
1324
     * @param string      $onChangeClause        onchange clause for fields
1325
     * @param integer     $tabindex              tab index
1326
     * @param integer     $tabindex_for_value    offset for the values tabindex
1327
     * @param integer     $idindex               id index
1328
     * @param string      $text_dir              text direction
1329
     * @param string      $special_chars_encoded replaced char if the string starts
1330
     *                                           with a \r\n pair (0x0d0a) add an
1331
     *                                           extra \n
1332
     * @param string      $vkey                  [multi_edit]['row_id']
1333
     * @param boolean     $is_upload             is upload or not
1334
     * @param boolean     $readOnly              is column read only or not
1335
     *
1336
     * @return string                           an html snippet
1337
     */
1338
    private function getBinaryAndBlobColumn(
1339
        array $column,
1340
        ?string $data,
1341
        $special_chars,
1342
        $biggest_max_file_size,
1343
        $backup_field,
1344
        $column_name_appendix,
1345
        $onChangeClause,
1346
        $tabindex,
1347
        $tabindex_for_value,
1348
        $idindex,
1349
        $text_dir,
1350
        $special_chars_encoded,
1351
        $vkey,
1352
        $is_upload,
1353
        $readOnly
1354
    ) {
1355
        $html_output = '';
1356
        // Add field type : Protected or Hexadecimal
1357
        $fields_type_html = '<input type="hidden" name="fields_type'
1358
            . $column_name_appendix . '" value="%s" />';
1359
        // Default value : hex
1360
        $fields_type_val = 'hex';
1361
        if (($GLOBALS['cfg']['ProtectBinary'] === 'blob' && $column['is_blob'])
1362
            || ($GLOBALS['cfg']['ProtectBinary'] === 'all')
1363
            || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob' && !$column['is_blob'])
1364
        ) {
1365
            $html_output .= __('Binary - do not edit');
1366
            if (isset($data)) {
1367
                $data_size = Util::formatByteDown(
1368
                    mb_strlen(stripslashes($data)),
1369
                    3,
1370
                    1
1371
                );
1372
                $html_output .= ' (' . $data_size[0] . ' ' . $data_size[1] . ')';
1373
                unset($data_size);
1374
            }
1375
            $fields_type_val = 'protected';
1376
            $html_output .= '<input type="hidden" name="fields'
1377
                . $column_name_appendix . '" value="" />';
1378
        } elseif ($column['is_blob']
1379
            || ($column['len'] > $GLOBALS['cfg']['LimitChars'])
1380
        ) {
1381
            $html_output .= "\n" . $this->getTextarea(
1382
                $column,
1383
                $backup_field,
1384
                $column_name_appendix,
1385
                $onChangeClause,
1386
                $tabindex,
1387
                $tabindex_for_value,
1388
                $idindex,
1389
                $text_dir,
1390
                $special_chars_encoded,
1391
                'HEX',
1392
                $readOnly
1393
            );
1394
        } else {
1395
            // field size should be at least 4 and max $GLOBALS['cfg']['LimitChars']
1396
            $fieldsize = min(max($column['len'], 4), $GLOBALS['cfg']['LimitChars']);
1397
            $html_output .= "\n" . $backup_field . "\n" . $this->getHtmlInput(
1398
                $column,
1399
                $column_name_appendix,
1400
                $special_chars,
1401
                $fieldsize,
1402
                $onChangeClause,
1403
                $tabindex,
1404
                $tabindex_for_value,
1405
                $idindex,
1406
                'HEX',
1407
                $readOnly
1408
            );
1409
        }
1410
        $html_output .= sprintf($fields_type_html, $fields_type_val);
1411
1412
        if ($is_upload && $column['is_blob'] && !$readOnly) {
1413
            // We don't want to prevent users from using
1414
            // browser's default drag-drop feature on some page(s),
1415
            // so we add noDragDrop class to the input
1416
            $html_output .= '<br />'
1417
                . '<input type="file"'
1418
                . ' name="fields_upload' . $vkey . '[' . $column['Field_md5'] . ']"'
1419
                . ' class="textfield noDragDrop" id="field_' . $idindex . '_3" size="10"'
1420
                . ' ' . $onChangeClause . '/>&nbsp;';
1421
            list($html_out,) = $this->getMaxUploadSize(
1422
                $column,
1423
                $biggest_max_file_size
1424
            );
1425
            $html_output .= $html_out;
1426
        }
1427
1428
        if (!empty($GLOBALS['cfg']['UploadDir']) && !$readOnly) {
1429
            $html_output .= $this->getSelectOptionForUpload($vkey, $column);
1430
        }
1431
1432
        return $html_output;
1433
    }
1434
1435
    /**
1436
     * Get HTML input type
1437
     *
1438
     * @param array   $column               description of column in given table
1439
     * @param string  $column_name_appendix the name attribute
1440
     * @param string  $special_chars        special characters
1441
     * @param integer $fieldsize            html field size
1442
     * @param string  $onChangeClause       onchange clause for fields
1443
     * @param integer $tabindex             tab index
1444
     * @param integer $tabindex_for_value   offset for the values tabindex
1445
     * @param integer $idindex              id index
1446
     * @param string  $data_type            the html5 data-* attribute type
1447
     * @param boolean $readOnly             is column read only or not
1448
     *
1449
     * @return string                       an html snippet
1450
     */
1451
    private function getHtmlInput(
1452
        array $column,
1453
        $column_name_appendix,
1454
        $special_chars,
1455
        $fieldsize,
1456
        $onChangeClause,
1457
        $tabindex,
1458
        $tabindex_for_value,
1459
        $idindex,
1460
        $data_type,
1461
        $readOnly
1462
    ) {
1463
        $input_type = 'text';
1464
        // do not use the 'date' or 'time' types here; they have no effect on some
1465
        // browsers and create side effects (see bug #4218)
1466
1467
        $the_class = 'textfield';
1468
        // verify True_Type which does not contain the parentheses and length
1469
        if ($readOnly) {
1470
            //NOOP. Disable date/timepicker
1471
        } elseif ($column['True_Type'] === 'date') {
1472
            $the_class .= ' datefield';
1473
        } elseif ($column['True_Type'] === 'time') {
1474
            $the_class .= ' timefield';
1475
        } elseif ($column['True_Type'] === 'datetime'
1476
            || $column['True_Type'] === 'timestamp'
1477
        ) {
1478
            $the_class .= ' datetimefield';
1479
        }
1480
        $input_min_max = false;
1481
        if (in_array($column['True_Type'], $this->dbi->types->getIntegerTypes())) {
1482
            $extracted_columnspec = Util::extractColumnSpec(
1483
                $column['Type']
1484
            );
1485
            $is_unsigned = $extracted_columnspec['unsigned'];
1486
            $min_max_values = $this->dbi->types->getIntegerRange(
1487
                $column['True_Type'],
1488
                ! $is_unsigned
1489
            );
1490
            $input_min_max = 'min="' . $min_max_values[0] . '" '
1491
                . 'max="' . $min_max_values[1] . '"';
1492
            $data_type = 'INT';
1493
        }
1494
        return '<input type="' . $input_type . '"'
1495
            . ' name="fields' . $column_name_appendix . '"'
1496
            . ' value="' . $special_chars . '" size="' . $fieldsize . '"'
1497
            . ((isset($column['is_char']) && $column['is_char'])
1498
            ? ' data-maxlength="' . $fieldsize . '"'
1499
            : '')
1500
            . ($readOnly ? ' readonly="readonly"' : '')
1501
            . ($input_min_max !== false ? ' ' . $input_min_max : '')
1502
            . ' data-type="' . $data_type . '"'
1503
            . ($input_type === 'time' ? ' step="1"' : '')
1504
            . ' class="' . $the_class . '" ' . $onChangeClause
1505
            . ' tabindex="' . ($tabindex + $tabindex_for_value) . '"'
1506
            . ' id="field_' . ($idindex) . '_3" />';
1507
    }
1508
1509
    /**
1510
     * Get HTML select option for upload
1511
     *
1512
     * @param string $vkey   [multi_edit]['row_id']
1513
     * @param array  $column description of column in given table
1514
     *
1515
     * @return string|void an html snippet
1516
     */
1517
    private function getSelectOptionForUpload($vkey, array $column)
1518
    {
1519
        $files = $this->fileListing->getFileSelectOptions(
1520
            Util::userDir($GLOBALS['cfg']['UploadDir'])
1521
        );
1522
1523
        if ($files === false) {
1524
            return '<span style="color:red">' . __('Error') . '</span><br />' . "\n"
1525
                . __('The directory you set for upload work cannot be reached.') . "\n";
1526
        } elseif (!empty($files)) {
1527
            return "<br />\n"
1528
                . '<i>' . __('Or') . '</i>' . ' '
1529
                . __('web server upload directory:') . '<br />' . "\n"
1530
                . '<select size="1" name="fields_uploadlocal'
1531
                . $vkey . '[' . $column['Field_md5'] . ']">' . "\n"
1532
                . '<option value="" selected="selected"></option>' . "\n"
1533
                . $files
1534
                . '</select>' . "\n";
1535
        }
1536
1537
        return null;
1538
    }
1539
1540
    /**
1541
     * Retrieve the maximum upload file size
1542
     *
1543
     * @param array   $column                description of column in given table
1544
     * @param integer $biggest_max_file_size biggest max file size for uploading
1545
     *
1546
     * @return array an html snippet and $biggest_max_file_size
1547
     */
1548
    private function getMaxUploadSize(array $column, $biggest_max_file_size)
1549
    {
1550
        // find maximum upload size, based on field type
1551
        /**
1552
         * @todo with functions this is not so easy, as you can basically
1553
         * process any data with function like MD5
1554
         */
1555
        global $max_upload_size;
1556
        $max_field_sizes = [
1557
            'tinyblob'   =>        '256',
1558
            'blob'       =>      '65536',
1559
            'mediumblob' =>   '16777216',
1560
            'longblob'   => '4294967296' // yeah, really
1561
        ];
1562
1563
        $this_field_max_size = $max_upload_size; // from PHP max
1564
        if ($this_field_max_size > $max_field_sizes[$column['pma_type']]) {
1565
            $this_field_max_size = $max_field_sizes[$column['pma_type']];
1566
        }
1567
        $html_output
1568
            = Util::getFormattedMaximumUploadSize(
1569
                $this_field_max_size
1570
            ) . "\n";
1571
        // do not generate here the MAX_FILE_SIZE, because we should
1572
        // put only one in the form to accommodate the biggest field
1573
        if ($this_field_max_size > $biggest_max_file_size) {
1574
            $biggest_max_file_size = $this_field_max_size;
1575
        }
1576
        return [$html_output, $biggest_max_file_size];
1577
    }
1578
1579
    /**
1580
     * Get HTML for the Value column of other datatypes
1581
     * (here, "column" is used in the sense of HTML column in HTML table)
1582
     *
1583
     * @param array   $column                description of column in given table
1584
     * @param string  $default_char_editing  default char editing mode which is stored
1585
     *                                       in the config.inc.php script
1586
     * @param string  $backup_field          hidden input field
1587
     * @param string  $column_name_appendix  the name attribute
1588
     * @param string  $onChangeClause        onchange clause for fields
1589
     * @param integer $tabindex              tab index
1590
     * @param string  $special_chars         special characters
1591
     * @param integer $tabindex_for_value    offset for the values tabindex
1592
     * @param integer $idindex               id index
1593
     * @param string  $text_dir              text direction
1594
     * @param string  $special_chars_encoded replaced char if the string starts
1595
     *                                       with a \r\n pair (0x0d0a) add an extra \n
1596
     * @param string  $data                  data to edit
1597
     * @param array   $extracted_columnspec  associative array containing type,
1598
     *                                       spec_in_brackets and possibly
1599
     *                                       enum_set_values (another array)
1600
     * @param boolean $readOnly              is column read only or not
1601
     *
1602
     * @return string an html snippet
1603
     */
1604
    private function getValueColumnForOtherDatatypes(
1605
        array $column,
1606
        $default_char_editing,
1607
        $backup_field,
1608
        $column_name_appendix,
1609
        $onChangeClause,
1610
        $tabindex,
1611
        $special_chars,
1612
        $tabindex_for_value,
1613
        $idindex,
1614
        $text_dir,
1615
        $special_chars_encoded,
1616
        $data,
1617
        array $extracted_columnspec,
1618
        $readOnly
1619
    ) {
1620
        // HTML5 data-* attribute data-type
1621
        $data_type = $this->dbi->types->getTypeClass($column['True_Type']);
1622
        $fieldsize = $this->getColumnSize($column, $extracted_columnspec);
1623
        $html_output = $backup_field . "\n";
1624
        if ($column['is_char']
1625
            && ($GLOBALS['cfg']['CharEditing'] == 'textarea'
1626
            || mb_strpos($data, "\n") !== false)
1627
        ) {
1628
            $html_output .= "\n";
1629
            $GLOBALS['cfg']['CharEditing'] = $default_char_editing;
1630
            $html_output .= $this->getTextarea(
1631
                $column,
1632
                $backup_field,
1633
                $column_name_appendix,
1634
                $onChangeClause,
1635
                $tabindex,
1636
                $tabindex_for_value,
1637
                $idindex,
1638
                $text_dir,
1639
                $special_chars_encoded,
1640
                $data_type,
1641
                $readOnly
1642
            );
1643
        } else {
1644
            $html_output .= $this->getHtmlInput(
1645
                $column,
1646
                $column_name_appendix,
1647
                $special_chars,
1648
                $fieldsize,
1649
                $onChangeClause,
1650
                $tabindex,
1651
                $tabindex_for_value,
1652
                $idindex,
1653
                $data_type,
1654
                $readOnly
1655
            );
1656
1657
            $virtual = [
1658
                'VIRTUAL', 'PERSISTENT', 'VIRTUAL GENERATED', 'STORED GENERATED'
1659
            ];
1660
            if (in_array($column['Extra'], $virtual)) {
1661
                $html_output .= '<input type="hidden" name="virtual'
1662
                    . $column_name_appendix . '" value="1" />';
1663
            }
1664
            if ($column['Extra'] == 'auto_increment') {
1665
                $html_output .= '<input type="hidden" name="auto_increment'
1666
                    . $column_name_appendix . '" value="1" />';
1667
            }
1668
            if (substr($column['pma_type'], 0, 9) == 'timestamp') {
1669
                $html_output .= '<input type="hidden" name="fields_type'
1670
                    . $column_name_appendix . '" value="timestamp" />';
1671
            }
1672
            if (substr($column['pma_type'], 0, 8) == 'datetime') {
1673
                $html_output .= '<input type="hidden" name="fields_type'
1674
                    . $column_name_appendix . '" value="datetime" />';
1675
            }
1676
            if ($column['True_Type'] == 'bit') {
1677
                $html_output .= '<input type="hidden" name="fields_type'
1678
                    . $column_name_appendix . '" value="bit" />';
1679
            }
1680
            if ($column['pma_type'] == 'date'
1681
                || $column['pma_type'] == 'datetime'
1682
                || substr($column['pma_type'], 0, 9) == 'timestamp'
1683
            ) {
1684
                // the _3 suffix points to the date field
1685
                // the _2 suffix points to the corresponding NULL checkbox
1686
                // in dateFormat, 'yy' means the year with 4 digits
1687
            }
1688
        }
1689
        return $html_output;
1690
    }
1691
1692
    /**
1693
     * Get the field size
1694
     *
1695
     * @param array $column               description of column in given table
1696
     * @param array $extracted_columnspec associative array containing type,
1697
     *                                    spec_in_brackets and possibly enum_set_values
1698
     *                                    (another array)
1699
     *
1700
     * @return integer      field size
1701
     */
1702
    private function getColumnSize(array $column, array $extracted_columnspec)
1703
    {
1704
        if ($column['is_char']) {
1705
            $fieldsize = $extracted_columnspec['spec_in_brackets'];
1706
            if ($fieldsize > $GLOBALS['cfg']['MaxSizeForInputField']) {
1707
                /**
1708
                 * This case happens for CHAR or VARCHAR columns which have
1709
                 * a size larger than the maximum size for input field.
1710
                 */
1711
                $GLOBALS['cfg']['CharEditing'] = 'textarea';
1712
            }
1713
        } else {
1714
            /**
1715
             * This case happens for example for INT or DATE columns;
1716
             * in these situations, the value returned in $column['len']
1717
             * seems appropriate.
1718
             */
1719
            $fieldsize = $column['len'];
1720
        }
1721
        return min(
1722
            max($fieldsize, $GLOBALS['cfg']['MinSizeForInputField']),
1723
            $GLOBALS['cfg']['MaxSizeForInputField']
1724
        );
1725
    }
1726
1727
    /**
1728
     * Get HTML for gis data types
1729
     *
1730
     * @return string an html snippet
1731
     */
1732
    private function getHtmlForGisDataTypes()
1733
    {
1734
        $edit_str = Util::getIcon('b_edit', __('Edit/Insert'));
1735
        return '<span class="open_gis_editor">'
1736
            . Util::linkOrButton(
1737
                '#',
1738
                $edit_str,
1739
                [],
1740
                '_blank'
1741
            )
1742
            . '</span>';
1743
    }
1744
1745
    /**
1746
     * get html for continue insertion form
1747
     *
1748
     * @param string $table              name of the table
1749
     * @param string $db                 name of the database
1750
     * @param array  $where_clause_array array of where clauses
1751
     * @param string $err_url            error url
1752
     *
1753
     * @return string                   an html snippet
1754
     */
1755
    public function getContinueInsertionForm(
1756
        $table,
1757
        $db,
1758
        array $where_clause_array,
1759
        $err_url
1760
    ) {
1761
        return $this->template->render('table/insert/continue_insertion_form', [
1762
            'db' => $db,
1763
            'table' => $table,
1764
            'where_clause_array' => $where_clause_array,
1765
            'err_url' => $err_url,
1766
            'goto' => $GLOBALS['goto'],
1767
            'sql_query' => isset($_POST['sql_query']) ? $_POST['sql_query'] : null,
1768
            'has_where_clause' => isset($_REQUEST['where_clause']),
1769
            'insert_rows_default' => $GLOBALS['cfg']['InsertRows'],
1770
        ]);
1771
    }
1772
1773
    /**
1774
     * Get action panel
1775
     *
1776
     * @param array|null $where_clause       where clause
1777
     * @param string     $after_insert       insert mode, e.g. new_insert, same_insert
1778
     * @param integer    $tabindex           tab index
1779
     * @param integer    $tabindex_for_value offset for the values tabindex
1780
     * @param boolean    $found_unique_key   boolean variable for unique key
1781
     *
1782
     * @return string an html snippet
1783
     */
1784
    public function getActionsPanel(
1785
        $where_clause,
1786
        $after_insert,
1787
        $tabindex,
1788
        $tabindex_for_value,
1789
        $found_unique_key
1790
    ) {
1791
        $html_output = '<fieldset id="actions_panel">'
1792
            . '<table cellpadding="5" cellspacing="0" class="tdblock width100">'
1793
            . '<tr>'
1794
            . '<td class="nowrap vmiddle">'
1795
            . $this->getSubmitTypeDropDown($where_clause, $tabindex, $tabindex_for_value)
1796
            . "\n";
1797
1798
        $html_output .= '</td>'
1799
            . '<td class="vmiddle">'
1800
            . '&nbsp;&nbsp;&nbsp;<strong>'
1801
            . __('and then') . '</strong>&nbsp;&nbsp;&nbsp;'
1802
            . '</td>'
1803
            . '<td class="nowrap vmiddle">'
1804
            . $this->getAfterInsertDropDown(
1805
                $where_clause,
1806
                $after_insert,
1807
                $found_unique_key
1808
            )
1809
            . '</td>'
1810
            . '</tr>';
1811
        $html_output .= '<tr>'
1812
            . $this->getSubmitAndResetButtonForActionsPanel($tabindex, $tabindex_for_value)
1813
            . '</tr>'
1814
            . '</table>'
1815
            . '</fieldset>';
1816
        return $html_output;
1817
    }
1818
1819
    /**
1820
     * Get a HTML drop down for submit types
1821
     *
1822
     * @param array|null $where_clause       where clause
1823
     * @param integer    $tabindex           tab index
1824
     * @param integer    $tabindex_for_value offset for the values tabindex
1825
     *
1826
     * @return string                       an html snippet
1827
     */
1828
    private function getSubmitTypeDropDown(
1829
        $where_clause,
1830
        $tabindex,
1831
        $tabindex_for_value
1832
    ) {
1833
        $html_output = '<select name="submit_type" class="control_at_footer" tabindex="'
1834
            . ($tabindex + $tabindex_for_value + 1) . '">';
1835
        if (isset($where_clause)) {
1836
            $html_output .= '<option value="save">' . __('Save') . '</option>';
1837
        }
1838
        $html_output .= '<option value="insert">'
1839
            . __('Insert as new row')
1840
            . '</option>'
1841
            . '<option value="insertignore">'
1842
            . __('Insert as new row and ignore errors')
1843
            . '</option>'
1844
            . '<option value="showinsert">'
1845
            . __('Show insert query')
1846
            . '</option>'
1847
            . '</select>';
1848
        return $html_output;
1849
    }
1850
1851
    /**
1852
     * Get HTML drop down for after insert
1853
     *
1854
     * @param array|null $where_clause     where clause
1855
     * @param string     $after_insert     insert mode, e.g. new_insert, same_insert
1856
     * @param boolean    $found_unique_key boolean variable for unique key
1857
     *
1858
     * @return string                   an html snippet
1859
     */
1860
    private function getAfterInsertDropDown($where_clause, $after_insert, $found_unique_key)
1861
    {
1862
        $html_output = '<select name="after_insert" class="control_at_footer">'
1863
            . '<option value="back" '
1864
            . ($after_insert == 'back' ? 'selected="selected"' : '') . '>'
1865
            . __('Go back to previous page') . '</option>'
1866
            . '<option value="new_insert" '
1867
            . ($after_insert == 'new_insert' ? 'selected="selected"' : '') . '>'
1868
            . __('Insert another new row') . '</option>';
1869
1870
        if (isset($where_clause)) {
1871
            $html_output .= '<option value="same_insert" '
1872
                . ($after_insert == 'same_insert' ? 'selected="selected"' : '') . '>'
1873
                . __('Go back to this page') . '</option>';
1874
1875
            // If we have just numeric primary key, we can also edit next
1876
            // in 2.8.2, we were looking for `field_name` = numeric_value
1877
            //if (preg_match('@^[\s]*`[^`]*` = [0-9]+@', $where_clause)) {
1878
            // in 2.9.0, we are looking for `table_name`.`field_name` = numeric_value
1879
            $is_numeric = false;
1880
            if (! is_array($where_clause)) {
1881
                $where_clause = [$where_clause];
1882
            }
1883
            for ($i = 0, $nb = count($where_clause); $i < $nb; $i++) {
1884
                // preg_match() returns 1 if there is a match
1885
                $is_numeric = (preg_match(
1886
                    '@^[\s]*`[^`]*`[\.]`[^`]*` = [0-9]+@',
1887
                    $where_clause[$i]
1888
                ) == 1);
1889
                if ($is_numeric === true) {
1890
                    break;
1891
                }
1892
            }
1893
            if ($found_unique_key && $is_numeric) {
1894
                $html_output .= '<option value="edit_next" '
1895
                    . ($after_insert == 'edit_next' ? 'selected="selected"' : '') . '>'
1896
                    . __('Edit next row') . '</option>';
1897
            }
1898
        }
1899
        $html_output .= '</select>';
1900
        return $html_output;
1901
    }
1902
1903
    /**
1904
     * get Submit button and Reset button for action panel
1905
     *
1906
     * @param integer $tabindex           tab index
1907
     * @param integer $tabindex_for_value offset for the values tabindex
1908
     *
1909
     * @return string an html snippet
1910
     */
1911
    private function getSubmitAndResetButtonForActionsPanel($tabindex, $tabindex_for_value)
1912
    {
1913
        return '<td>'
1914
        . Util::showHint(
1915
            __(
1916
                'Use TAB key to move from value to value,'
1917
                . ' or CTRL+arrows to move anywhere.'
1918
            )
1919
        )
1920
        . '</td>'
1921
        . '<td colspan="3" class="right vmiddle">'
1922
        . '<input type="button" class="preview_sql" value="' . __('Preview SQL') . '"'
1923
        . ' tabindex="' . ($tabindex + $tabindex_for_value + 6) . '" />'
1924
        . '<input type="reset" class="control_at_footer" value="' . __('Reset') . '"'
1925
        . ' tabindex="' . ($tabindex + $tabindex_for_value + 7) . '" />'
1926
        . '<input type="submit" class="control_at_footer" value="' . __('Go') . '"'
1927
        . ' tabindex="' . ($tabindex + $tabindex_for_value + 8) . '" id="buttonYes" />'
1928
        . '</td>';
1929
    }
1930
1931
    /**
1932
     * Get table head and table foot for insert row table
1933
     *
1934
     * @param array $url_params url parameters
1935
     *
1936
     * @return string           an html snippet
1937
     */
1938
    private function getHeadAndFootOfInsertRowTable(array $url_params)
1939
    {
1940
        $html_output = '<div class="responsivetable">'
1941
            . '<table class="insertRowTable topmargin">'
1942
            . '<thead>'
1943
            . '<tr>'
1944
            . '<th>' . __('Column') . '</th>';
1945
1946
        if ($GLOBALS['cfg']['ShowFieldTypesInDataEditView']) {
1947
            $html_output .= $this->showTypeOrFunction('type', $url_params, true);
1948
        }
1949
        if ($GLOBALS['cfg']['ShowFunctionFields']) {
1950
            $html_output .= $this->showTypeOrFunction('function', $url_params, true);
1951
        }
1952
1953
        $html_output .= '<th>' . __('Null') . '</th>'
1954
            . '<th class="fillPage">' . __('Value') . '</th>'
1955
            . '</tr>'
1956
            . '</thead>'
1957
            . ' <tfoot>'
1958
            . '<tr>'
1959
            . '<th colspan="5" class="tblFooters right">'
1960
            . '<input type="submit" value="' . __('Go') . '" />'
1961
            . '</th>'
1962
            . '</tr>'
1963
            . '</tfoot>';
1964
        return $html_output;
1965
    }
1966
1967
    /**
1968
     * Prepares the field value and retrieve special chars, backup field and data array
1969
     *
1970
     * @param array   $current_row          a row of the table
1971
     * @param array   $column               description of column in given table
1972
     * @param array   $extracted_columnspec associative array containing type,
1973
     *                                      spec_in_brackets and possibly
1974
     *                                      enum_set_values (another array)
1975
     * @param boolean $real_null_value      whether column value null or not null
1976
     * @param array   $gis_data_types       list of GIS data types
1977
     * @param string  $column_name_appendix string to append to column name in input
1978
     * @param bool    $as_is                use the data as is, used in repopulating
1979
     *
1980
     * @return array $real_null_value, $data, $special_chars, $backup_field,
1981
     *               $special_chars_encoded
1982
     */
1983
    private function getSpecialCharsAndBackupFieldForExistingRow(
1984
        array $current_row,
1985
        array $column,
1986
        array $extracted_columnspec,
1987
        $real_null_value,
1988
        array $gis_data_types,
1989
        $column_name_appendix,
1990
        $as_is
1991
    ) {
1992
        $special_chars_encoded = '';
1993
        $data = null;
1994
        // (we are editing)
1995
        if (!isset($current_row[$column['Field']])) {
1996
            $real_null_value = true;
1997
            $current_row[$column['Field']] = '';
1998
            $special_chars = '';
1999
            $data = $current_row[$column['Field']];
2000
        } elseif ($column['True_Type'] == 'bit') {
2001
            $special_chars = $as_is
2002
                ? $current_row[$column['Field']]
2003
                : Util::printableBitValue(
2004
                    (int) $current_row[$column['Field']],
2005
                    (int) $extracted_columnspec['spec_in_brackets']
2006
                );
2007
        } elseif ((substr($column['True_Type'], 0, 9) == 'timestamp'
2008
            || $column['True_Type'] == 'datetime'
2009
            || $column['True_Type'] == 'time')
2010
            && (mb_strpos($current_row[$column['Field']], ".") !== false)
2011
        ) {
2012
            $current_row[$column['Field']] = $as_is
2013
                ? $current_row[$column['Field']]
2014
                : Util::addMicroseconds(
2015
                    $current_row[$column['Field']]
2016
                );
2017
            $special_chars = htmlspecialchars($current_row[$column['Field']]);
2018
        } elseif (in_array($column['True_Type'], $gis_data_types)) {
2019
            // Convert gis data to Well Know Text format
2020
            $current_row[$column['Field']] = $as_is
2021
                ? $current_row[$column['Field']]
2022
                : Util::asWKT(
2023
                    $current_row[$column['Field']],
2024
                    true
2025
                );
2026
            $special_chars = htmlspecialchars($current_row[$column['Field']]);
2027
        } else {
2028
            // special binary "characters"
2029
            if ($column['is_binary']
2030
                || ($column['is_blob'] && $GLOBALS['cfg']['ProtectBinary'] !== 'all')
2031
            ) {
2032
                $current_row[$column['Field']] = $as_is
2033
                    ? $current_row[$column['Field']]
2034
                    : bin2hex(
2035
                        $current_row[$column['Field']]
2036
                    );
2037
            } // end if
2038
            $special_chars = htmlspecialchars($current_row[$column['Field']]);
2039
2040
            //We need to duplicate the first \n or otherwise we will lose
2041
            //the first newline entered in a VARCHAR or TEXT column
2042
            $special_chars_encoded
2043
                = Util::duplicateFirstNewline($special_chars);
2044
2045
            $data = $current_row[$column['Field']];
2046
        } // end if... else...
2047
2048
        //when copying row, it is useful to empty auto-increment column
2049
        // to prevent duplicate key error
2050
        if (isset($_REQUEST['default_action'])
2051
            && $_REQUEST['default_action'] === 'insert'
2052
        ) {
2053
            if ($column['Key'] === 'PRI'
2054
                && mb_strpos($column['Extra'], 'auto_increment') !== false
2055
            ) {
2056
                $data = $special_chars_encoded = $special_chars = null;
2057
            }
2058
        }
2059
        // If a timestamp field value is not included in an update
2060
        // statement MySQL auto-update it to the current timestamp;
2061
        // however, things have changed since MySQL 4.1, so
2062
        // it's better to set a fields_prev in this situation
2063
        $backup_field = '<input type="hidden" name="fields_prev'
2064
            . $column_name_appendix . '" value="'
2065
            . htmlspecialchars($current_row[$column['Field']]) . '" />';
2066
2067
        return [
2068
            $real_null_value,
2069
            $special_chars_encoded,
2070
            $special_chars,
2071
            $data,
2072
            $backup_field
2073
        ];
2074
    }
2075
2076
    /**
2077
     * display default values
2078
     *
2079
     * @param array   $column          description of column in given table
2080
     * @param boolean $real_null_value whether column value null or not null
2081
     *
2082
     * @return array $real_null_value, $data, $special_chars,
2083
     *               $backup_field, $special_chars_encoded
2084
     */
2085
    private function getSpecialCharsAndBackupFieldForInsertingMode(
2086
        array $column,
2087
        $real_null_value
2088
    ) {
2089
        if (! isset($column['Default'])) {
2090
            $column['Default']    = '';
2091
            $real_null_value          = true;
2092
            $data                     = '';
2093
        } else {
2094
            $data                     = $column['Default'];
2095
        }
2096
2097
        $trueType = $column['True_Type'];
2098
2099
        if ($trueType == 'bit') {
2100
            $special_chars = Util::convertBitDefaultValue(
2101
                $column['Default']
2102
            );
2103
        } elseif (substr($trueType, 0, 9) == 'timestamp'
2104
            || $trueType == 'datetime'
2105
            || $trueType == 'time'
2106
        ) {
2107
            $special_chars = Util::addMicroseconds($column['Default']);
2108
        } elseif ($trueType == 'binary' || $trueType == 'varbinary') {
2109
            $special_chars = bin2hex($column['Default']);
2110
        } else {
2111
            $special_chars = htmlspecialchars($column['Default']);
2112
        }
2113
        $backup_field = '';
2114
        $special_chars_encoded = Util::duplicateFirstNewline(
2115
            $special_chars
2116
        );
2117
        return [
2118
            $real_null_value, $data, $special_chars,
2119
            $backup_field, $special_chars_encoded
2120
        ];
2121
    }
2122
2123
    /**
2124
     * Prepares the update/insert of a row
2125
     *
2126
     * @return array $loop_array, $using_key, $is_insert, $is_insertignore
2127
     */
2128
    public function getParamsForUpdateOrInsert()
2129
    {
2130
        if (isset($_REQUEST['where_clause'])) {
2131
            // we were editing something => use the WHERE clause
2132
            $loop_array = is_array($_REQUEST['where_clause'])
2133
                ? $_REQUEST['where_clause']
2134
                : [$_REQUEST['where_clause']];
2135
            $using_key  = true;
2136
            $is_insert  = isset($_REQUEST['submit_type'])
2137
                          && ($_REQUEST['submit_type'] == 'insert'
2138
                          || $_REQUEST['submit_type'] == 'showinsert'
2139
                          || $_REQUEST['submit_type'] == 'insertignore');
2140
        } else {
2141
            // new row => use indexes
2142
            $loop_array = [];
2143
            if (! empty($_REQUEST['fields'])) {
2144
                foreach ($_REQUEST['fields']['multi_edit'] as $key => $dummy) {
2145
                    $loop_array[] = $key;
2146
                }
2147
            }
2148
            $using_key  = false;
2149
            $is_insert  = true;
2150
        }
2151
        $is_insertignore  = isset($_REQUEST['submit_type'])
2152
            && $_REQUEST['submit_type'] == 'insertignore';
2153
        return [$loop_array, $using_key, $is_insert, $is_insertignore];
2154
    }
2155
2156
    /**
2157
     * Check wether insert row mode and if so include tbl_changen script and set
2158
     * global variables.
2159
     *
2160
     * @return void
2161
     */
2162
    public function isInsertRow()
2163
    {
2164
        if (isset($_REQUEST['insert_rows'])
2165
            && is_numeric($_REQUEST['insert_rows'])
2166
            && $_REQUEST['insert_rows'] != $GLOBALS['cfg']['InsertRows']
2167
        ) {
2168
            $GLOBALS['cfg']['InsertRows'] = $_REQUEST['insert_rows'];
2169
            $response = Response::getInstance();
2170
            $header = $response->getHeader();
2171
            $scripts = $header->getScripts();
2172
            $scripts->addFile('vendor/jquery/additional-methods.js');
2173
            $scripts->addFile('tbl_change.js');
2174
            if (!defined('TESTSUITE')) {
2175
                include 'tbl_change.php';
2176
                exit;
2177
            }
2178
        }
2179
    }
2180
2181
    /**
2182
     * set $_SESSION for edit_next
2183
     *
2184
     * @param string $one_where_clause one where clause from where clauses array
2185
     *
2186
     * @return void
2187
     */
2188
    public function setSessionForEditNext($one_where_clause)
2189
    {
2190
        $local_query = 'SELECT * FROM ' . Util::backquote($GLOBALS['db'])
2191
            . '.' . Util::backquote($GLOBALS['table']) . ' WHERE '
2192
            . str_replace('` =', '` >', $one_where_clause) . ' LIMIT 1;';
2193
2194
        $res = $this->dbi->query($local_query);
2195
        $row = $this->dbi->fetchRow($res);
2196
        $meta = $this->dbi->getFieldsMeta($res);
2197
        // must find a unique condition based on unique key,
2198
        // not a combination of all fields
2199
        list($unique_condition, $clause_is_unique)
2200
            = Util::getUniqueCondition(
2201
                $res, // handle
2202
                count($meta), // fields_cnt
2203
                $meta, // fields_meta
2204
                $row, // row
2205
                true, // force_unique
2206
                false, // restrict_to_table
2207
                null // analyzed_sql_results
2208
            );
2209
        if (! empty($unique_condition)) {
2210
            $_SESSION['edit_next'] = $unique_condition;
2211
        }
2212
        unset($unique_condition, $clause_is_unique);
2213
    }
2214
2215
    /**
2216
     * set $goto_include variable for different cases and retrieve like,
2217
     * if $GLOBALS['goto'] empty, if $goto_include previously not defined
2218
     * and new_insert, same_insert, edit_next
2219
     *
2220
     * @param string $goto_include store some script for include, otherwise it is
2221
     *                             boolean false
2222
     *
2223
     * @return string
2224
     */
2225
    public function getGotoInclude($goto_include)
2226
    {
2227
        $valid_options = ['new_insert', 'same_insert', 'edit_next'];
2228
        if (isset($_REQUEST['after_insert'])
2229
            && in_array($_REQUEST['after_insert'], $valid_options)
2230
        ) {
2231
            $goto_include = 'tbl_change.php';
2232
        } elseif (! empty($GLOBALS['goto'])) {
2233
            if (! preg_match('@^[a-z_]+\.php$@', $GLOBALS['goto'])) {
2234
                // this should NOT happen
2235
                //$GLOBALS['goto'] = false;
2236
                $goto_include = false;
2237
            } else {
2238
                $goto_include = $GLOBALS['goto'];
2239
            }
2240
            if ($GLOBALS['goto'] == 'db_sql.php' && strlen($GLOBALS['table']) > 0) {
2241
                $GLOBALS['table'] = '';
2242
            }
2243
        }
2244
        if (! $goto_include) {
2245
            if (strlen($GLOBALS['table']) === 0) {
2246
                $goto_include = 'db_sql.php';
2247
            } else {
2248
                $goto_include = 'tbl_sql.php';
2249
            }
2250
        }
2251
        return $goto_include;
2252
    }
2253
2254
    /**
2255
     * Defines the url to return in case of failure of the query
2256
     *
2257
     * @param array $url_params url parameters
2258
     *
2259
     * @return string           error url for query failure
2260
     */
2261
    public function getErrorUrl(array $url_params)
2262
    {
2263
        if (isset($_REQUEST['err_url'])) {
2264
            return $_REQUEST['err_url'];
2265
        }
2266
2267
        return 'tbl_change.php' . Url::getCommon($url_params);
2268
    }
2269
2270
    /**
2271
     * Builds the sql query
2272
     *
2273
     * @param boolean $is_insertignore $_REQUEST['submit_type'] == 'insertignore'
2274
     * @param array   $query_fields    column names array
2275
     * @param array   $value_sets      array of query values
2276
     *
2277
     * @return array of query
2278
     */
2279
    public function buildSqlQuery($is_insertignore, array $query_fields, array $value_sets)
2280
    {
2281
        if ($is_insertignore) {
2282
            $insert_command = 'INSERT IGNORE ';
2283
        } else {
2284
            $insert_command = 'INSERT ';
2285
        }
2286
        $query = [
2287
            $insert_command . 'INTO '
2288
            . Util::backquote($GLOBALS['table'])
2289
            . ' (' . implode(', ', $query_fields) . ') VALUES ('
2290
            . implode('), (', $value_sets) . ')'
2291
        ];
2292
        unset($insert_command, $query_fields);
2293
        return $query;
2294
    }
2295
2296
    /**
2297
     * Executes the sql query and get the result, then move back to the calling page
2298
     *
2299
     * @param array $url_params url parameters array
2300
     * @param array $query      built query from buildSqlQuery()
2301
     *
2302
     * @return array $url_params, $total_affected_rows, $last_messages
2303
     *               $warning_messages, $error_messages, $return_to_sql_query
2304
     */
2305
    public function executeSqlQuery(array $url_params, array $query)
2306
    {
2307
        $return_to_sql_query = '';
2308
        if (! empty($GLOBALS['sql_query'])) {
2309
            $url_params['sql_query'] = $GLOBALS['sql_query'];
2310
            $return_to_sql_query = $GLOBALS['sql_query'];
2311
        }
2312
        $GLOBALS['sql_query'] = implode('; ', $query) . ';';
2313
        // to ensure that the query is displayed in case of
2314
        // "insert as new row" and then "insert another new row"
2315
        $GLOBALS['display_query'] = $GLOBALS['sql_query'];
2316
2317
        $total_affected_rows = 0;
2318
        $last_messages = [];
2319
        $warning_messages = [];
2320
        $error_messages = [];
2321
2322
        foreach ($query as $single_query) {
2323
            if ($_REQUEST['submit_type'] == 'showinsert') {
2324
                $last_messages[] = Message::notice(__('Showing SQL query'));
2325
                continue;
2326
            }
2327
            if ($GLOBALS['cfg']['IgnoreMultiSubmitErrors']) {
2328
                $result = $this->dbi->tryQuery($single_query);
2329
            } else {
2330
                $result = $this->dbi->query($single_query);
2331
            }
2332
            if (! $result) {
2333
                $error_messages[] = $this->dbi->getError();
2334
            } else {
2335
                // The next line contains a real assignment, it's not a typo
2336
                if ($tmp = @$this->dbi->affectedRows()) {
2337
                    $total_affected_rows += $tmp;
2338
                }
2339
                unset($tmp);
2340
2341
                $insert_id = $this->dbi->insertId();
2342
                if ($insert_id != 0) {
2343
                    // insert_id is id of FIRST record inserted in one insert, so if we
2344
                    // inserted multiple rows, we had to increment this
2345
2346
                    if ($total_affected_rows > 0) {
2347
                        $insert_id = $insert_id + $total_affected_rows - 1;
2348
                    }
2349
                    $last_message = Message::notice(__('Inserted row id: %1$d'));
2350
                    $last_message->addParam($insert_id);
2351
                    $last_messages[] = $last_message;
2352
                }
2353
                $this->dbi->freeResult($result);
2354
            }
2355
            $warning_messages = $this->getWarningMessages();
2356
        }
2357
        return [
2358
            $url_params,
2359
            $total_affected_rows,
2360
            $last_messages,
2361
            $warning_messages,
2362
            $error_messages,
2363
            $return_to_sql_query
2364
        ];
2365
    }
2366
2367
    /**
2368
     * get the warning messages array
2369
     *
2370
     * @return array
2371
     */
2372
    private function getWarningMessages()
2373
    {
2374
        $warning_essages = [];
2375
        foreach ($this->dbi->getWarnings() as $warning) {
2376
            $warning_essages[] = Message::sanitize(
2377
                $warning['Level'] . ': #' . $warning['Code'] . ' ' . $warning['Message']
2378
            );
2379
        }
2380
        return $warning_essages;
2381
    }
2382
2383
    /**
2384
     * Column to display from the foreign table?
2385
     *
2386
     * @param string $where_comparison string that contain relation field value
2387
     * @param array  $map              all Relations to foreign tables for a given
2388
     *                                 table or optionally a given column in a table
2389
     * @param string $relation_field   relation field
2390
     *
2391
     * @return string display value from the foreign table
2392
     */
2393
    public function getDisplayValueForForeignTableColumn(
2394
        $where_comparison,
2395
        array $map,
2396
        $relation_field
2397
    ) {
2398
        $foreigner = $this->relation->searchColumnInForeigners($map, $relation_field);
2399
        $display_field = $this->relation->getDisplayField(
2400
            $foreigner['foreign_db'],
2401
            $foreigner['foreign_table']
2402
        );
2403
        // Field to display from the foreign table?
2404
        if (! is_null($display_field) && strlen($display_field) > 0) {
2405
            $dispsql = 'SELECT ' . Util::backquote($display_field)
2406
                . ' FROM ' . Util::backquote($foreigner['foreign_db'])
2407
                . '.' . Util::backquote($foreigner['foreign_table'])
2408
                . ' WHERE ' . Util::backquote($foreigner['foreign_field'])
2409
                . $where_comparison;
2410
            $dispresult = $this->dbi->tryQuery(
2411
                $dispsql,
2412
                DatabaseInterface::CONNECT_USER,
2413
                DatabaseInterface::QUERY_STORE
2414
            );
2415
            if ($dispresult && $this->dbi->numRows($dispresult) > 0) {
2416
                list($dispval) = $this->dbi->fetchRow($dispresult);
2417
            } else {
2418
                $dispval = '';
2419
            }
2420
            if ($dispresult) {
2421
                $this->dbi->freeResult($dispresult);
2422
            }
2423
            return $dispval;
2424
        }
2425
        return '';
2426
    }
2427
2428
    /**
2429
     * Display option in the cell according to user choices
2430
     *
2431
     * @param array  $map                  all Relations to foreign tables for a given
2432
     *                                     table or optionally a given column in a table
2433
     * @param string $relation_field       relation field
2434
     * @param string $where_comparison     string that contain relation field value
2435
     * @param string $dispval              display value from the foreign table
2436
     * @param string $relation_field_value relation field value
2437
     *
2438
     * @return string HTML <a> tag
2439
     */
2440
    public function getLinkForRelationalDisplayField(
2441
        array $map,
2442
        $relation_field,
2443
        $where_comparison,
2444
        $dispval,
2445
        $relation_field_value
2446
    ) {
2447
        $foreigner = $this->relation->searchColumnInForeigners($map, $relation_field);
2448
        if ('K' == $_SESSION['tmpval']['relational_display']) {
2449
            // user chose "relational key" in the display options, so
2450
            // the title contains the display field
2451
            $title = (! empty($dispval))
2452
                ? ' title="' . htmlspecialchars($dispval) . '"'
2453
                : '';
2454
        } else {
2455
            $title = ' title="' . htmlspecialchars($relation_field_value) . '"';
2456
        }
2457
        $_url_params = [
2458
            'db'    => $foreigner['foreign_db'],
2459
            'table' => $foreigner['foreign_table'],
2460
            'pos'   => '0',
2461
            'sql_query' => 'SELECT * FROM '
2462
                . Util::backquote($foreigner['foreign_db'])
2463
                . '.' . Util::backquote($foreigner['foreign_table'])
2464
                . ' WHERE ' . Util::backquote($foreigner['foreign_field'])
2465
                . $where_comparison
2466
        ];
2467
        $output = '<a href="sql.php'
2468
            . Url::getCommon($_url_params) . '"' . $title . '>';
2469
2470
        if ('D' == $_SESSION['tmpval']['relational_display']) {
2471
            // user chose "relational display field" in the
2472
            // display options, so show display field in the cell
2473
            $output .= (!empty($dispval)) ? htmlspecialchars($dispval) : '';
2474
        } else {
2475
            // otherwise display data in the cell
2476
            $output .= htmlspecialchars($relation_field_value);
2477
        }
2478
        $output .= '</a>';
2479
        return $output;
2480
    }
2481
2482
    /**
2483
     * Transform edited values
2484
     *
2485
     * @param string $db             db name
2486
     * @param string $table          table name
2487
     * @param array  $transformation mimetypes for all columns of a table
2488
     *                               [field_name][field_key]
2489
     * @param array  &$edited_values transform columns list and new values
2490
     * @param string $file           file containing the transformation plugin
2491
     * @param string $column_name    column name
2492
     * @param array  $extra_data     extra data array
2493
     * @param string $type           the type of transformation
2494
     *
2495
     * @return array
2496
     */
2497
    public function transformEditedValues(
2498
        $db,
2499
        $table,
2500
        array $transformation,
2501
        array &$edited_values,
2502
        $file,
2503
        $column_name,
2504
        array $extra_data,
2505
        $type
2506
    ) {
2507
        $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
2508
        if (is_file($include_file)) {
2509
            include_once $include_file;
2510
            $_url_params = [
2511
                'db'            => $db,
2512
                'table'         => $table,
2513
                'where_clause'  => $_REQUEST['where_clause'],
2514
                'transform_key' => $column_name
2515
            ];
2516
            $transform_options = $this->transformations->getOptions(
2517
                isset($transformation[$type . '_options'])
2518
                ? $transformation[$type . '_options']
2519
                : ''
2520
            );
2521
            $transform_options['wrapper_link'] = Url::getCommon($_url_params);
2522
            $class_name = $this->transformations->getClassName($include_file);
2523
            /** @var TransformationsPlugin $transformation_plugin */
2524
            $transformation_plugin = new $class_name();
2525
2526
            foreach ($edited_values as $cell_index => $curr_cell_edited_values) {
2527
                if (isset($curr_cell_edited_values[$column_name])) {
2528
                    $edited_values[$cell_index][$column_name]
2529
                        = $extra_data['transformations'][$cell_index]
2530
                            = $transformation_plugin->applyTransformation(
2531
                                $curr_cell_edited_values[$column_name],
2532
                                $transform_options
2533
                            );
2534
                }
2535
            }   // end of loop for each transformation cell
2536
        }
2537
        return $extra_data;
2538
    }
2539
2540
    /**
2541
     * Get current value in multi edit mode
2542
     *
2543
     * @param array  $multi_edit_funcs        multiple edit functions array
2544
     * @param array  $multi_edit_salt         multiple edit array with encryption salt
2545
     * @param array  $gis_from_text_functions array that contains gis from text functions
2546
     * @param string $current_value           current value in the column
2547
     * @param array  $gis_from_wkb_functions  initially $val is $multi_edit_columns[$key]
2548
     * @param array  $func_optional_param     array('RAND','UNIX_TIMESTAMP')
2549
     * @param array  $func_no_param           array of set of string
2550
     * @param string $key                     an md5 of the column name
2551
     *
2552
     * @return array
2553
     */
2554
    public function getCurrentValueAsAnArrayForMultipleEdit(
2555
        $multi_edit_funcs,
2556
        $multi_edit_salt,
2557
        $gis_from_text_functions,
2558
        $current_value,
2559
        $gis_from_wkb_functions,
2560
        $func_optional_param,
2561
        $func_no_param,
2562
        $key
2563
    ) {
2564
        if (empty($multi_edit_funcs[$key])) {
2565
            return $current_value;
2566
        } elseif ('UUID' === $multi_edit_funcs[$key]) {
2567
            /* This way user will know what UUID new row has */
2568
            $uuid = $this->dbi->fetchValue('SELECT UUID()');
2569
            return "'" . $uuid . "'";
2570
        } elseif ((in_array($multi_edit_funcs[$key], $gis_from_text_functions)
2571
            && substr($current_value, 0, 3) == "'''")
2572
            || in_array($multi_edit_funcs[$key], $gis_from_wkb_functions)
2573
        ) {
2574
            // Remove enclosing apostrophes
2575
            $current_value = mb_substr($current_value, 1, -1);
2576
            // Remove escaping apostrophes
2577
            $current_value = str_replace("''", "'", $current_value);
2578
            return $multi_edit_funcs[$key] . '(' . $current_value . ')';
2579
        } elseif (! in_array($multi_edit_funcs[$key], $func_no_param)
2580
            || ($current_value != "''"
2581
            && in_array($multi_edit_funcs[$key], $func_optional_param))
2582
        ) {
2583
            if ((isset($multi_edit_salt[$key])
2584
                && ($multi_edit_funcs[$key] == "AES_ENCRYPT"
2585
                || $multi_edit_funcs[$key] == "AES_DECRYPT"))
2586
                || (! empty($multi_edit_salt[$key])
2587
                && ($multi_edit_funcs[$key] == "DES_ENCRYPT"
2588
                || $multi_edit_funcs[$key] == "DES_DECRYPT"
2589
                || $multi_edit_funcs[$key] == "ENCRYPT"))
2590
            ) {
2591
                return $multi_edit_funcs[$key] . '(' . $current_value . ",'"
2592
                    . $this->dbi->escapeString($multi_edit_salt[$key]) . "')";
2593
            }
2594
2595
            return $multi_edit_funcs[$key] . '(' . $current_value . ')';
0 ignored issues
show
Bug Best Practice introduced by
The expression return $multi_edit_funcs... . $current_value . ')' returns the type string which is incompatible with the documented return type array.
Loading history...
2596
        }
2597
2598
        return $multi_edit_funcs[$key] . '()';
0 ignored issues
show
Bug Best Practice introduced by
The expression return $multi_edit_funcs[$key] . '()' returns the type string which is incompatible with the documented return type array.
Loading history...
2599
    }
2600
2601
    /**
2602
     * Get query values array and query fields array for insert and update in multi edit
2603
     *
2604
     * @param array   $multi_edit_columns_name      multiple edit columns name array
2605
     * @param array   $multi_edit_columns_null      multiple edit columns null array
2606
     * @param string  $current_value                current value in the column in loop
2607
     * @param array   $multi_edit_columns_prev      multiple edit previous columns array
2608
     * @param array   $multi_edit_funcs             multiple edit functions array
2609
     * @param boolean $is_insert                    boolean value whether insert or not
2610
     * @param array   $query_values                 SET part of the sql query
2611
     * @param array   $query_fields                 array of query fields
2612
     * @param string  $current_value_as_an_array    current value in the column
2613
     *                                              as an array
2614
     * @param array   $value_sets                   array of valu sets
2615
     * @param string  $key                          an md5 of the column name
2616
     * @param array   $multi_edit_columns_null_prev array of multiple edit columns
2617
     *                                              null previous
2618
     *
2619
     * @return array ($query_values, $query_fields)
2620
     */
2621
    public function getQueryValuesForInsertAndUpdateInMultipleEdit(
2622
        $multi_edit_columns_name,
2623
        $multi_edit_columns_null,
2624
        $current_value,
2625
        $multi_edit_columns_prev,
2626
        $multi_edit_funcs,
2627
        $is_insert,
2628
        $query_values,
2629
        $query_fields,
2630
        $current_value_as_an_array,
2631
        $value_sets,
2632
        $key,
2633
        $multi_edit_columns_null_prev
2634
    ) {
2635
        //  i n s e r t
2636
        if ($is_insert) {
2637
            // no need to add column into the valuelist
2638
            if (strlen($current_value_as_an_array) > 0) {
2639
                $query_values[] = $current_value_as_an_array;
2640
                // first inserted row so prepare the list of fields
2641
                if (empty($value_sets)) {
2642
                    $query_fields[] = Util::backquote(
2643
                        $multi_edit_columns_name[$key]
2644
                    );
2645
                }
2646
            }
2647
        } elseif (! empty($multi_edit_columns_null_prev[$key])
2648
            && ! isset($multi_edit_columns_null[$key])
2649
        ) {
2650
            //  u p d a t e
2651
2652
            // field had the null checkbox before the update
2653
            // field no longer has the null checkbox
2654
            $query_values[]
2655
                = Util::backquote($multi_edit_columns_name[$key])
2656
                . ' = ' . $current_value_as_an_array;
2657
        } elseif (empty($multi_edit_funcs[$key])
2658
            && isset($multi_edit_columns_prev[$key])
2659
            && (("'" . $this->dbi->escapeString($multi_edit_columns_prev[$key]) . "'" === $current_value)
2660
            || ('0x' . $multi_edit_columns_prev[$key] === $current_value))
2661
        ) {
2662
            // No change for this column and no MySQL function is used -> next column
2663
        } elseif (! empty($current_value)) {
2664
            // avoid setting a field to NULL when it's already NULL
2665
            // (field had the null checkbox before the update
2666
            //  field still has the null checkbox)
2667
            if (empty($multi_edit_columns_null_prev[$key])
2668
                || empty($multi_edit_columns_null[$key])
2669
            ) {
2670
                 $query_values[]
2671
                     = Util::backquote($multi_edit_columns_name[$key])
2672
                    . ' = ' . $current_value_as_an_array;
2673
            }
2674
        }
2675
        return [$query_values, $query_fields];
2676
    }
2677
2678
    /**
2679
     * Get the current column value in the form for different data types
2680
     *
2681
     * @param string|false $possibly_uploaded_val        uploaded file content
2682
     * @param string       $key                          an md5 of the column name
2683
     * @param array|null   $multi_edit_columns_type      array of multi edit column types
2684
     * @param string       $current_value                current column value in the form
2685
     * @param array|null   $multi_edit_auto_increment    multi edit auto increment
2686
     * @param integer      $rownumber                    index of where clause array
2687
     * @param array        $multi_edit_columns_name      multi edit column names array
2688
     * @param array        $multi_edit_columns_null      multi edit columns null array
2689
     * @param array        $multi_edit_columns_null_prev multi edit columns previous null
2690
     * @param boolean      $is_insert                    whether insert or not
2691
     * @param boolean      $using_key                    whether editing or new row
2692
     * @param string       $where_clause                 where clause
2693
     * @param string       $table                        table name
2694
     * @param array        $multi_edit_funcs             multiple edit functions array
2695
     *
2696
     * @return string  current column value in the form
2697
     */
2698
    public function getCurrentValueForDifferentTypes(
2699
        $possibly_uploaded_val,
2700
        $key,
2701
        ?array $multi_edit_columns_type,
2702
        $current_value,
2703
        ?array $multi_edit_auto_increment,
2704
        $rownumber,
2705
        $multi_edit_columns_name,
2706
        $multi_edit_columns_null,
2707
        $multi_edit_columns_null_prev,
2708
        $is_insert,
2709
        $using_key,
2710
        $where_clause,
2711
        $table,
2712
        $multi_edit_funcs
2713
    ) {
2714
        // Fetch the current values of a row to use in case we have a protected field
2715
        if ($is_insert
2716
            && $using_key && isset($multi_edit_columns_type)
2717
            && is_array($multi_edit_columns_type) && !empty($where_clause)
2718
        ) {
2719
            $protected_row = $this->dbi->fetchSingleRow(
2720
                'SELECT * FROM ' . Util::backquote($table)
2721
                . ' WHERE ' . $where_clause . ';'
2722
            );
2723
        }
2724
2725
        if (false !== $possibly_uploaded_val) {
2726
            $current_value = $possibly_uploaded_val;
2727
        } elseif (! empty($multi_edit_funcs[$key])) {
2728
            $current_value = "'" . $this->dbi->escapeString($current_value)
2729
                . "'";
2730
        } else {
2731
            // c o l u m n    v a l u e    i n    t h e    f o r m
2732
            if (isset($multi_edit_columns_type[$key])) {
2733
                $type = $multi_edit_columns_type[$key];
2734
            } else {
2735
                $type = '';
2736
            }
2737
2738
            if ($type != 'protected' && $type != 'set' && strlen($current_value) === 0) {
2739
                // best way to avoid problems in strict mode
2740
                // (works also in non-strict mode)
2741
                if (isset($multi_edit_auto_increment)
2742
                    && isset($multi_edit_auto_increment[$key])
2743
                ) {
2744
                    $current_value = 'NULL';
2745
                } else {
2746
                    $current_value = "''";
2747
                }
2748
            } elseif ($type == 'set') {
2749
                if (! empty($_REQUEST['fields']['multi_edit'][$rownumber][$key])) {
2750
                    $current_value = implode(
2751
                        ',',
2752
                        $_REQUEST['fields']['multi_edit'][$rownumber][$key]
2753
                    );
2754
                    $current_value = "'"
2755
                        . $this->dbi->escapeString($current_value) . "'";
2756
                } else {
2757
                     $current_value = "''";
2758
                }
2759
            } elseif ($type == 'protected') {
2760
                // here we are in protected mode (asked in the config)
2761
                // so tbl_change has put this special value in the
2762
                // columns array, so we do not change the column value
2763
                // but we can still handle column upload
2764
2765
                // when in UPDATE mode, do not alter field's contents. When in INSERT
2766
                // mode, insert empty field because no values were submitted.
2767
                // If protected blobs where set, insert original fields content.
2768
                if (! empty($protected_row[$multi_edit_columns_name[$key]])) {
2769
                    $current_value = '0x'
2770
                        . bin2hex($protected_row[$multi_edit_columns_name[$key]]);
2771
                } else {
2772
                    $current_value = '';
2773
                }
2774
            } elseif ($type === 'hex') {
2775
                if (substr($current_value, 0, 2) != '0x') {
2776
                    $current_value = '0x' . $current_value;
2777
                }
2778
            } elseif ($type == 'bit') {
2779
                $current_value = preg_replace('/[^01]/', '0', $current_value);
2780
                $current_value = "b'" . $this->dbi->escapeString($current_value)
2781
                    . "'";
2782
            } elseif (! ($type == 'datetime' || $type == 'timestamp')
2783
                || ($current_value != 'CURRENT_TIMESTAMP'
2784
                    && $current_value != 'current_timestamp()')
2785
            ) {
2786
                $current_value = "'" . $this->dbi->escapeString($current_value)
2787
                    . "'";
2788
            }
2789
2790
            // Was the Null checkbox checked for this field?
2791
            // (if there is a value, we ignore the Null checkbox: this could
2792
            // be possible if Javascript is disabled in the browser)
2793
            if (! empty($multi_edit_columns_null[$key])
2794
                && ($current_value == "''" || $current_value == '')
2795
            ) {
2796
                $current_value = 'NULL';
2797
            }
2798
2799
            // The Null checkbox was unchecked for this field
2800
            if (empty($current_value)
2801
                && ! empty($multi_edit_columns_null_prev[$key])
2802
                && ! isset($multi_edit_columns_null[$key])
2803
            ) {
2804
                $current_value = "''";
2805
            }
2806
        }  // end else (column value in the form)
2807
        return $current_value;
2808
    }
2809
2810
    /**
2811
     * Check whether inline edited value can be truncated or not,
2812
     * and add additional parameters for extra_data array  if needed
2813
     *
2814
     * @param string $db          Database name
2815
     * @param string $table       Table name
2816
     * @param string $column_name Column name
2817
     * @param array  &$extra_data Extra data for ajax response
2818
     *
2819
     * @return void
2820
     */
2821
    public function verifyWhetherValueCanBeTruncatedAndAppendExtraData(
2822
        $db,
2823
        $table,
2824
        $column_name,
2825
        array &$extra_data
2826
    ) {
2827
        $extra_data['isNeedToRecheck'] = false;
2828
2829
        $sql_for_real_value = 'SELECT ' . Util::backquote($table) . '.'
2830
            . Util::backquote($column_name)
2831
            . ' FROM ' . Util::backquote($db) . '.'
2832
            . Util::backquote($table)
2833
            . ' WHERE ' . $_REQUEST['where_clause'][0];
2834
2835
        $result = $this->dbi->tryQuery($sql_for_real_value);
2836
        $fields_meta = $this->dbi->getFieldsMeta($result);
2837
        $meta = $fields_meta[0];
2838
        if ($row = $this->dbi->fetchRow($result)) {
2839
            $new_value = $row[0];
2840
            if ((substr($meta->type, 0, 9) == 'timestamp')
2841
                || ($meta->type == 'datetime')
2842
                || ($meta->type == 'time')
2843
            ) {
2844
                $new_value = Util::addMicroseconds($new_value);
2845
            } elseif (mb_strpos($meta->flags, 'binary') !== false) {
2846
                $new_value = '0x' . bin2hex($new_value);
2847
            }
2848
            $extra_data['isNeedToRecheck'] = true;
2849
            $extra_data['truncatableFieldValue'] = $new_value;
2850
        }
2851
        $this->dbi->freeResult($result);
2852
    }
2853
2854
    /**
2855
     * Function to get the columns of a table
2856
     *
2857
     * @param string $db    current db
2858
     * @param string $table current table
2859
     *
2860
     * @return array
2861
     */
2862
    public function getTableColumns($db, $table)
2863
    {
2864
        $this->dbi->selectDb($db);
2865
        return array_values($this->dbi->getColumns($db, $table, null, true));
2866
    }
2867
2868
    /**
2869
     * Function to determine Insert/Edit rows
2870
     *
2871
     * @param string $where_clause where clause
2872
     * @param string $db           current database
2873
     * @param string $table        current table
2874
     *
2875
     * @return mixed
2876
     */
2877
    public function determineInsertOrEdit($where_clause, $db, $table)
2878
    {
2879
        if (isset($_REQUEST['where_clause'])) {
2880
            $where_clause = $_REQUEST['where_clause'];
2881
        }
2882
        if (isset($_SESSION['edit_next'])) {
2883
            $where_clause = $_SESSION['edit_next'];
2884
            unset($_SESSION['edit_next']);
2885
            $after_insert = 'edit_next';
2886
        }
2887
        if (isset($_REQUEST['ShowFunctionFields'])) {
2888
            $GLOBALS['cfg']['ShowFunctionFields'] = $_REQUEST['ShowFunctionFields'];
2889
        }
2890
        if (isset($_REQUEST['ShowFieldTypesInDataEditView'])) {
2891
            $GLOBALS['cfg']['ShowFieldTypesInDataEditView']
2892
                = $_REQUEST['ShowFieldTypesInDataEditView'];
2893
        }
2894
        if (isset($_REQUEST['after_insert'])) {
2895
            $after_insert = $_REQUEST['after_insert'];
2896
        }
2897
2898
        if (isset($where_clause)) {
2899
            // we are editing
2900
            $insert_mode = false;
2901
            $where_clause_array = $this->getWhereClauseArray($where_clause);
2902
            list($where_clauses, $result, $rows, $found_unique_key)
2903
                = $this->analyzeWhereClauses(
2904
                    $where_clause_array,
2905
                    $table,
2906
                    $db
2907
                );
2908
        } else {
2909
            // we are inserting
2910
            $insert_mode = true;
2911
            $where_clause = null;
2912
            list($result, $rows) = $this->loadFirstRow($table, $db);
2913
            $where_clauses = null;
2914
            $where_clause_array = [];
2915
            $found_unique_key = false;
2916
        }
2917
2918
        // Copying a row - fetched data will be inserted as a new row,
2919
        // therefore the where clause is needless.
2920
        if (isset($_REQUEST['default_action'])
2921
            && $_REQUEST['default_action'] === 'insert'
2922
        ) {
2923
            $where_clause = $where_clauses = null;
2924
        }
2925
2926
        return [
2927
            $insert_mode, $where_clause, $where_clause_array, $where_clauses,
2928
            $result, $rows, $found_unique_key,
2929
            isset($after_insert) ? $after_insert : null
2930
        ];
2931
    }
2932
2933
    /**
2934
     * Function to get comments for the table columns
2935
     *
2936
     * @param string $db    current database
2937
     * @param string $table current table
2938
     *
2939
     * @return array comments for columns
2940
     */
2941
    public function getCommentsMap($db, $table)
2942
    {
2943
        $comments_map = [];
2944
2945
        if ($GLOBALS['cfg']['ShowPropertyComments']) {
2946
            $comments_map = $this->relation->getComments($db, $table);
2947
        }
2948
2949
        return $comments_map;
2950
    }
2951
2952
    /**
2953
     * Function to get URL parameters
2954
     *
2955
     * @param string $db    current database
2956
     * @param string $table current table
2957
     *
2958
     * @return array url parameters
2959
     */
2960
    public function getUrlParameters($db, $table)
2961
    {
2962
        /**
2963
         * @todo check if we could replace by "db_|tbl_" - please clarify!?
2964
         */
2965
        $url_params = [
2966
            'db' => $db,
2967
            'sql_query' => $_POST['sql_query']
2968
        ];
2969
2970
        if (preg_match('@^tbl_@', $GLOBALS['goto'])) {
2971
            $url_params['table'] = $table;
2972
        }
2973
2974
        return $url_params;
2975
    }
2976
2977
    /**
2978
     * Function to get html for the gis editor div
2979
     *
2980
     * @return string
2981
     */
2982
    public function getHtmlForGisEditor()
2983
    {
2984
        return '<div id="gis_editor"></div>'
2985
            . '<div id="popup_background"></div>'
2986
            . '<br />';
2987
    }
2988
2989
    /**
2990
     * Function to get html for the ignore option in insert mode
2991
     *
2992
     * @param int  $row_id  row id
2993
     * @param bool $checked ignore option is checked or not
2994
     *
2995
     * @return string
2996
     */
2997
    public function getHtmlForIgnoreOption($row_id, $checked = true)
2998
    {
2999
        return '<input type="checkbox"'
3000
                . ($checked ? ' checked="checked"' : '')
3001
                . ' name="insert_ignore_' . $row_id . '"'
3002
                . ' id="insert_ignore_' . $row_id . '" />'
3003
                . '<label for="insert_ignore_' . $row_id . '">'
3004
                . __('Ignore')
3005
                . '</label><br />' . "\n";
3006
    }
3007
3008
    /**
3009
     * Function to get html for the function option
3010
     *
3011
     * @param array  $column               column
3012
     * @param string $column_name_appendix column name appendix
3013
     *
3014
     * @return String
3015
     */
3016
    private function getHtmlForFunctionOption(array $column, $column_name_appendix)
3017
    {
3018
        return '<tr class="noclick">'
3019
            . '<td '
3020
            . 'class="center">'
3021
            . $column['Field_title']
3022
            . '<input type="hidden" name="fields_name' . $column_name_appendix
3023
            . '" value="' . $column['Field_html'] . '"/>'
3024
            . '</td>';
3025
    }
3026
3027
    /**
3028
     * Function to get html for the column type
3029
     *
3030
     * @param array $column column
3031
     *
3032
     * @return string
3033
     */
3034
    private function getHtmlForInsertEditColumnType(array $column)
3035
    {
3036
        return '<td class="center' . $column['wrap'] . '">'
3037
            . '<span class="column_type" dir="ltr">' . $column['pma_type'] . '</span>'
3038
            . '</td>';
3039
    }
3040
3041
    /**
3042
     * Function to get html for the insert edit form header
3043
     *
3044
     * @param bool $has_blob_field whether has blob field
3045
     * @param bool $is_upload      whether is upload
3046
     *
3047
     * @return string
3048
     */
3049
    public function getHtmlForInsertEditFormHeader($has_blob_field, $is_upload)
3050
    {
3051
        $html_output = '<form id="insertForm" class="lock-page ';
3052
        if ($has_blob_field && $is_upload) {
3053
            $html_output .= 'disableAjax';
3054
        }
3055
        $html_output .= '" method="post" action="tbl_replace.php" name="insertForm" ';
3056
        if ($is_upload) {
3057
            $html_output .= ' enctype="multipart/form-data"';
3058
        }
3059
        $html_output .= '>';
3060
3061
        return $html_output;
3062
    }
3063
3064
    /**
3065
     * Function to get html for each insert/edit column
3066
     *
3067
     * @param array  $table_columns         table columns
3068
     * @param int    $column_number         column index in table_columns
3069
     * @param array  $comments_map          comments map
3070
     * @param bool   $timestamp_seen        whether timestamp seen
3071
     * @param array  $current_result        current result
3072
     * @param string $chg_evt_handler       javascript change event handler
3073
     * @param string $jsvkey                javascript validation key
3074
     * @param string $vkey                  validation key
3075
     * @param bool   $insert_mode           whether insert mode
3076
     * @param array  $current_row           current row
3077
     * @param int    &$o_rows               row offset
3078
     * @param int    &$tabindex             tab index
3079
     * @param int    $columns_cnt           columns count
3080
     * @param bool   $is_upload             whether upload
3081
     * @param int    $tabindex_for_function tab index offset for function
3082
     * @param array  $foreigners            foreigners
3083
     * @param int    $tabindex_for_null     tab index offset for null
3084
     * @param int    $tabindex_for_value    tab index offset for value
3085
     * @param string $table                 table
3086
     * @param string $db                    database
3087
     * @param int    $row_id                row id
3088
     * @param array  $titles                titles
3089
     * @param int    $biggest_max_file_size biggest max file size
3090
     * @param string $default_char_editing  default char editing mode which is stored
3091
     *                                      in the config.inc.php script
3092
     * @param string $text_dir              text direction
3093
     * @param array  $repopulate            the data to be repopulated
3094
     * @param array  $column_mime           the mime information of column
3095
     * @param string $where_clause          the where clause
3096
     *
3097
     * @return string
3098
     */
3099
    private function getHtmlForInsertEditFormColumn(
3100
        array $table_columns,
3101
        $column_number,
3102
        array $comments_map,
3103
        $timestamp_seen,
3104
        $current_result,
3105
        $chg_evt_handler,
3106
        $jsvkey,
3107
        $vkey,
3108
        $insert_mode,
3109
        array $current_row,
3110
        &$o_rows,
3111
        &$tabindex,
3112
        $columns_cnt,
3113
        $is_upload,
3114
        $tabindex_for_function,
3115
        array $foreigners,
3116
        $tabindex_for_null,
3117
        $tabindex_for_value,
3118
        $table,
3119
        $db,
3120
        $row_id,
3121
        array $titles,
3122
        $biggest_max_file_size,
3123
        $default_char_editing,
3124
        $text_dir,
3125
        array $repopulate,
3126
        array $column_mime,
3127
        $where_clause
3128
    ) {
3129
        $column = $table_columns[$column_number];
3130
        $readOnly = false;
3131
        if (! $this->userHasColumnPrivileges($column, $insert_mode)) {
3132
            $readOnly = true;
3133
        }
3134
3135
        if (! isset($column['processed'])) {
3136
            $column = $this->analyzeTableColumnsArray(
3137
                $column,
3138
                $comments_map,
3139
                $timestamp_seen
3140
            );
3141
        }
3142
        $as_is = false;
3143
        if (!empty($repopulate) && !empty($current_row)) {
3144
            $current_row[$column['Field']] = $repopulate[$column['Field_md5']];
3145
            $as_is = true;
3146
        }
3147
3148
        $extracted_columnspec
3149
            = Util::extractColumnSpec($column['Type']);
3150
3151
        if (-1 === $column['len']) {
3152
            $column['len'] = $this->dbi->fieldLen(
3153
                $current_result,
3154
                $column_number
3155
            );
3156
            // length is unknown for geometry fields,
3157
            // make enough space to edit very simple WKTs
3158
            if (-1 === $column['len']) {
3159
                $column['len'] = 30;
3160
            }
3161
        }
3162
        //Call validation when the form submitted...
3163
        $onChangeClause = $chg_evt_handler
3164
            . "=\"return verificationsAfterFieldChange('"
3165
            . Sanitize::escapeJsString($column['Field_md5']) . "', '"
3166
            . Sanitize::escapeJsString($jsvkey) . "','" . $column['pma_type'] . "')\"";
3167
3168
        // Use an MD5 as an array index to avoid having special characters
3169
        // in the name attribute (see bug #1746964 )
3170
        $column_name_appendix = $vkey . '[' . $column['Field_md5'] . ']';
3171
3172
        if ($column['Type'] === 'datetime'
3173
            && ! isset($column['Default'])
3174
            && ! is_null($column['Default'])
3175
            && $insert_mode
3176
        ) {
3177
            $column['Default'] = date('Y-m-d H:i:s', time());
3178
        }
3179
3180
        $html_output = $this->getHtmlForFunctionOption(
3181
            $column,
3182
            $column_name_appendix
3183
        );
3184
3185
        if ($GLOBALS['cfg']['ShowFieldTypesInDataEditView']) {
3186
            $html_output .= $this->getHtmlForInsertEditColumnType($column);
3187
        } //End if
3188
3189
        // Get a list of GIS data types.
3190
        $gis_data_types = Util::getGISDatatypes();
3191
3192
        // Prepares the field value
3193
        $real_null_value = false;
3194
        $special_chars_encoded = '';
3195
        if (!empty($current_row)) {
3196
            // (we are editing)
3197
            list(
3198
                $real_null_value, $special_chars_encoded, $special_chars,
3199
                $data, $backup_field
3200
            )
3201
                = $this->getSpecialCharsAndBackupFieldForExistingRow(
3202
                    $current_row,
3203
                    $column,
3204
                    $extracted_columnspec,
3205
                    $real_null_value,
3206
                    $gis_data_types,
3207
                    $column_name_appendix,
3208
                    $as_is
3209
                );
3210
        } else {
3211
            // (we are inserting)
3212
            // display default values
3213
            $tmp = $column;
3214
            if (isset($repopulate[$column['Field_md5']])) {
3215
                $tmp['Default'] = $repopulate[$column['Field_md5']];
3216
            }
3217
            list($real_null_value, $data, $special_chars, $backup_field,
3218
                $special_chars_encoded
3219
            )
3220
                = $this->getSpecialCharsAndBackupFieldForInsertingMode(
3221
                    $tmp,
3222
                    $real_null_value
3223
                );
3224
            unset($tmp);
3225
        }
3226
3227
        $idindex = ($o_rows * $columns_cnt) + $column_number + 1;
3228
        $tabindex = $idindex;
3229
3230
        // Get a list of data types that are not yet supported.
3231
        $no_support_types = Util::unsupportedDatatypes();
3232
3233
        // The function column
3234
        // -------------------
3235
        $foreignData = $this->relation->getForeignData(
3236
            $foreigners,
3237
            $column['Field'],
3238
            false,
3239
            '',
3240
            ''
3241
        );
3242
        if ($GLOBALS['cfg']['ShowFunctionFields']) {
3243
            $html_output .= $this->getFunctionColumn(
3244
                $column,
3245
                $is_upload,
3246
                $column_name_appendix,
3247
                $onChangeClause,
3248
                $no_support_types,
3249
                $tabindex_for_function,
3250
                $tabindex,
3251
                $idindex,
3252
                $insert_mode,
3253
                $readOnly,
3254
                $foreignData
3255
            );
3256
        }
3257
3258
        // The null column
3259
        // ---------------
3260
        $html_output .= $this->getNullColumn(
3261
            $column,
3262
            $column_name_appendix,
3263
            $real_null_value,
3264
            $tabindex,
3265
            $tabindex_for_null,
3266
            $idindex,
3267
            $vkey,
3268
            $foreigners,
3269
            $foreignData,
3270
            $readOnly
3271
        );
3272
3273
        // The value column (depends on type)
3274
        // ----------------
3275
        // See bug #1667887 for the reason why we don't use the maxlength
3276
        // HTML attribute
3277
3278
        //add data attributes "no of decimals" and "data type"
3279
        $no_decimals = 0;
3280
        $type = current(explode("(", $column['pma_type']));
3281
        if (preg_match('/\(([^()]+)\)/', $column['pma_type'], $match)) {
3282
            $match[0] = trim($match[0], '()');
3283
            $no_decimals = $match[0];
3284
        }
3285
        $html_output .= '<td' . ' data-type="' . $type . '"' . ' data-decimals="'
3286
            . $no_decimals . '">' . "\n";
3287
        // Will be used by js/tbl_change.js to set the default value
3288
        // for the "Continue insertion" feature
3289
        $html_output .= '<span class="default_value hide">'
3290
            . $special_chars . '</span>';
3291
3292
        // Check input transformation of column
3293
        $transformed_html = '';
3294
        if (!empty($column_mime['input_transformation'])) {
3295
            $file = $column_mime['input_transformation'];
3296
            $include_file = 'libraries/classes/Plugins/Transformations/' . $file;
3297
            if (is_file($include_file)) {
3298
                include_once $include_file;
3299
                $class_name = $this->transformations->getClassName($include_file);
3300
                $transformation_plugin = new $class_name();
3301
                $transformation_options = $this->transformations->getOptions(
3302
                    $column_mime['input_transformation_options']
3303
                );
3304
                $_url_params = [
3305
                    'db'            => $db,
3306
                    'table'         => $table,
3307
                    'transform_key' => $column['Field'],
3308
                    'where_clause'  => $where_clause
3309
                ];
3310
                $transformation_options['wrapper_link']
3311
                    = Url::getCommon($_url_params);
3312
                $current_value = '';
3313
                if (isset($current_row[$column['Field']])) {
3314
                    $current_value = $current_row[$column['Field']];
3315
                }
3316
                if (method_exists($transformation_plugin, 'getInputHtml')) {
3317
                    $transformed_html = $transformation_plugin->getInputHtml(
3318
                        $column,
3319
                        $row_id,
3320
                        $column_name_appendix,
3321
                        $transformation_options,
3322
                        $current_value,
3323
                        $text_dir,
3324
                        $tabindex,
3325
                        $tabindex_for_value,
3326
                        $idindex
3327
                    );
3328
                }
3329
                if (method_exists($transformation_plugin, 'getScripts')) {
3330
                    $GLOBALS['plugin_scripts'] = array_merge(
3331
                        $GLOBALS['plugin_scripts'],
3332
                        $transformation_plugin->getScripts()
3333
                    );
3334
                }
3335
            }
3336
        }
3337
        if (!empty($transformed_html)) {
3338
            $html_output .= $transformed_html;
3339
        } else {
3340
            $html_output .= $this->getValueColumn(
3341
                $column,
3342
                $backup_field,
3343
                $column_name_appendix,
3344
                $onChangeClause,
3345
                $tabindex,
3346
                $tabindex_for_value,
3347
                $idindex,
3348
                $data,
3349
                $special_chars,
3350
                $foreignData,
3351
                [$table, $db],
3352
                $row_id,
3353
                $titles,
3354
                $text_dir,
3355
                $special_chars_encoded,
3356
                $vkey,
3357
                $is_upload,
3358
                $biggest_max_file_size,
3359
                $default_char_editing,
3360
                $no_support_types,
3361
                $gis_data_types,
3362
                $extracted_columnspec,
3363
                $readOnly
3364
            );
3365
        }
3366
        return $html_output;
3367
    }
3368
3369
    /**
3370
     * Function to get html for each insert/edit row
3371
     *
3372
     * @param array  $url_params            url parameters
3373
     * @param array  $table_columns         table columns
3374
     * @param array  $comments_map          comments map
3375
     * @param bool   $timestamp_seen        whether timestamp seen
3376
     * @param array  $current_result        current result
3377
     * @param string $chg_evt_handler       javascript change event handler
3378
     * @param string $jsvkey                javascript validation key
3379
     * @param string $vkey                  validation key
3380
     * @param bool   $insert_mode           whether insert mode
3381
     * @param array  $current_row           current row
3382
     * @param int    &$o_rows               row offset
3383
     * @param int    &$tabindex             tab index
3384
     * @param int    $columns_cnt           columns count
3385
     * @param bool   $is_upload             whether upload
3386
     * @param int    $tabindex_for_function tab index offset for function
3387
     * @param array  $foreigners            foreigners
3388
     * @param int    $tabindex_for_null     tab index offset for null
3389
     * @param int    $tabindex_for_value    tab index offset for value
3390
     * @param string $table                 table
3391
     * @param string $db                    database
3392
     * @param int    $row_id                row id
3393
     * @param array  $titles                titles
3394
     * @param int    $biggest_max_file_size biggest max file size
3395
     * @param string $text_dir              text direction
3396
     * @param array  $repopulate            the data to be repopulated
3397
     * @param array  $where_clause_array    the array of where clauses
3398
     *
3399
     * @return string
3400
     */
3401
    public function getHtmlForInsertEditRow(
3402
        array $url_params,
3403
        array $table_columns,
3404
        array $comments_map,
3405
        $timestamp_seen,
3406
        $current_result,
3407
        $chg_evt_handler,
3408
        $jsvkey,
3409
        $vkey,
3410
        $insert_mode,
3411
        array $current_row,
3412
        &$o_rows,
3413
        &$tabindex,
3414
        $columns_cnt,
3415
        $is_upload,
3416
        $tabindex_for_function,
3417
        array $foreigners,
3418
        $tabindex_for_null,
3419
        $tabindex_for_value,
3420
        $table,
3421
        $db,
3422
        $row_id,
3423
        array $titles,
3424
        $biggest_max_file_size,
3425
        $text_dir,
3426
        array $repopulate,
3427
        array $where_clause_array
3428
    ) {
3429
        $html_output = $this->getHeadAndFootOfInsertRowTable($url_params)
3430
            . '<tbody>';
3431
3432
        //store the default value for CharEditing
3433
        $default_char_editing = $GLOBALS['cfg']['CharEditing'];
3434
        $mime_map = $this->transformations->getMime($db, $table);
3435
        $where_clause = '';
3436
        if (isset($where_clause_array[$row_id])) {
3437
            $where_clause = $where_clause_array[$row_id];
3438
        }
3439
        for ($column_number = 0; $column_number < $columns_cnt; $column_number++) {
3440
            $table_column = $table_columns[$column_number];
3441
            $column_mime = [];
3442
            if (isset($mime_map[$table_column['Field']])) {
3443
                $column_mime = $mime_map[$table_column['Field']];
3444
            }
3445
            $html_output .= $this->getHtmlForInsertEditFormColumn(
3446
                $table_columns,
3447
                $column_number,
3448
                $comments_map,
3449
                $timestamp_seen,
3450
                $current_result,
3451
                $chg_evt_handler,
3452
                $jsvkey,
3453
                $vkey,
3454
                $insert_mode,
3455
                $current_row,
3456
                $o_rows,
3457
                $tabindex,
3458
                $columns_cnt,
3459
                $is_upload,
3460
                $tabindex_for_function,
3461
                $foreigners,
3462
                $tabindex_for_null,
3463
                $tabindex_for_value,
3464
                $table,
3465
                $db,
3466
                $row_id,
3467
                $titles,
3468
                $biggest_max_file_size,
3469
                $default_char_editing,
3470
                $text_dir,
3471
                $repopulate,
3472
                $column_mime,
3473
                $where_clause
3474
            );
3475
        } // end for
3476
        $o_rows++;
3477
        $html_output .= '  </tbody>'
3478
            . '</table></div><br />'
3479
            . '<div class="clearfloat"></div>';
3480
3481
        return $html_output;
3482
    }
3483
3484
    /**
3485
     * Returns whether the user has necessary insert/update privileges for the column
3486
     *
3487
     * @param array $table_column array of column details
3488
     * @param bool  $insert_mode  whether on insert mode
3489
     *
3490
     * @return boolean whether user has necessary privileges
3491
     */
3492
    private function userHasColumnPrivileges(array $table_column, $insert_mode)
3493
    {
3494
        $privileges = $table_column['Privileges'];
3495
        return ($insert_mode && strstr($privileges, 'insert') !== false)
3496
            || (! $insert_mode && strstr($privileges, 'update') !== false);
3497
    }
3498
}
3499