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