1 | <?php |
||
2 | /* vim: set expandtab sw=4 ts=4 sts=4: */ |
||
3 | /** |
||
4 | * Hold the PhpMyAdmin\Display\Results class |
||
5 | * |
||
6 | * @package PhpMyAdmin |
||
7 | */ |
||
8 | declare(strict_types=1); |
||
9 | |||
10 | namespace PhpMyAdmin\Display; |
||
11 | |||
12 | use PhpMyAdmin\Config\SpecialSchemaLinks; |
||
13 | use PhpMyAdmin\Core; |
||
14 | use PhpMyAdmin\DatabaseInterface; |
||
15 | use PhpMyAdmin\Index; |
||
16 | use PhpMyAdmin\Message; |
||
17 | use PhpMyAdmin\Plugins\Transformations\Output\Text_Octetstream_Sql; |
||
18 | use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_Json; |
||
19 | use PhpMyAdmin\Plugins\Transformations\Output\Text_Plain_Sql; |
||
20 | use PhpMyAdmin\Plugins\Transformations\Text_Plain_Link; |
||
21 | use PhpMyAdmin\Plugins\TransformationsPlugin; |
||
22 | use PhpMyAdmin\Relation; |
||
23 | use PhpMyAdmin\Response; |
||
24 | use PhpMyAdmin\Sanitize; |
||
25 | use PhpMyAdmin\Sql; |
||
26 | use PhpMyAdmin\SqlParser\Statements\SelectStatement; |
||
27 | use PhpMyAdmin\SqlParser\Utils\Query; |
||
28 | use PhpMyAdmin\Table; |
||
29 | use PhpMyAdmin\Template; |
||
30 | use PhpMyAdmin\Transformations; |
||
31 | use PhpMyAdmin\Url; |
||
32 | use PhpMyAdmin\Util; |
||
33 | use stdClass; |
||
34 | |||
35 | /** |
||
36 | * Handle all the functionalities related to displaying results |
||
37 | * of sql queries, stored procedure, browsing sql processes or |
||
38 | * displaying binary log. |
||
39 | * |
||
40 | * @package PhpMyAdmin |
||
41 | */ |
||
42 | class Results |
||
43 | { |
||
44 | // Define constants |
||
45 | public const NO_EDIT_OR_DELETE = 'nn'; |
||
46 | public const UPDATE_ROW = 'ur'; |
||
47 | public const DELETE_ROW = 'dr'; |
||
48 | public const KILL_PROCESS = 'kp'; |
||
49 | |||
50 | public const POSITION_LEFT = 'left'; |
||
51 | public const POSITION_RIGHT = 'right'; |
||
52 | public const POSITION_BOTH = 'both'; |
||
53 | public const POSITION_NONE = 'none'; |
||
54 | |||
55 | public const DISPLAY_FULL_TEXT = 'F'; |
||
56 | public const DISPLAY_PARTIAL_TEXT = 'P'; |
||
57 | |||
58 | public const HEADER_FLIP_TYPE_AUTO = 'auto'; |
||
59 | public const HEADER_FLIP_TYPE_CSS = 'css'; |
||
60 | public const HEADER_FLIP_TYPE_FAKE = 'fake'; |
||
61 | |||
62 | public const DATE_FIELD = 'date'; |
||
63 | public const DATETIME_FIELD = 'datetime'; |
||
64 | public const TIMESTAMP_FIELD = 'timestamp'; |
||
65 | public const TIME_FIELD = 'time'; |
||
66 | public const STRING_FIELD = 'string'; |
||
67 | public const GEOMETRY_FIELD = 'geometry'; |
||
68 | public const BLOB_FIELD = 'BLOB'; |
||
69 | public const BINARY_FIELD = 'BINARY'; |
||
70 | |||
71 | public const RELATIONAL_KEY = 'K'; |
||
72 | public const RELATIONAL_DISPLAY_COLUMN = 'D'; |
||
73 | |||
74 | public const GEOMETRY_DISP_GEOM = 'GEOM'; |
||
75 | public const GEOMETRY_DISP_WKT = 'WKT'; |
||
76 | public const GEOMETRY_DISP_WKB = 'WKB'; |
||
77 | |||
78 | public const SMART_SORT_ORDER = 'SMART'; |
||
79 | public const ASCENDING_SORT_DIR = 'ASC'; |
||
80 | public const DESCENDING_SORT_DIR = 'DESC'; |
||
81 | |||
82 | public const TABLE_TYPE_INNO_DB = 'InnoDB'; |
||
83 | public const ALL_ROWS = 'all'; |
||
84 | public const QUERY_TYPE_SELECT = 'SELECT'; |
||
85 | |||
86 | public const ROUTINE_PROCEDURE = 'procedure'; |
||
87 | public const ROUTINE_FUNCTION = 'function'; |
||
88 | |||
89 | public const ACTION_LINK_CONTENT_ICONS = 'icons'; |
||
90 | public const ACTION_LINK_CONTENT_TEXT = 'text'; |
||
91 | |||
92 | // Declare global fields |
||
93 | |||
94 | /** array with properties of the class */ |
||
95 | private $_property_array = [ |
||
96 | |||
97 | /** integer server id */ |
||
98 | 'server' => null, |
||
99 | |||
100 | /** string Database name */ |
||
101 | 'db' => null, |
||
102 | |||
103 | /** string Table name */ |
||
104 | 'table' => null, |
||
105 | |||
106 | /** string the URL to go back in case of errors */ |
||
107 | 'goto' => null, |
||
108 | |||
109 | /** string the SQL query */ |
||
110 | 'sql_query' => null, |
||
111 | |||
112 | /** |
||
113 | * integer the total number of rows returned by the SQL query without any |
||
114 | * appended "LIMIT" clause programmatically |
||
115 | */ |
||
116 | 'unlim_num_rows' => null, |
||
117 | |||
118 | /** array meta information about fields */ |
||
119 | 'fields_meta' => null, |
||
120 | |||
121 | /** boolean */ |
||
122 | 'is_count' => null, |
||
123 | |||
124 | /** integer */ |
||
125 | 'is_export' => null, |
||
126 | |||
127 | /** boolean */ |
||
128 | 'is_func' => null, |
||
129 | |||
130 | /** integer */ |
||
131 | 'is_analyse' => null, |
||
132 | |||
133 | /** integer the total number of rows returned by the SQL query */ |
||
134 | 'num_rows' => null, |
||
135 | |||
136 | /** integer the total number of fields returned by the SQL query */ |
||
137 | 'fields_cnt' => null, |
||
138 | |||
139 | /** double time taken for execute the SQL query */ |
||
140 | 'querytime' => null, |
||
141 | |||
142 | /** string path for theme images directory */ |
||
143 | 'pma_theme_image' => null, |
||
144 | |||
145 | /** string */ |
||
146 | 'text_dir' => null, |
||
147 | |||
148 | /** boolean */ |
||
149 | 'is_maint' => null, |
||
150 | |||
151 | /** boolean */ |
||
152 | 'is_explain' => null, |
||
153 | |||
154 | /** boolean */ |
||
155 | 'is_show' => null, |
||
156 | |||
157 | /** boolean */ |
||
158 | 'is_browse_distinct' => null, |
||
159 | |||
160 | /** array table definitions */ |
||
161 | 'showtable' => null, |
||
162 | |||
163 | /** string */ |
||
164 | 'printview' => null, |
||
165 | |||
166 | /** string URL query */ |
||
167 | 'url_query' => null, |
||
168 | |||
169 | /** array column names to highlight */ |
||
170 | 'highlight_columns' => null, |
||
171 | |||
172 | /** array holding various display information */ |
||
173 | 'display_params' => null, |
||
174 | |||
175 | /** array mime types information of fields */ |
||
176 | 'mime_map' => null, |
||
177 | |||
178 | /** boolean */ |
||
179 | 'editable' => null, |
||
180 | |||
181 | /** random unique ID to distinguish result set */ |
||
182 | 'unique_id' => null, |
||
183 | |||
184 | /** where clauses for each row, each table in the row */ |
||
185 | 'whereClauseMap' => [], |
||
186 | ]; |
||
187 | |||
188 | /** |
||
189 | * This variable contains the column transformation information |
||
190 | * for some of the system databases. |
||
191 | * One element of this array represent all relevant columns in all tables in |
||
192 | * one specific database |
||
193 | */ |
||
194 | public $transformation_info; |
||
195 | |||
196 | /** |
||
197 | * @var Relation |
||
198 | */ |
||
199 | private $relation; |
||
200 | |||
201 | /** |
||
202 | * @var Transformations |
||
203 | */ |
||
204 | private $transformations; |
||
205 | |||
206 | /** |
||
207 | * @var Template |
||
208 | */ |
||
209 | public $template; |
||
210 | |||
211 | /** |
||
212 | * Constructor for PhpMyAdmin\Display\Results class |
||
213 | * |
||
214 | * @param string $db the database name |
||
215 | * @param string $table the table name |
||
216 | * @param int $server the server id |
||
217 | * @param string $goto the URL to go back in case of errors |
||
218 | * @param string $sql_query the SQL query |
||
219 | * |
||
220 | * @access public |
||
221 | */ |
||
222 | public function __construct($db, $table, $server, $goto, $sql_query) |
||
223 | { |
||
224 | $this->relation = new Relation($GLOBALS['dbi']); |
||
225 | $this->transformations = new Transformations(); |
||
226 | $this->template = new Template(); |
||
227 | |||
228 | $this->_setDefaultTransformations(); |
||
229 | |||
230 | $this->__set('db', $db); |
||
231 | $this->__set('table', $table); |
||
232 | $this->__set('server', $server); |
||
233 | $this->__set('goto', $goto); |
||
234 | $this->__set('sql_query', $sql_query); |
||
235 | $this->__set('unique_id', mt_rand()); |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Get any property of this class |
||
240 | * |
||
241 | * @param string $property name of the property |
||
242 | * |
||
243 | * @return mixed|void if property exist, value of the relevant property |
||
244 | */ |
||
245 | public function __get($property) |
||
246 | { |
||
247 | return $this->_property_array[$property] ?? null; |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Set values for any property of this class |
||
252 | * |
||
253 | * @param string $property name of the property |
||
254 | * @param mixed $value value to set |
||
255 | * |
||
256 | * @return void |
||
257 | */ |
||
258 | public function __set($property, $value) |
||
259 | { |
||
260 | if (array_key_exists($property, $this->_property_array)) { |
||
261 | $this->_property_array[$property] = $value; |
||
262 | } |
||
263 | } |
||
264 | |||
265 | /** |
||
266 | * Sets default transformations for some columns |
||
267 | * |
||
268 | * @return void |
||
269 | */ |
||
270 | private function _setDefaultTransformations() |
||
271 | { |
||
272 | $json_highlighting_data = [ |
||
273 | 'libraries/classes/Plugins/Transformations/Output/Text_Plain_Json.php', |
||
274 | Text_Plain_Json::class, |
||
275 | 'Text_Plain', |
||
276 | ]; |
||
277 | $sql_highlighting_data = [ |
||
278 | 'libraries/classes/Plugins/Transformations/Output/Text_Plain_Sql.php', |
||
279 | Text_Plain_Sql::class, |
||
280 | 'Text_Plain', |
||
281 | ]; |
||
282 | $blob_sql_highlighting_data = [ |
||
283 | 'libraries/classes/Plugins/Transformations/Output/Text_Octetstream_Sql.php', |
||
284 | Text_Octetstream_Sql::class, |
||
285 | 'Text_Octetstream', |
||
286 | ]; |
||
287 | $link_data = [ |
||
288 | 'libraries/classes/Plugins/Transformations/Text_Plain_Link.php', |
||
289 | Text_Plain_Link::class, |
||
290 | 'Text_Plain', |
||
291 | ]; |
||
292 | $this->transformation_info = [ |
||
293 | 'information_schema' => [ |
||
294 | 'events' => [ |
||
295 | 'event_definition' => $sql_highlighting_data, |
||
296 | ], |
||
297 | 'processlist' => [ |
||
298 | 'info' => $sql_highlighting_data, |
||
299 | ], |
||
300 | 'routines' => [ |
||
301 | 'routine_definition' => $sql_highlighting_data, |
||
302 | ], |
||
303 | 'triggers' => [ |
||
304 | 'action_statement' => $sql_highlighting_data, |
||
305 | ], |
||
306 | 'views' => [ |
||
307 | 'view_definition' => $sql_highlighting_data, |
||
308 | ], |
||
309 | ], |
||
310 | 'mysql' => [ |
||
311 | 'event' => [ |
||
312 | 'body' => $blob_sql_highlighting_data, |
||
313 | 'body_utf8' => $blob_sql_highlighting_data, |
||
314 | ], |
||
315 | 'general_log' => [ |
||
316 | 'argument' => $sql_highlighting_data, |
||
317 | ], |
||
318 | 'help_category' => [ |
||
319 | 'url' => $link_data, |
||
320 | ], |
||
321 | 'help_topic' => [ |
||
322 | 'example' => $sql_highlighting_data, |
||
323 | 'url' => $link_data, |
||
324 | ], |
||
325 | 'proc' => [ |
||
326 | 'param_list' => $blob_sql_highlighting_data, |
||
327 | 'returns' => $blob_sql_highlighting_data, |
||
328 | 'body' => $blob_sql_highlighting_data, |
||
329 | 'body_utf8' => $blob_sql_highlighting_data, |
||
330 | ], |
||
331 | 'slow_log' => [ |
||
332 | 'sql_text' => $sql_highlighting_data, |
||
333 | ], |
||
334 | ], |
||
335 | ]; |
||
336 | |||
337 | $cfgRelation = $this->relation->getRelationsParam(); |
||
338 | if ($cfgRelation['db']) { |
||
339 | $this->transformation_info[$cfgRelation['db']] = []; |
||
340 | $relDb = &$this->transformation_info[$cfgRelation['db']]; |
||
341 | if (! empty($cfgRelation['history'])) { |
||
342 | $relDb[$cfgRelation['history']] = [ |
||
343 | 'sqlquery' => $sql_highlighting_data, |
||
344 | ]; |
||
345 | } |
||
346 | if (! empty($cfgRelation['bookmark'])) { |
||
347 | $relDb[$cfgRelation['bookmark']] = [ |
||
348 | 'query' => $sql_highlighting_data, |
||
349 | ]; |
||
350 | } |
||
351 | if (! empty($cfgRelation['tracking'])) { |
||
352 | $relDb[$cfgRelation['tracking']] = [ |
||
353 | 'schema_sql' => $sql_highlighting_data, |
||
354 | 'data_sql' => $sql_highlighting_data, |
||
355 | ]; |
||
356 | } |
||
357 | if (! empty($cfgRelation['favorite'])) { |
||
358 | $relDb[$cfgRelation['favorite']] = [ |
||
359 | 'tables' => $json_highlighting_data, |
||
360 | ]; |
||
361 | } |
||
362 | if (! empty($cfgRelation['recent'])) { |
||
363 | $relDb[$cfgRelation['recent']] = [ |
||
364 | 'tables' => $json_highlighting_data, |
||
365 | ]; |
||
366 | } |
||
367 | if (! empty($cfgRelation['savedsearches'])) { |
||
368 | $relDb[$cfgRelation['savedsearches']] = [ |
||
369 | 'search_data' => $json_highlighting_data, |
||
370 | ]; |
||
371 | } |
||
372 | if (! empty($cfgRelation['designer_settings'])) { |
||
373 | $relDb[$cfgRelation['designer_settings']] = [ |
||
374 | 'settings_data' => $json_highlighting_data, |
||
375 | ]; |
||
376 | } |
||
377 | if (! empty($cfgRelation['table_uiprefs'])) { |
||
378 | $relDb[$cfgRelation['table_uiprefs']] = [ |
||
379 | 'prefs' => $json_highlighting_data, |
||
380 | ]; |
||
381 | } |
||
382 | if (! empty($cfgRelation['userconfig'])) { |
||
383 | $relDb[$cfgRelation['userconfig']] = [ |
||
384 | 'config_data' => $json_highlighting_data, |
||
385 | ]; |
||
386 | } |
||
387 | if (! empty($cfgRelation['export_templates'])) { |
||
388 | $relDb[$cfgRelation['export_templates']] = [ |
||
389 | 'template_data' => $json_highlighting_data, |
||
390 | ]; |
||
391 | } |
||
392 | } |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Set properties which were not initialized at the constructor |
||
397 | * |
||
398 | * @param integer $unlim_num_rows the total number of rows returned by |
||
399 | * the SQL query without any appended |
||
400 | * "LIMIT" clause programmatically |
||
401 | * @param stdClass $fields_meta meta information about fields |
||
402 | * @param boolean $is_count statement is SELECT COUNT |
||
403 | * @param integer $is_export statement contains INTO OUTFILE |
||
404 | * @param boolean $is_func statement contains a function like SUM() |
||
405 | * @param integer $is_analyse statement contains PROCEDURE ANALYSE |
||
406 | * @param integer $num_rows total no. of rows returned by SQL query |
||
407 | * @param integer $fields_cnt total no.of fields returned by SQL query |
||
408 | * @param double $querytime time taken for execute the SQL query |
||
409 | * @param string $pmaThemeImage path for theme images directory |
||
410 | * @param string $text_dir text direction |
||
411 | * @param boolean $is_maint statement contains a maintenance command |
||
412 | * @param boolean $is_explain statement contains EXPLAIN |
||
413 | * @param boolean $is_show statement contains SHOW |
||
414 | * @param array $showtable table definitions |
||
415 | * @param string $printview print view was requested |
||
416 | * @param string $url_query URL query |
||
417 | * @param boolean $editable whether the results set is editable |
||
418 | * @param boolean $is_browse_dist whether browsing distinct values |
||
419 | * |
||
420 | * @return void |
||
421 | * |
||
422 | * @see sql.php |
||
423 | */ |
||
424 | public function setProperties( |
||
425 | $unlim_num_rows, |
||
426 | $fields_meta, |
||
427 | $is_count, |
||
428 | $is_export, |
||
429 | $is_func, |
||
430 | $is_analyse, |
||
431 | $num_rows, |
||
432 | $fields_cnt, |
||
433 | $querytime, |
||
434 | $pmaThemeImage, |
||
435 | $text_dir, |
||
436 | $is_maint, |
||
437 | $is_explain, |
||
438 | $is_show, |
||
439 | $showtable, |
||
440 | $printview, |
||
441 | $url_query, |
||
442 | $editable, |
||
443 | $is_browse_dist |
||
444 | ) { |
||
445 | |||
446 | $this->__set('unlim_num_rows', $unlim_num_rows); |
||
447 | $this->__set('fields_meta', $fields_meta); |
||
448 | $this->__set('is_count', $is_count); |
||
449 | $this->__set('is_export', $is_export); |
||
450 | $this->__set('is_func', $is_func); |
||
451 | $this->__set('is_analyse', $is_analyse); |
||
452 | $this->__set('num_rows', $num_rows); |
||
453 | $this->__set('fields_cnt', $fields_cnt); |
||
454 | $this->__set('querytime', $querytime); |
||
455 | $this->__set('pma_theme_image', $pmaThemeImage); |
||
456 | $this->__set('text_dir', $text_dir); |
||
457 | $this->__set('is_maint', $is_maint); |
||
458 | $this->__set('is_explain', $is_explain); |
||
459 | $this->__set('is_show', $is_show); |
||
460 | $this->__set('showtable', $showtable); |
||
461 | $this->__set('printview', $printview); |
||
462 | $this->__set('url_query', $url_query); |
||
463 | $this->__set('editable', $editable); |
||
464 | $this->__set('is_browse_distinct', $is_browse_dist); |
||
465 | } |
||
466 | |||
467 | /** |
||
468 | * Defines the parts to display for a print view |
||
469 | * |
||
470 | * @param array $displayParts the parts to display |
||
471 | * |
||
472 | * @return array the modified display parts |
||
473 | * |
||
474 | * @access private |
||
475 | * |
||
476 | */ |
||
477 | private function _setDisplayPartsForPrintView(array $displayParts) |
||
478 | { |
||
479 | // set all elements to false! |
||
480 | $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link |
||
481 | $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link |
||
482 | $displayParts['sort_lnk'] = (string) '0'; |
||
483 | $displayParts['nav_bar'] = (string) '0'; |
||
484 | $displayParts['bkm_form'] = (string) '0'; |
||
485 | $displayParts['text_btn'] = (string) '0'; |
||
486 | $displayParts['pview_lnk'] = (string) '0'; |
||
487 | |||
488 | return $displayParts; |
||
489 | } |
||
490 | |||
491 | /** |
||
492 | * Defines the parts to display for a SHOW statement |
||
493 | * |
||
494 | * @param array $displayParts the parts to display |
||
495 | * |
||
496 | * @return array the modified display parts |
||
497 | * |
||
498 | * @access private |
||
499 | * |
||
500 | */ |
||
501 | private function _setDisplayPartsForShow(array $displayParts) |
||
502 | { |
||
503 | preg_match( |
||
504 | '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?' |
||
505 | . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS' |
||
506 | . ')@i', |
||
507 | $this->__get('sql_query'), |
||
508 | $which |
||
509 | ); |
||
510 | |||
511 | $bIsProcessList = isset($which[1]); |
||
512 | if ($bIsProcessList) { |
||
513 | $str = ' ' . strtoupper($which[1]); |
||
514 | $bIsProcessList = $bIsProcessList |
||
515 | && strpos($str, 'PROCESSLIST') > 0; |
||
516 | } |
||
517 | |||
518 | if ($bIsProcessList) { |
||
519 | // no edit link |
||
520 | $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; |
||
521 | // "kill process" type edit link |
||
522 | $displayParts['del_lnk'] = self::KILL_PROCESS; |
||
523 | } else { |
||
524 | // Default case -> no links |
||
525 | // no edit link |
||
526 | $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; |
||
527 | // no delete link |
||
528 | $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; |
||
529 | } |
||
530 | // Other settings |
||
531 | $displayParts['sort_lnk'] = (string) '0'; |
||
532 | $displayParts['nav_bar'] = (string) '0'; |
||
533 | $displayParts['bkm_form'] = (string) '1'; |
||
534 | $displayParts['text_btn'] = (string) '1'; |
||
535 | $displayParts['pview_lnk'] = (string) '1'; |
||
536 | |||
537 | return $displayParts; |
||
538 | } |
||
539 | |||
540 | /** |
||
541 | * Defines the parts to display for statements not related to data |
||
542 | * |
||
543 | * @param array $displayParts the parts to display |
||
544 | * |
||
545 | * @return array the modified display parts |
||
546 | * |
||
547 | * @access private |
||
548 | * |
||
549 | */ |
||
550 | private function _setDisplayPartsForNonData(array $displayParts) |
||
551 | { |
||
552 | // Statement is a "SELECT COUNT", a |
||
553 | // "CHECK/ANALYZE/REPAIR/OPTIMIZE/CHECKSUM", an "EXPLAIN" one or |
||
554 | // contains a "PROC ANALYSE" part |
||
555 | $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; // no edit link |
||
556 | $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; // no delete link |
||
557 | $displayParts['sort_lnk'] = (string) '0'; |
||
558 | $displayParts['nav_bar'] = (string) '0'; |
||
559 | $displayParts['bkm_form'] = (string) '1'; |
||
560 | |||
561 | if ($this->__get('is_maint')) { |
||
562 | $displayParts['text_btn'] = (string) '1'; |
||
563 | } else { |
||
564 | $displayParts['text_btn'] = (string) '0'; |
||
565 | } |
||
566 | $displayParts['pview_lnk'] = (string) '1'; |
||
567 | |||
568 | return $displayParts; |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Defines the parts to display for other statements (probably SELECT) |
||
573 | * |
||
574 | * @param array $displayParts the parts to display |
||
575 | * |
||
576 | * @return array the modified display parts |
||
577 | * |
||
578 | * @access private |
||
579 | * |
||
580 | */ |
||
581 | private function _setDisplayPartsForSelect(array $displayParts) |
||
582 | { |
||
583 | // Other statements (ie "SELECT" ones) -> updates |
||
584 | // $displayParts['edit_lnk'], $displayParts['del_lnk'] and |
||
585 | // $displayParts['text_btn'] (keeps other default values) |
||
586 | |||
587 | $fields_meta = $this->__get('fields_meta'); |
||
588 | $prev_table = ''; |
||
589 | $displayParts['text_btn'] = (string) '1'; |
||
590 | $number_of_columns = $this->__get('fields_cnt'); |
||
591 | |||
592 | for ($i = 0; $i < $number_of_columns; $i++) { |
||
593 | $is_link = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
594 | || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE) |
||
595 | || ($displayParts['sort_lnk'] != '0'); |
||
596 | |||
597 | // Displays edit/delete/sort/insert links? |
||
598 | if ($is_link |
||
599 | && $prev_table != '' |
||
600 | && $fields_meta[$i]->table != '' |
||
601 | && $fields_meta[$i]->table != $prev_table |
||
602 | ) { |
||
603 | // don't display links |
||
604 | $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; |
||
605 | $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; |
||
606 | /** |
||
607 | * @todo May be problematic with same field names |
||
608 | * in two joined table. |
||
609 | */ |
||
610 | // $displayParts['sort_lnk'] = (string) '0'; |
||
611 | if ($displayParts['text_btn'] == '1') { |
||
612 | break; |
||
613 | } |
||
614 | } // end if |
||
615 | |||
616 | // Always display print view link |
||
617 | $displayParts['pview_lnk'] = (string) '1'; |
||
618 | if ($fields_meta[$i]->table != '') { |
||
619 | $prev_table = $fields_meta[$i]->table; |
||
620 | } |
||
621 | } // end for |
||
622 | |||
623 | if ($prev_table == '') { // no table for any of the columns |
||
624 | // don't display links |
||
625 | $displayParts['edit_lnk'] = self::NO_EDIT_OR_DELETE; |
||
626 | $displayParts['del_lnk'] = self::NO_EDIT_OR_DELETE; |
||
627 | } |
||
628 | |||
629 | return $displayParts; |
||
630 | } |
||
631 | |||
632 | /** |
||
633 | * Defines the parts to display for the results of a SQL query |
||
634 | * and the total number of rows |
||
635 | * |
||
636 | * @param array $displayParts the parts to display (see a few |
||
637 | * lines above for explanations) |
||
638 | * |
||
639 | * @return array the first element is an array with explicit indexes |
||
640 | * for all the display elements |
||
641 | * the second element is the total number of rows returned |
||
642 | * by the SQL query without any programmatically appended |
||
643 | * LIMIT clause (just a copy of $unlim_num_rows if it exists, |
||
644 | * else computed inside this function) |
||
645 | * |
||
646 | * |
||
647 | * @access private |
||
648 | * |
||
649 | * @see getTable() |
||
650 | */ |
||
651 | private function _setDisplayPartsAndTotal(array $displayParts) |
||
652 | { |
||
653 | $the_total = 0; |
||
654 | |||
655 | // 1. Following variables are needed for use in isset/empty or |
||
656 | // use with array indexes or safe use in foreach |
||
657 | $db = $this->__get('db'); |
||
658 | $table = $this->__get('table'); |
||
659 | $unlim_num_rows = $this->__get('unlim_num_rows'); |
||
660 | $num_rows = $this->__get('num_rows'); |
||
661 | $printview = $this->__get('printview'); |
||
662 | |||
663 | // 2. Updates the display parts |
||
664 | if ($printview == '1') { |
||
665 | $displayParts = $this->_setDisplayPartsForPrintView($displayParts); |
||
666 | } elseif ($this->__get('is_count') || $this->__get('is_analyse') |
||
667 | || $this->__get('is_maint') || $this->__get('is_explain') |
||
668 | ) { |
||
669 | $displayParts = $this->_setDisplayPartsForNonData($displayParts); |
||
670 | } elseif ($this->__get('is_show')) { |
||
671 | $displayParts = $this->_setDisplayPartsForShow($displayParts); |
||
672 | } else { |
||
673 | $displayParts = $this->_setDisplayPartsForSelect($displayParts); |
||
674 | } // end if..elseif...else |
||
675 | |||
676 | // 3. Gets the total number of rows if it is unknown |
||
677 | if (isset($unlim_num_rows) && $unlim_num_rows != '') { |
||
678 | $the_total = $unlim_num_rows; |
||
679 | } elseif (($displayParts['nav_bar'] == '1') |
||
680 | || ($displayParts['sort_lnk'] == '1') |
||
681 | && (strlen($db) > 0 && strlen($table) > 0) |
||
682 | ) { |
||
683 | $the_total = $GLOBALS['dbi']->getTable($db, $table)->countRecords(); |
||
684 | } |
||
685 | |||
686 | // if for COUNT query, number of rows returned more than 1 |
||
687 | // (may be being used GROUP BY) |
||
688 | if ($this->__get('is_count') && isset($num_rows) && $num_rows > 1) { |
||
689 | $displayParts['nav_bar'] = (string) '1'; |
||
690 | $displayParts['sort_lnk'] = (string) '1'; |
||
691 | } |
||
692 | // 4. If navigation bar or sorting fields names URLs should be |
||
693 | // displayed but there is only one row, change these settings to |
||
694 | // false |
||
695 | if ($displayParts['nav_bar'] == '1' || $displayParts['sort_lnk'] == '1') { |
||
696 | // - Do not display sort links if less than 2 rows. |
||
697 | // - For a VIEW we (probably) did not count the number of rows |
||
698 | // so don't test this number here, it would remove the possibility |
||
699 | // of sorting VIEW results. |
||
700 | $_table = new Table($table, $db); |
||
701 | if (isset($unlim_num_rows) |
||
702 | && ($unlim_num_rows < 2) |
||
703 | && ! $_table->isView() |
||
704 | ) { |
||
705 | $displayParts['sort_lnk'] = (string) '0'; |
||
706 | } |
||
707 | } // end if (3) |
||
708 | |||
709 | return [ |
||
710 | $displayParts, |
||
711 | $the_total, |
||
712 | ]; |
||
713 | } |
||
714 | |||
715 | /** |
||
716 | * Return true if we are executing a query in the form of |
||
717 | * "SELECT * FROM <a table> ..." |
||
718 | * |
||
719 | * @param array $analyzed_sql_results analyzed sql results |
||
720 | * |
||
721 | * @return boolean |
||
722 | * |
||
723 | * @access private |
||
724 | * |
||
725 | * @see _getTableHeaders(), _getColumnParams() |
||
726 | */ |
||
727 | private function _isSelect(array $analyzed_sql_results) |
||
728 | { |
||
729 | return ! ($this->__get('is_count') |
||
730 | || $this->__get('is_export') |
||
731 | || $this->__get('is_func') |
||
732 | || $this->__get('is_analyse')) |
||
733 | && ! empty($analyzed_sql_results['select_from']) |
||
734 | && ! empty($analyzed_sql_results['statement']->from) |
||
735 | && (count($analyzed_sql_results['statement']->from) === 1) |
||
736 | && ! empty($analyzed_sql_results['statement']->from[0]->table); |
||
737 | } |
||
738 | |||
739 | /** |
||
740 | * Get a navigation button |
||
741 | * |
||
742 | * @param string $caption iconic caption for button |
||
743 | * @param string $title text for button |
||
744 | * @param integer $pos position for next query |
||
745 | * @param string $html_sql_query query ready for display |
||
746 | * @param boolean $back whether 'begin' or 'previous' |
||
747 | * @param string $onsubmit optional onsubmit clause |
||
748 | * @param string $input_for_real_end optional hidden field for special treatment |
||
749 | * @param string $onclick optional onclick clause |
||
750 | * |
||
751 | * @return string html content |
||
752 | * |
||
753 | * @access private |
||
754 | * |
||
755 | * @see _getMoveBackwardButtonsForTableNavigation(), |
||
756 | * _getMoveForwardButtonsForTableNavigation() |
||
757 | */ |
||
758 | private function _getTableNavigationButton( |
||
759 | $caption, |
||
760 | $title, |
||
761 | $pos, |
||
762 | $html_sql_query, |
||
763 | $back, |
||
764 | $onsubmit = '', |
||
765 | $input_for_real_end = '', |
||
766 | $onclick = '' |
||
767 | ) { |
||
768 | $caption_output = ''; |
||
769 | if ($back) { |
||
770 | if (Util::showIcons('TableNavigationLinksMode')) { |
||
771 | $caption_output .= $caption; |
||
772 | } |
||
773 | if (Util::showText('TableNavigationLinksMode')) { |
||
774 | $caption_output .= ' ' . $title; |
||
775 | } |
||
776 | } else { |
||
777 | if (Util::showText('TableNavigationLinksMode')) { |
||
778 | $caption_output .= $title; |
||
779 | } |
||
780 | if (Util::showIcons('TableNavigationLinksMode')) { |
||
781 | $caption_output .= ' ' . $caption; |
||
782 | } |
||
783 | } |
||
784 | |||
785 | return $this->template->render('display/results/table_navigation_button', [ |
||
786 | 'db' => $this->__get('db'), |
||
787 | 'table' => $this->__get('table'), |
||
788 | 'sql_query' => $html_sql_query, |
||
789 | 'pos' => $pos, |
||
790 | 'is_browse_distinct' => $this->__get('is_browse_distinct'), |
||
791 | 'goto' => $this->__get('goto'), |
||
792 | 'input_for_real_end' => $input_for_real_end, |
||
793 | 'caption_output' => $caption_output, |
||
794 | 'title' => $title, |
||
795 | 'onsubmit' => $onsubmit, |
||
796 | 'onclick' => $onclick, |
||
797 | ]); |
||
798 | } |
||
799 | |||
800 | /** |
||
801 | * Possibly return a page selector for table navigation |
||
802 | * |
||
803 | * @return array ($output, $nbTotalPage) |
||
804 | * |
||
805 | * @access private |
||
806 | */ |
||
807 | private function _getHtmlPageSelector(): array |
||
808 | { |
||
809 | $pageNow = @floor( |
||
810 | $_SESSION['tmpval']['pos'] |
||
811 | / $_SESSION['tmpval']['max_rows'] |
||
812 | ) + 1; |
||
813 | |||
814 | $nbTotalPage = @ceil( |
||
815 | $this->__get('unlim_num_rows') |
||
816 | / $_SESSION['tmpval']['max_rows'] |
||
817 | ); |
||
818 | |||
819 | $output = ''; |
||
820 | if ($nbTotalPage > 1) { |
||
821 | $_url_params = [ |
||
822 | 'db' => $this->__get('db'), |
||
823 | 'table' => $this->__get('table'), |
||
824 | 'sql_query' => $this->__get('sql_query'), |
||
825 | 'goto' => $this->__get('goto'), |
||
826 | 'is_browse_distinct' => $this->__get('is_browse_distinct'), |
||
827 | ]; |
||
828 | |||
829 | $output = $this->template->render('display/results/page_selector', [ |
||
830 | 'url_params' => $_url_params, |
||
831 | 'page_selector' => Util::pageselector( |
||
832 | 'pos', |
||
833 | $_SESSION['tmpval']['max_rows'], |
||
834 | $pageNow, |
||
835 | $nbTotalPage, |
||
836 | 200, |
||
837 | 5, |
||
838 | 5, |
||
839 | 20, |
||
840 | 10 |
||
841 | ), |
||
842 | ]); |
||
843 | } |
||
844 | return [ |
||
845 | $output, |
||
846 | $nbTotalPage, |
||
847 | ]; |
||
848 | } |
||
849 | |||
850 | /** |
||
851 | * Get a navigation bar to browse among the results of a SQL query |
||
852 | * |
||
853 | * @param integer $posNext the offset for the "next" page |
||
854 | * @param integer $posPrevious the offset for the "previous" page |
||
855 | * @param boolean $isInnodb whether its InnoDB or not |
||
856 | * @param string $sortByKeyHtml the sort by key dialog |
||
857 | * |
||
858 | * @return string html content |
||
859 | * |
||
860 | * @access private |
||
861 | * |
||
862 | * @see getTable() |
||
863 | */ |
||
864 | private function _getTableNavigation( |
||
865 | $posNext, |
||
866 | $posPrevious, |
||
867 | $isInnodb, |
||
868 | $sortByKeyHtml |
||
869 | ): string { |
||
870 | $isShowingAll = $_SESSION['tmpval']['max_rows'] === self::ALL_ROWS; |
||
871 | |||
872 | // Move to the beginning or to the previous page |
||
873 | $moveBackwardButtons = ''; |
||
874 | if ($_SESSION['tmpval']['pos'] && ! $isShowingAll) { |
||
875 | $moveBackwardButtons = $this->_getMoveBackwardButtonsForTableNavigation( |
||
876 | htmlspecialchars($this->__get('sql_query')), |
||
877 | $posPrevious |
||
878 | ); |
||
879 | } |
||
880 | |||
881 | $pageSelector = ''; |
||
882 | $numberTotalPage = 1; |
||
883 | if (! $isShowingAll) { |
||
884 | list( |
||
885 | $pageSelector, |
||
886 | $numberTotalPage |
||
887 | ) = $this->_getHtmlPageSelector(); |
||
888 | } |
||
889 | |||
890 | // Move to the next page or to the last one |
||
891 | $moveForwardButtons = ''; |
||
892 | if ($this->__get('unlim_num_rows') === false // view with unknown number of rows |
||
893 | || (! $isShowingAll |
||
894 | && $_SESSION['tmpval']['pos'] + $_SESSION['tmpval']['max_rows'] < $this->__get('unlim_num_rows') |
||
895 | && $this->__get('num_rows') >= $_SESSION['tmpval']['max_rows']) |
||
896 | ) { |
||
897 | $moveForwardButtons = $this->_getMoveForwardButtonsForTableNavigation( |
||
898 | htmlspecialchars($this->__get('sql_query')), |
||
899 | $posNext, |
||
900 | $isInnodb |
||
901 | ); |
||
902 | } |
||
903 | |||
904 | $hiddenFields = [ |
||
905 | 'db' => $this->__get('db'), |
||
906 | 'table' => $this->__get('table'), |
||
907 | 'server' => $this->__get('server'), |
||
908 | 'sql_query' => $this->__get('sql_query'), |
||
909 | 'is_browse_distinct' => $this->__get('is_browse_distinct'), |
||
910 | 'goto' => $this->__get('goto'), |
||
911 | ]; |
||
912 | |||
913 | return $this->template->render('display/results/table_navigation', [ |
||
914 | 'move_backward_buttons' => $moveBackwardButtons, |
||
915 | 'page_selector' => $pageSelector, |
||
916 | 'move_forward_buttons' => $moveForwardButtons, |
||
917 | 'number_total_page' => $numberTotalPage, |
||
918 | 'has_show_all' => $GLOBALS['cfg']['ShowAll'] || ($this->__get('unlim_num_rows') <= 500), |
||
919 | 'hidden_fields' => $hiddenFields, |
||
920 | 'session_max_rows' => $isShowingAll ? $GLOBALS['cfg']['MaxRows'] : 'all', |
||
921 | 'unique_id' => $this->__get('unique_id'), |
||
922 | 'is_showing_all' => $isShowingAll, |
||
923 | 'unlim_num_rows' => $this->__get('unlim_num_rows'), |
||
924 | 'max_rows' => $_SESSION['tmpval']['max_rows'], |
||
925 | 'pos' => $_SESSION['tmpval']['pos'], |
||
926 | 'sort_by_key' => $sortByKeyHtml, |
||
927 | ]); |
||
928 | } |
||
929 | |||
930 | /** |
||
931 | * Prepare move backward buttons - previous and first |
||
932 | * |
||
933 | * @param string $html_sql_query the sql encoded by html special characters |
||
934 | * @param integer $pos_prev the offset for the "previous" page |
||
935 | * |
||
936 | * @return string html content |
||
937 | * |
||
938 | * @access private |
||
939 | * |
||
940 | * @see _getTableNavigation() |
||
941 | */ |
||
942 | private function _getMoveBackwardButtonsForTableNavigation( |
||
943 | $html_sql_query, |
||
944 | $pos_prev |
||
945 | ) { |
||
946 | return $this->_getTableNavigationButton( |
||
947 | '<<', |
||
948 | _pgettext('First page', 'Begin'), |
||
949 | 0, |
||
950 | $html_sql_query, |
||
951 | true |
||
952 | ) |
||
953 | . $this->_getTableNavigationButton( |
||
954 | '<', |
||
955 | _pgettext('Previous page', 'Previous'), |
||
956 | $pos_prev, |
||
957 | $html_sql_query, |
||
958 | true |
||
959 | ); |
||
960 | } |
||
961 | |||
962 | /** |
||
963 | * Prepare move forward buttons - next and last |
||
964 | * |
||
965 | * @param string $html_sql_query the sql encoded by htmlspecialchars() |
||
966 | * @param integer $pos_next the offset for the "next" page |
||
967 | * @param boolean $is_innodb whether it's InnoDB or not |
||
968 | * |
||
969 | * @return string html content |
||
970 | * |
||
971 | * @access private |
||
972 | * |
||
973 | * @see _getTableNavigation() |
||
974 | */ |
||
975 | private function _getMoveForwardButtonsForTableNavigation( |
||
976 | $html_sql_query, |
||
977 | $pos_next, |
||
978 | $is_innodb |
||
979 | ) { |
||
980 | // display the Next button |
||
981 | $buttons_html = $this->_getTableNavigationButton( |
||
982 | '>', |
||
983 | _pgettext('Next page', 'Next'), |
||
984 | $pos_next, |
||
985 | $html_sql_query, |
||
986 | false |
||
987 | ); |
||
988 | |||
989 | // prepare some options for the End button |
||
990 | if ($is_innodb |
||
991 | && $this->__get('unlim_num_rows') > $GLOBALS['cfg']['MaxExactCount'] |
||
992 | ) { |
||
993 | $input_for_real_end = '<input id="real_end_input" type="hidden" ' |
||
994 | . 'name="find_real_end" value="1">'; |
||
995 | // no backquote around this message |
||
996 | $onclick = ''; |
||
997 | } else { |
||
998 | $input_for_real_end = $onclick = ''; |
||
999 | } |
||
1000 | |||
1001 | $maxRows = $_SESSION['tmpval']['max_rows']; |
||
1002 | $onsubmit = 'onsubmit="return ' |
||
1003 | . ($_SESSION['tmpval']['pos'] |
||
1004 | + $maxRows |
||
1005 | < $this->__get('unlim_num_rows') |
||
1006 | && $this->__get('num_rows') >= $maxRows |
||
1007 | ? 'true' |
||
1008 | : 'false') . '"'; |
||
1009 | |||
1010 | // display the End button |
||
1011 | $buttons_html .= $this->_getTableNavigationButton( |
||
1012 | '>>', |
||
1013 | _pgettext('Last page', 'End'), |
||
1014 | @((ceil( |
||
1015 | $this->__get('unlim_num_rows') |
||
1016 | / $_SESSION['tmpval']['max_rows'] |
||
1017 | ) - 1) * $maxRows), |
||
1018 | $html_sql_query, |
||
1019 | false, |
||
1020 | $onsubmit, |
||
1021 | $input_for_real_end, |
||
1022 | $onclick |
||
1023 | ); |
||
1024 | |||
1025 | return $buttons_html; |
||
1026 | } |
||
1027 | |||
1028 | /** |
||
1029 | * Get the headers of the results table, for all of the columns |
||
1030 | * |
||
1031 | * @param array $displayParts which elements to display |
||
1032 | * @param array $analyzed_sql_results analyzed sql results |
||
1033 | * @param array $sort_expression sort expression |
||
1034 | * @param array $sort_expression_nodirection sort expression |
||
1035 | * without direction |
||
1036 | * @param array $sort_direction sort direction |
||
1037 | * @param boolean $is_limited_display with limited operations |
||
1038 | * or not |
||
1039 | * @param string $unsorted_sql_query query without the sort part |
||
1040 | * |
||
1041 | * @return string html content |
||
1042 | * |
||
1043 | * @access private |
||
1044 | * |
||
1045 | * @see getTableHeaders() |
||
1046 | */ |
||
1047 | private function _getTableHeadersForColumns( |
||
1048 | array $displayParts, |
||
1049 | array $analyzed_sql_results, |
||
1050 | array $sort_expression, |
||
1051 | array $sort_expression_nodirection, |
||
1052 | array $sort_direction, |
||
1053 | $is_limited_display, |
||
1054 | $unsorted_sql_query |
||
1055 | ) { |
||
1056 | $html = ''; |
||
1057 | |||
1058 | // required to generate sort links that will remember whether the |
||
1059 | // "Show all" button has been clicked |
||
1060 | $sql_md5 = md5($this->__get('sql_query')); |
||
1061 | $session_max_rows = $is_limited_display |
||
1062 | ? 0 |
||
1063 | : $_SESSION['tmpval']['query'][$sql_md5]['max_rows']; |
||
1064 | |||
1065 | // Following variable are needed for use in isset/empty or |
||
1066 | // use with array indexes/safe use in the for loop |
||
1067 | $highlight_columns = $this->__get('highlight_columns'); |
||
1068 | $fields_meta = $this->__get('fields_meta'); |
||
1069 | |||
1070 | // Prepare Display column comments if enabled |
||
1071 | // ($GLOBALS['cfg']['ShowBrowseComments']). |
||
1072 | $comments_map = $this->_getTableCommentsArray($analyzed_sql_results); |
||
1073 | |||
1074 | list($col_order, $col_visib) = $this->_getColumnParams( |
||
1075 | $analyzed_sql_results |
||
1076 | ); |
||
1077 | |||
1078 | // optimize: avoid calling a method on each iteration |
||
1079 | $number_of_columns = $this->__get('fields_cnt'); |
||
1080 | |||
1081 | for ($j = 0; $j < $number_of_columns; $j++) { |
||
1082 | // assign $i with the appropriate column order |
||
1083 | $i = $col_order ? $col_order[$j] : $j; |
||
1084 | |||
1085 | // See if this column should get highlight because it's used in the |
||
1086 | // where-query. |
||
1087 | $name = $fields_meta[$i]->name; |
||
1088 | $condition_field = isset($highlight_columns[$name]) |
||
1089 | || isset($highlight_columns[Util::backquote($name)]) |
||
1090 | ? true |
||
1091 | : false; |
||
1092 | |||
1093 | // Prepare comment-HTML-wrappers for each row, if defined/enabled. |
||
1094 | $comments = $this->_getCommentForRow($comments_map, $fields_meta[$i]); |
||
1095 | $display_params = $this->__get('display_params'); |
||
1096 | |||
1097 | if (($displayParts['sort_lnk'] == '1') && ! $is_limited_display) { |
||
1098 | list($order_link, $sorted_header_html) |
||
1099 | = $this->_getOrderLinkAndSortedHeaderHtml( |
||
1100 | $fields_meta[$i], |
||
1101 | $sort_expression, |
||
1102 | $sort_expression_nodirection, |
||
1103 | $i, |
||
1104 | $unsorted_sql_query, |
||
1105 | $session_max_rows, |
||
1106 | $comments, |
||
1107 | $sort_direction, |
||
1108 | $col_visib, |
||
1109 | $col_visib[$j] |
||
1110 | ); |
||
1111 | |||
1112 | $html .= $sorted_header_html; |
||
1113 | |||
1114 | $display_params['desc'][] = ' <th ' |
||
1115 | . 'class="draggable' |
||
1116 | . ($condition_field ? ' condition' : '') |
||
1117 | . '" data-column="' . htmlspecialchars($fields_meta[$i]->name) |
||
1118 | . '">' . "\n" . $order_link . $comments . ' </th>' . "\n"; |
||
1119 | } else { |
||
1120 | // Results can't be sorted |
||
1121 | $html |
||
1122 | .= $this->_getDraggableClassForNonSortableColumns( |
||
1123 | $col_visib, |
||
1124 | $col_visib[$j], |
||
1125 | $condition_field, |
||
1126 | $fields_meta[$i], |
||
1127 | $comments |
||
1128 | ); |
||
1129 | |||
1130 | $display_params['desc'][] = ' <th ' |
||
1131 | . 'class="draggable' |
||
1132 | . ($condition_field ? ' condition"' : '') |
||
1133 | . '" data-column="' . htmlspecialchars((string) $fields_meta[$i]->name) |
||
1134 | . '"> ' |
||
1135 | . htmlspecialchars((string) $fields_meta[$i]->name) |
||
1136 | . $comments . ' </th>'; |
||
1137 | } // end else |
||
1138 | |||
1139 | $this->__set('display_params', $display_params); |
||
1140 | } // end for |
||
1141 | return $html; |
||
1142 | } |
||
1143 | |||
1144 | /** |
||
1145 | * Get the headers of the results table |
||
1146 | * |
||
1147 | * @param array $displayParts which elements to display |
||
1148 | * @param array $analyzedSqlResults analyzed sql results |
||
1149 | * @param string $unsortedSqlQuery the unsorted sql query |
||
1150 | * @param array $sortExpression sort expression |
||
1151 | * @param array|string $sortExpressionNoDirection sort expression without direction |
||
1152 | * @param array $sortDirection sort direction |
||
1153 | * @param boolean $isLimitedDisplay with limited operations or not |
||
1154 | * |
||
1155 | * @return string html content |
||
1156 | * |
||
1157 | * @access private |
||
1158 | * |
||
1159 | * @see getTable() |
||
1160 | */ |
||
1161 | private function _getTableHeaders( |
||
1162 | array &$displayParts, |
||
1163 | array $analyzedSqlResults, |
||
1164 | $unsortedSqlQuery, |
||
1165 | array $sortExpression = [], |
||
1166 | $sortExpressionNoDirection = '', |
||
1167 | array $sortDirection = [], |
||
1168 | $isLimitedDisplay = false |
||
1169 | ): string { |
||
1170 | // Needed for use in isset/empty or |
||
1171 | // use with array indexes/safe use in foreach |
||
1172 | $printView = $this->__get('printview'); |
||
1173 | $displayParams = $this->__get('display_params'); |
||
1174 | |||
1175 | // Output data needed for column reordering and show/hide column |
||
1176 | $dataForResettingColumnOrder = $this->_getDataForResettingColumnOrder($analyzedSqlResults); |
||
1177 | |||
1178 | $displayParams['emptypre'] = 0; |
||
1179 | $displayParams['emptyafter'] = 0; |
||
1180 | $displayParams['textbtn'] = ''; |
||
1181 | $fullOrPartialTextLink = ''; |
||
1182 | |||
1183 | $this->__set('display_params', $displayParams); |
||
1184 | |||
1185 | // Display options (if we are not in print view) |
||
1186 | $optionsBlock = ''; |
||
1187 | if (! (isset($printView) && ($printView == '1')) && ! $isLimitedDisplay) { |
||
1188 | $optionsBlock = $this->_getOptionsBlock(); |
||
1189 | |||
1190 | // prepare full/partial text button or link |
||
1191 | $fullOrPartialTextLink = $this->_getFullOrPartialTextButtonOrLink(); |
||
1192 | } |
||
1193 | |||
1194 | // 1. Set $colspan and generate html with full/partial |
||
1195 | // text button or link |
||
1196 | list($colspan, $buttonHtml) = $this->_getFieldVisibilityParams( |
||
1197 | $displayParts, |
||
1198 | $fullOrPartialTextLink |
||
1199 | ); |
||
1200 | |||
1201 | // 2. Displays the fields' name |
||
1202 | // 2.0 If sorting links should be used, checks if the query is a "JOIN" |
||
1203 | // statement (see 2.1.3) |
||
1204 | |||
1205 | // See if we have to highlight any header fields of a WHERE query. |
||
1206 | // Uses SQL-Parser results. |
||
1207 | $this->_setHighlightedColumnGlobalField($analyzedSqlResults); |
||
1208 | |||
1209 | // Get the headers for all of the columns |
||
1210 | $tableHeadersForColumns = $this->_getTableHeadersForColumns( |
||
1211 | $displayParts, |
||
1212 | $analyzedSqlResults, |
||
1213 | $sortExpression, |
||
1214 | $sortExpressionNoDirection, |
||
1215 | $sortDirection, |
||
1216 | $isLimitedDisplay, |
||
1217 | $unsortedSqlQuery |
||
1218 | ); |
||
1219 | |||
1220 | // Display column at rightside - checkboxes or empty column |
||
1221 | $columnAtRightSide = ''; |
||
1222 | if (! $printView) { |
||
1223 | $columnAtRightSide = $this->_getColumnAtRightSide( |
||
1224 | $displayParts, |
||
1225 | $fullOrPartialTextLink, |
||
1226 | $colspan |
||
1227 | ); |
||
1228 | } |
||
1229 | |||
1230 | return $this->template->render('display/results/table_headers', [ |
||
1231 | 'db' => $this->__get('db'), |
||
1232 | 'table' => $this->__get('table'), |
||
1233 | 'unique_id' => $this->__get('unique_id'), |
||
1234 | 'save_cells_at_once' => $GLOBALS['cfg']['SaveCellsAtOnce'], |
||
1235 | 'data_for_resetting_column_order' => $dataForResettingColumnOrder, |
||
1236 | 'options_block' => $optionsBlock, |
||
1237 | 'delete_link' => $displayParts['del_lnk'], |
||
1238 | 'delete_row' => self::DELETE_ROW, |
||
1239 | 'kill_process' => self::KILL_PROCESS, |
||
1240 | 'button' => $buttonHtml, |
||
1241 | 'table_headers_for_columns' => $tableHeadersForColumns, |
||
1242 | 'column_at_right_side' => $columnAtRightSide, |
||
1243 | ]); |
||
1244 | } |
||
1245 | |||
1246 | /** |
||
1247 | * Prepare unsorted sql query and sort by key drop down |
||
1248 | * |
||
1249 | * @param array $analyzed_sql_results analyzed sql results |
||
1250 | * @param array|null $sort_expression sort expression |
||
1251 | * |
||
1252 | * @return array two element array - $unsorted_sql_query, $drop_down_html |
||
1253 | * |
||
1254 | * @access private |
||
1255 | * |
||
1256 | * @see _getTableHeaders() |
||
1257 | */ |
||
1258 | private function _getUnsortedSqlAndSortByKeyDropDown( |
||
1259 | array $analyzed_sql_results, |
||
1260 | ?array $sort_expression |
||
1261 | ) { |
||
1262 | $drop_down_html = ''; |
||
1263 | |||
1264 | $unsorted_sql_query = Query::replaceClause( |
||
1265 | $analyzed_sql_results['statement'], |
||
1266 | $analyzed_sql_results['parser']->list, |
||
1267 | 'ORDER BY', |
||
1268 | '' |
||
1269 | ); |
||
1270 | |||
1271 | // Data is sorted by indexes only if it there is only one table. |
||
1272 | if ($this->_isSelect($analyzed_sql_results)) { |
||
1273 | // grab indexes data: |
||
1274 | $indexes = Index::getFromTable( |
||
1275 | $this->__get('table'), |
||
1276 | $this->__get('db') |
||
1277 | ); |
||
1278 | |||
1279 | // do we have any index? |
||
1280 | if (! empty($indexes)) { |
||
1281 | $drop_down_html = $this->_getSortByKeyDropDown( |
||
1282 | $indexes, |
||
1283 | $sort_expression, |
||
1284 | $unsorted_sql_query |
||
1285 | ); |
||
1286 | } |
||
1287 | } |
||
1288 | |||
1289 | return [ |
||
1290 | $unsorted_sql_query, |
||
1291 | $drop_down_html, |
||
1292 | ]; |
||
1293 | } |
||
1294 | |||
1295 | /** |
||
1296 | * Prepare sort by key dropdown - html code segment |
||
1297 | * |
||
1298 | * @param Index[] $indexes the indexes of the table for sort criteria |
||
1299 | * @param array|null $sortExpression the sort expression |
||
1300 | * @param string $unsortedSqlQuery the unsorted sql query |
||
1301 | * |
||
1302 | * @return string html content |
||
1303 | * |
||
1304 | * @access private |
||
1305 | * |
||
1306 | * @see _getTableHeaders() |
||
1307 | */ |
||
1308 | private function _getSortByKeyDropDown( |
||
1309 | $indexes, |
||
1310 | ?array $sortExpression, |
||
1311 | $unsortedSqlQuery |
||
1312 | ): string { |
||
1313 | $hiddenFields = [ |
||
1314 | 'db' => $this->__get('db'), |
||
1315 | 'table' => $this->__get('table'), |
||
1316 | 'server' => $this->__get('server'), |
||
1317 | 'sort_by_key' => '1', |
||
1318 | ]; |
||
1319 | |||
1320 | $isIndexUsed = false; |
||
1321 | $localOrder = is_array($sortExpression) ? implode(', ', $sortExpression) : ''; |
||
1322 | |||
1323 | $options = []; |
||
1324 | foreach ($indexes as $index) { |
||
1325 | $ascSort = '`' |
||
1326 | . implode('` ASC, `', array_keys($index->getColumns())) |
||
1327 | . '` ASC'; |
||
1328 | |||
1329 | $descSort = '`' |
||
1330 | . implode('` DESC, `', array_keys($index->getColumns())) |
||
1331 | . '` DESC'; |
||
1332 | |||
1333 | $isIndexUsed = $isIndexUsed |
||
1334 | || $localOrder === $ascSort |
||
1335 | || $localOrder === $descSort; |
||
1336 | |||
1337 | $unsortedSqlQueryFirstPart = $unsortedSqlQuery; |
||
1338 | $unsortedSqlQuerySecondPart = ''; |
||
1339 | if (preg_match( |
||
1340 | '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|' |
||
1341 | . 'FOR UPDATE|LOCK IN SHARE MODE))@is', |
||
1342 | $unsortedSqlQuery, |
||
1343 | $myReg |
||
1344 | )) { |
||
1345 | $unsortedSqlQueryFirstPart = $myReg[1]; |
||
1346 | $unsortedSqlQuerySecondPart = $myReg[2]; |
||
1347 | } |
||
1348 | |||
1349 | $options[] = [ |
||
1350 | 'value' => $unsortedSqlQueryFirstPart . ' ORDER BY ' |
||
1351 | . $ascSort . $unsortedSqlQuerySecondPart, |
||
1352 | 'content' => $index->getName() . ' (ASC)', |
||
1353 | 'is_selected' => $localOrder === $ascSort, |
||
1354 | ]; |
||
1355 | $options[] = [ |
||
1356 | 'value' => $unsortedSqlQueryFirstPart . ' ORDER BY ' |
||
1357 | . $descSort . $unsortedSqlQuerySecondPart, |
||
1358 | 'content' => $index->getName() . ' (DESC)', |
||
1359 | 'is_selected' => $localOrder === $descSort, |
||
1360 | ]; |
||
1361 | } |
||
1362 | $options[] = [ |
||
1363 | 'value' => $unsortedSqlQuery, |
||
1364 | 'content' => __('None'), |
||
1365 | 'is_selected' => ! $isIndexUsed, |
||
1366 | ]; |
||
1367 | |||
1368 | return $this->template->render('display/results/sort_by_key', [ |
||
1369 | 'hidden_fields' => $hiddenFields, |
||
1370 | 'options' => $options, |
||
1371 | ]); |
||
1372 | } |
||
1373 | |||
1374 | /** |
||
1375 | * Set column span, row span and prepare html with full/partial |
||
1376 | * text button or link |
||
1377 | * |
||
1378 | * @param array $displayParts which elements to display |
||
1379 | * @param string $full_or_partial_text_link full/partial link or text button |
||
1380 | * |
||
1381 | * @return array 2 element array - $colspan, $button_html |
||
1382 | * |
||
1383 | * @access private |
||
1384 | * |
||
1385 | * @see _getTableHeaders() |
||
1386 | */ |
||
1387 | private function _getFieldVisibilityParams( |
||
1388 | array &$displayParts, |
||
1389 | $full_or_partial_text_link |
||
1390 | ) { |
||
1391 | |||
1392 | $button_html = ''; |
||
1393 | $display_params = $this->__get('display_params'); |
||
1394 | |||
1395 | // 1. Displays the full/partial text button (part 1)... |
||
1396 | $button_html .= '<thead><tr>' . "\n"; |
||
1397 | |||
1398 | $emptyPreCondition = $displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE |
||
1399 | && $displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE; |
||
1400 | |||
1401 | $colspan = $emptyPreCondition ? ' colspan="4"' |
||
1402 | : ''; |
||
1403 | |||
1404 | $leftOrBoth = $GLOBALS['cfg']['RowActionLinks'] === self::POSITION_LEFT |
||
1405 | || $GLOBALS['cfg']['RowActionLinks'] === self::POSITION_BOTH; |
||
1406 | |||
1407 | // ... before the result table |
||
1408 | if (($displayParts['edit_lnk'] == self::NO_EDIT_OR_DELETE) |
||
1409 | && ($displayParts['del_lnk'] == self::NO_EDIT_OR_DELETE) |
||
1410 | && ($displayParts['text_btn'] == '1') |
||
1411 | ) { |
||
1412 | $display_params['emptypre'] = $emptyPreCondition ? 4 : 0; |
||
1413 | } elseif ($leftOrBoth && ($displayParts['text_btn'] == '1') |
||
1414 | ) { |
||
1415 | // ... at the left column of the result table header if possible |
||
1416 | // and required |
||
1417 | |||
1418 | $display_params['emptypre'] = $emptyPreCondition ? 4 : 0; |
||
1419 | |||
1420 | $button_html .= '<th class="column_action print_ignore" ' . $colspan |
||
1421 | . '>' . $full_or_partial_text_link . '</th>'; |
||
1422 | } elseif ($leftOrBoth |
||
1423 | && (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
1424 | || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) |
||
1425 | ) { |
||
1426 | // ... elseif no button, displays empty(ies) col(s) if required |
||
1427 | |||
1428 | $display_params['emptypre'] = $emptyPreCondition ? 4 : 0; |
||
1429 | |||
1430 | $button_html .= '<td ' . $colspan . '></td>'; |
||
1431 | } elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) { |
||
1432 | // ... elseif display an empty column if the actions links are |
||
1433 | // disabled to match the rest of the table |
||
1434 | $button_html .= '<th class="column_action"></th>'; |
||
1435 | } |
||
1436 | |||
1437 | $this->__set('display_params', $display_params); |
||
1438 | |||
1439 | return [ |
||
1440 | $colspan, |
||
1441 | $button_html, |
||
1442 | ]; |
||
1443 | } |
||
1444 | |||
1445 | /** |
||
1446 | * Get table comments as array |
||
1447 | * |
||
1448 | * @param array $analyzed_sql_results analyzed sql results |
||
1449 | * |
||
1450 | * @return array table comments |
||
1451 | * |
||
1452 | * @access private |
||
1453 | * |
||
1454 | * @see _getTableHeaders() |
||
1455 | */ |
||
1456 | private function _getTableCommentsArray(array $analyzed_sql_results) |
||
1457 | { |
||
1458 | if (! $GLOBALS['cfg']['ShowBrowseComments'] |
||
1459 | || empty($analyzed_sql_results['statement']->from) |
||
1460 | ) { |
||
1461 | return []; |
||
1462 | } |
||
1463 | |||
1464 | $ret = []; |
||
1465 | foreach ($analyzed_sql_results['statement']->from as $field) { |
||
1466 | if (empty($field->table)) { |
||
1467 | continue; |
||
1468 | } |
||
1469 | $ret[$field->table] = $this->relation->getComments( |
||
1470 | empty($field->database) ? $this->__get('db') : $field->database, |
||
1471 | $field->table |
||
1472 | ); |
||
1473 | } |
||
1474 | |||
1475 | return $ret; |
||
1476 | } |
||
1477 | |||
1478 | /** |
||
1479 | * Set global array for store highlighted header fields |
||
1480 | * |
||
1481 | * @param array $analyzed_sql_results analyzed sql results |
||
1482 | * |
||
1483 | * @return void |
||
1484 | * |
||
1485 | * @access private |
||
1486 | * |
||
1487 | * @see _getTableHeaders() |
||
1488 | */ |
||
1489 | private function _setHighlightedColumnGlobalField(array $analyzed_sql_results) |
||
1490 | { |
||
1491 | $highlight_columns = []; |
||
1492 | |||
1493 | if (! empty($analyzed_sql_results['statement']->where)) { |
||
1494 | foreach ($analyzed_sql_results['statement']->where as $expr) { |
||
1495 | foreach ($expr->identifiers as $identifier) { |
||
1496 | $highlight_columns[$identifier] = 'true'; |
||
1497 | } |
||
1498 | } |
||
1499 | } |
||
1500 | |||
1501 | $this->__set('highlight_columns', $highlight_columns); |
||
1502 | } |
||
1503 | |||
1504 | /** |
||
1505 | * Prepare data for column restoring and show/hide |
||
1506 | * |
||
1507 | * @param array $analyzedSqlResults analyzed sql results |
||
1508 | * |
||
1509 | * @return string html content |
||
1510 | * |
||
1511 | * @access private |
||
1512 | * |
||
1513 | * @see _getTableHeaders() |
||
1514 | */ |
||
1515 | private function _getDataForResettingColumnOrder(array $analyzedSqlResults): string |
||
1516 | { |
||
1517 | if (! $this->_isSelect($analyzedSqlResults)) { |
||
1518 | return ''; |
||
1519 | } |
||
1520 | |||
1521 | list($columnOrder, $columnVisibility) = $this->_getColumnParams( |
||
1522 | $analyzedSqlResults |
||
1523 | ); |
||
1524 | |||
1525 | $tableCreateTime = ''; |
||
1526 | $table = new Table($this->__get('table'), $this->__get('db')); |
||
1527 | if (! $table->isView()) { |
||
1528 | $tableCreateTime = $GLOBALS['dbi']->getTable( |
||
1529 | $this->__get('db'), |
||
1530 | $this->__get('table') |
||
1531 | )->getStatusInfo('Create_time'); |
||
1532 | } |
||
1533 | |||
1534 | return $this->template->render('display/results/data_for_resetting_column_order', [ |
||
1535 | 'column_order' => $columnOrder, |
||
1536 | 'column_visibility' => $columnVisibility, |
||
1537 | 'is_view' => $table->isView(), |
||
1538 | 'table_create_time' => $tableCreateTime, |
||
1539 | ]); |
||
1540 | } |
||
1541 | |||
1542 | /** |
||
1543 | * Prepare option fields block |
||
1544 | * |
||
1545 | * @return string html content |
||
1546 | * |
||
1547 | * @access private |
||
1548 | * |
||
1549 | * @see _getTableHeaders() |
||
1550 | */ |
||
1551 | private function _getOptionsBlock() |
||
1552 | { |
||
1553 | if (isset($_SESSION['tmpval']['possible_as_geometry']) && $_SESSION['tmpval']['possible_as_geometry'] == false) { |
||
1554 | if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_GEOM) { |
||
1555 | $_SESSION['tmpval']['geoOption'] = self::GEOMETRY_DISP_WKT; |
||
1556 | } |
||
1557 | } |
||
1558 | return $this->template->render('display/results/options_block', [ |
||
1559 | 'unique_id' => $this->__get('unique_id'), |
||
1560 | 'geo_option' => $_SESSION['tmpval']['geoOption'], |
||
1561 | 'hide_transformation' => $_SESSION['tmpval']['hide_transformation'], |
||
1562 | 'display_blob' => $_SESSION['tmpval']['display_blob'], |
||
1563 | 'display_binary' => $_SESSION['tmpval']['display_binary'], |
||
1564 | 'relational_display' => $_SESSION['tmpval']['relational_display'], |
||
1565 | 'displaywork' => $GLOBALS['cfgRelation']['displaywork'], |
||
1566 | 'relwork' => $GLOBALS['cfgRelation']['relwork'], |
||
1567 | 'possible_as_geometry' => $_SESSION['tmpval']['possible_as_geometry'], |
||
1568 | 'pftext' => $_SESSION['tmpval']['pftext'], |
||
1569 | 'db' => $this->__get('db'), |
||
1570 | 'table' => $this->__get('table'), |
||
1571 | 'sql_query' => $this->__get('sql_query'), |
||
1572 | 'goto' => $this->__get('goto'), |
||
1573 | 'default_sliders_state' => $GLOBALS['cfg']['InitialSlidersState'], |
||
1574 | ]); |
||
1575 | } |
||
1576 | |||
1577 | /** |
||
1578 | * Get full/partial text button or link |
||
1579 | * |
||
1580 | * @return string html content |
||
1581 | * |
||
1582 | * @access private |
||
1583 | * |
||
1584 | * @see _getTableHeaders() |
||
1585 | */ |
||
1586 | private function _getFullOrPartialTextButtonOrLink() |
||
1587 | { |
||
1588 | |||
1589 | $url_params_full_text = [ |
||
1590 | 'db' => $this->__get('db'), |
||
1591 | 'table' => $this->__get('table'), |
||
1592 | 'sql_query' => $this->__get('sql_query'), |
||
1593 | 'goto' => $this->__get('goto'), |
||
1594 | 'full_text_button' => 1, |
||
1595 | ]; |
||
1596 | |||
1597 | if ($_SESSION['tmpval']['pftext'] == self::DISPLAY_FULL_TEXT) { |
||
1598 | // currently in fulltext mode so show the opposite link |
||
1599 | $tmp_image_file = $this->__get('pma_theme_image') . 's_partialtext.png'; |
||
1600 | $tmp_txt = __('Partial texts'); |
||
1601 | $url_params_full_text['pftext'] = self::DISPLAY_PARTIAL_TEXT; |
||
1602 | } else { |
||
1603 | $tmp_image_file = $this->__get('pma_theme_image') . 's_fulltext.png'; |
||
1604 | $tmp_txt = __('Full texts'); |
||
1605 | $url_params_full_text['pftext'] = self::DISPLAY_FULL_TEXT; |
||
1606 | } |
||
1607 | |||
1608 | $tmp_image = '<img class="fulltext" src="' . $tmp_image_file . '" alt="' |
||
1609 | . $tmp_txt . '" title="' . $tmp_txt . '">'; |
||
1610 | $tmp_url = 'sql.php' . Url::getCommon($url_params_full_text); |
||
1611 | |||
1612 | return Util::linkOrButton($tmp_url, $tmp_image); |
||
1613 | } |
||
1614 | |||
1615 | /** |
||
1616 | * Get comment for row |
||
1617 | * |
||
1618 | * @param array $commentsMap comments array |
||
1619 | * @param array $fieldsMeta set of field properties |
||
1620 | * |
||
1621 | * @return string html content |
||
1622 | * |
||
1623 | * @access private |
||
1624 | * |
||
1625 | * @see _getTableHeaders() |
||
1626 | */ |
||
1627 | private function _getCommentForRow(array $commentsMap, $fieldsMeta) |
||
1628 | { |
||
1629 | return $this->template->render('display/results/comment_for_row', [ |
||
1630 | 'comments_map' => $commentsMap, |
||
1631 | 'fields_meta' => $fieldsMeta, |
||
1632 | 'limit_chars' => $GLOBALS['cfg']['LimitChars'], |
||
1633 | ]); |
||
1634 | } |
||
1635 | |||
1636 | /** |
||
1637 | * Prepare parameters and html for sorted table header fields |
||
1638 | * |
||
1639 | * @param stdClass $fields_meta set of field properties |
||
1640 | * @param array $sort_expression sort expression |
||
1641 | * @param array $sort_expression_nodirection sort expression without direction |
||
1642 | * @param integer $column_index the index of the column |
||
1643 | * @param string $unsorted_sql_query the unsorted sql query |
||
1644 | * @param integer $session_max_rows maximum rows resulted by sql |
||
1645 | * @param string $comments comment for row |
||
1646 | * @param array $sort_direction sort direction |
||
1647 | * @param boolean $col_visib column is visible(false) |
||
1648 | * array column isn't visible(string array) |
||
1649 | * @param string $col_visib_j element of $col_visib array |
||
1650 | * |
||
1651 | * @return array 2 element array - $order_link, $sorted_header_html |
||
1652 | * |
||
1653 | * @access private |
||
1654 | * |
||
1655 | * @see _getTableHeaders() |
||
1656 | */ |
||
1657 | private function _getOrderLinkAndSortedHeaderHtml( |
||
1658 | $fields_meta, |
||
1659 | array $sort_expression, |
||
1660 | array $sort_expression_nodirection, |
||
1661 | $column_index, |
||
1662 | $unsorted_sql_query, |
||
1663 | $session_max_rows, |
||
1664 | $comments, |
||
1665 | array $sort_direction, |
||
1666 | $col_visib, |
||
1667 | $col_visib_j |
||
1668 | ) { |
||
1669 | |||
1670 | $sorted_header_html = ''; |
||
1671 | |||
1672 | // Checks if the table name is required; it's the case |
||
1673 | // for a query with a "JOIN" statement and if the column |
||
1674 | // isn't aliased, or in queries like |
||
1675 | // SELECT `1`.`master_field` , `2`.`master_field` |
||
1676 | // FROM `PMA_relation` AS `1` , `PMA_relation` AS `2` |
||
1677 | |||
1678 | $sort_tbl = isset($fields_meta->table) |
||
1679 | && strlen($fields_meta->table) > 0 |
||
1680 | && $fields_meta->orgname == $fields_meta->name |
||
1681 | ? Util::backquote( |
||
1682 | $fields_meta->table |
||
1683 | ) . '.' |
||
1684 | : ''; |
||
1685 | |||
1686 | $name_to_use_in_sort = $fields_meta->name; |
||
1687 | |||
1688 | // Generates the orderby clause part of the query which is part |
||
1689 | // of URL |
||
1690 | list($single_sort_order, $multi_sort_order, $order_img) |
||
1691 | = $this->_getSingleAndMultiSortUrls( |
||
1692 | $sort_expression, |
||
1693 | $sort_expression_nodirection, |
||
1694 | $sort_tbl, |
||
1695 | $name_to_use_in_sort, |
||
1696 | $sort_direction, |
||
1697 | $fields_meta |
||
1698 | ); |
||
1699 | |||
1700 | if (preg_match( |
||
1701 | '@(.*)([[:space:]](LIMIT (.*)|PROCEDURE (.*)|FOR UPDATE|' |
||
1702 | . 'LOCK IN SHARE MODE))@is', |
||
1703 | $unsorted_sql_query, |
||
1704 | $regs3 |
||
1705 | )) { |
||
1706 | $single_sorted_sql_query = $regs3[1] . $single_sort_order . $regs3[2]; |
||
1707 | $multi_sorted_sql_query = $regs3[1] . $multi_sort_order . $regs3[2]; |
||
1708 | } else { |
||
1709 | $single_sorted_sql_query = $unsorted_sql_query . $single_sort_order; |
||
1710 | $multi_sorted_sql_query = $unsorted_sql_query . $multi_sort_order; |
||
1711 | } |
||
1712 | |||
1713 | $_single_url_params = [ |
||
1714 | 'db' => $this->__get('db'), |
||
1715 | 'table' => $this->__get('table'), |
||
1716 | 'sql_query' => $single_sorted_sql_query, |
||
1717 | 'session_max_rows' => $session_max_rows, |
||
1718 | 'is_browse_distinct' => $this->__get('is_browse_distinct'), |
||
1719 | ]; |
||
1720 | |||
1721 | $_multi_url_params = [ |
||
1722 | 'db' => $this->__get('db'), |
||
1723 | 'table' => $this->__get('table'), |
||
1724 | 'sql_query' => $multi_sorted_sql_query, |
||
1725 | 'session_max_rows' => $session_max_rows, |
||
1726 | 'is_browse_distinct' => $this->__get('is_browse_distinct'), |
||
1727 | ]; |
||
1728 | $single_order_url = 'sql.php' . Url::getCommon($_single_url_params); |
||
1729 | $multi_order_url = 'sql.php' . Url::getCommon($_multi_url_params); |
||
1730 | |||
1731 | // Displays the sorting URL |
||
1732 | // enable sort order swapping for image |
||
1733 | $order_link = $this->_getSortOrderLink( |
||
1734 | $order_img, |
||
1735 | $fields_meta, |
||
1736 | $single_order_url, |
||
1737 | $multi_order_url |
||
1738 | ); |
||
1739 | |||
1740 | $sorted_header_html .= $this->_getDraggableClassForSortableColumns( |
||
1741 | $col_visib, |
||
1742 | $col_visib_j, |
||
1743 | $fields_meta, |
||
1744 | $order_link, |
||
1745 | $comments |
||
1746 | ); |
||
1747 | |||
1748 | return [ |
||
1749 | $order_link, |
||
1750 | $sorted_header_html, |
||
1751 | ]; |
||
1752 | } |
||
1753 | |||
1754 | /** |
||
1755 | * Prepare parameters and html for sorted table header fields |
||
1756 | * |
||
1757 | * @param array $sort_expression sort expression |
||
1758 | * @param array $sort_expression_nodirection sort expression without direction |
||
1759 | * @param string $sort_tbl The name of the table to which |
||
1760 | * the current column belongs to |
||
1761 | * @param string $name_to_use_in_sort The current column under |
||
1762 | * consideration |
||
1763 | * @param array $sort_direction sort direction |
||
1764 | * @param stdClass $fields_meta set of field properties |
||
1765 | * |
||
1766 | * @return array 3 element array - $single_sort_order, $sort_order, $order_img |
||
1767 | * |
||
1768 | * @access private |
||
1769 | * |
||
1770 | * @see _getOrderLinkAndSortedHeaderHtml() |
||
1771 | */ |
||
1772 | private function _getSingleAndMultiSortUrls( |
||
1773 | array $sort_expression, |
||
1774 | array $sort_expression_nodirection, |
||
1775 | $sort_tbl, |
||
1776 | $name_to_use_in_sort, |
||
1777 | array $sort_direction, |
||
1778 | $fields_meta |
||
1779 | ) { |
||
1780 | $sort_order = ""; |
||
1781 | // Check if the current column is in the order by clause |
||
1782 | $is_in_sort = $this->_isInSorted( |
||
1783 | $sort_expression, |
||
1784 | $sort_expression_nodirection, |
||
1785 | $sort_tbl, |
||
1786 | $name_to_use_in_sort |
||
1787 | ); |
||
1788 | $current_name = $name_to_use_in_sort; |
||
1789 | if ($sort_expression_nodirection[0] == '' || ! $is_in_sort) { |
||
1790 | $special_index = $sort_expression_nodirection[0] == '' |
||
1791 | ? 0 |
||
1792 | : count($sort_expression_nodirection); |
||
1793 | $sort_expression_nodirection[$special_index] |
||
1794 | = Util::backquote( |
||
1795 | $current_name |
||
1796 | ); |
||
1797 | $sort_direction[$special_index] = preg_match( |
||
1798 | '@time|date@i', |
||
1799 | $fields_meta->type |
||
1800 | ) ? self::DESCENDING_SORT_DIR : self::ASCENDING_SORT_DIR; |
||
1801 | } |
||
1802 | |||
1803 | $sort_expression_nodirection = array_filter($sort_expression_nodirection); |
||
1804 | $single_sort_order = null; |
||
1805 | foreach ($sort_expression_nodirection as $index => $expression) { |
||
1806 | // check if this is the first clause, |
||
1807 | // if it is then we have to add "order by" |
||
1808 | $is_first_clause = ($index == 0); |
||
1809 | $name_to_use_in_sort = $expression; |
||
1810 | $sort_tbl_new = $sort_tbl; |
||
1811 | // Test to detect if the column name is a standard name |
||
1812 | // Standard name has the table name prefixed to the column name |
||
1813 | if (mb_strpos($name_to_use_in_sort, '.') !== false) { |
||
1814 | $matches = explode('.', $name_to_use_in_sort); |
||
1815 | // Matches[0] has the table name |
||
1816 | // Matches[1] has the column name |
||
1817 | $name_to_use_in_sort = $matches[1]; |
||
1818 | $sort_tbl_new = $matches[0]; |
||
1819 | } |
||
1820 | |||
1821 | // $name_to_use_in_sort might contain a space due to |
||
1822 | // formatting of function expressions like "COUNT(name )" |
||
1823 | // so we remove the space in this situation |
||
1824 | $name_to_use_in_sort = str_replace([' )', '``'], [')', '`'], $name_to_use_in_sort); |
||
1825 | $name_to_use_in_sort = trim($name_to_use_in_sort, '`'); |
||
1826 | |||
1827 | // If this the first column name in the order by clause add |
||
1828 | // order by clause to the column name |
||
1829 | $query_head = $is_first_clause ? "\nORDER BY " : ""; |
||
1830 | // Again a check to see if the given column is a aggregate column |
||
1831 | if (mb_strpos($name_to_use_in_sort, '(') !== false) { |
||
1832 | $sort_order .= $query_head . $name_to_use_in_sort . ' ' ; |
||
1833 | } else { |
||
1834 | if (strlen($sort_tbl_new) > 0) { |
||
1835 | $sort_tbl_new .= "."; |
||
1836 | } |
||
1837 | $sort_order .= $query_head . $sort_tbl_new |
||
1838 | . Util::backquote( |
||
1839 | $name_to_use_in_sort |
||
1840 | ) . ' ' ; |
||
1841 | } |
||
1842 | |||
1843 | // For a special case where the code generates two dots between |
||
1844 | // column name and table name. |
||
1845 | $sort_order = preg_replace("/\.\./", ".", $sort_order); |
||
1846 | // Incase this is the current column save $single_sort_order |
||
1847 | if ($current_name == $name_to_use_in_sort) { |
||
1848 | if (mb_strpos($current_name, '(') !== false) { |
||
1849 | $single_sort_order = "\n" . 'ORDER BY ' . Util::backquote($current_name) . ' '; |
||
1850 | } else { |
||
1851 | $single_sort_order = "\n" . 'ORDER BY ' . $sort_tbl |
||
1852 | . Util::backquote( |
||
1853 | $current_name |
||
1854 | ) . ' '; |
||
1855 | } |
||
1856 | if ($is_in_sort) { |
||
1857 | list($single_sort_order, $order_img) |
||
1858 | = $this->_getSortingUrlParams( |
||
1859 | $sort_direction, |
||
1860 | $single_sort_order, |
||
1861 | $index |
||
1862 | ); |
||
1863 | } else { |
||
1864 | $single_sort_order .= strtoupper($sort_direction[$index]); |
||
1865 | } |
||
1866 | } |
||
1867 | if ($current_name == $name_to_use_in_sort && $is_in_sort) { |
||
1868 | // We need to generate the arrow button and related html |
||
1869 | list($sort_order, $order_img) = $this->_getSortingUrlParams( |
||
1870 | $sort_direction, |
||
1871 | $sort_order, |
||
1872 | $index |
||
1873 | ); |
||
1874 | $order_img .= " <small>" . ($index + 1) . "</small>"; |
||
1875 | } else { |
||
1876 | $sort_order .= strtoupper($sort_direction[$index]); |
||
1877 | } |
||
1878 | // Separate columns by a comma |
||
1879 | $sort_order .= ", "; |
||
1880 | } |
||
1881 | // remove the comma from the last column name in the newly |
||
1882 | // constructed clause |
||
1883 | $sort_order = mb_substr( |
||
1884 | $sort_order, |
||
1885 | 0, |
||
1886 | mb_strlen($sort_order) - 2 |
||
1887 | ); |
||
1888 | if (empty($order_img)) { |
||
1889 | $order_img = ''; |
||
1890 | } |
||
1891 | return [ |
||
1892 | $single_sort_order, |
||
1893 | $sort_order, |
||
1894 | $order_img, |
||
1895 | ]; |
||
1896 | } |
||
1897 | |||
1898 | /** |
||
1899 | * Check whether the column is sorted |
||
1900 | * |
||
1901 | * @param array $sort_expression sort expression |
||
1902 | * @param array $sort_expression_nodirection sort expression without direction |
||
1903 | * @param string $sort_tbl the table name |
||
1904 | * @param string $name_to_use_in_sort the sorting column name |
||
1905 | * |
||
1906 | * @return boolean the column sorted or not |
||
1907 | * |
||
1908 | * @access private |
||
1909 | * |
||
1910 | * @see _getTableHeaders() |
||
1911 | */ |
||
1912 | private function _isInSorted( |
||
1913 | array $sort_expression, |
||
1914 | array $sort_expression_nodirection, |
||
1915 | $sort_tbl, |
||
1916 | $name_to_use_in_sort |
||
1917 | ) { |
||
1918 | |||
1919 | $index_in_expression = 0; |
||
1920 | |||
1921 | foreach ($sort_expression_nodirection as $index => $clause) { |
||
1922 | if (mb_strpos($clause, '.') !== false) { |
||
1923 | $fragments = explode('.', $clause); |
||
1924 | $clause2 = $fragments[0] . "." . str_replace('`', '', $fragments[1]); |
||
1925 | } else { |
||
1926 | $clause2 = $sort_tbl . str_replace('`', '', $clause); |
||
1927 | } |
||
1928 | if ($clause2 === $sort_tbl . $name_to_use_in_sort) { |
||
1929 | $index_in_expression = $index; |
||
1930 | break; |
||
1931 | } |
||
1932 | } |
||
1933 | if (empty($sort_expression[$index_in_expression])) { |
||
1934 | $is_in_sort = false; |
||
1935 | } else { |
||
1936 | // Field name may be preceded by a space, or any number |
||
1937 | // of characters followed by a dot (tablename.fieldname) |
||
1938 | // so do a direct comparison for the sort expression; |
||
1939 | // this avoids problems with queries like |
||
1940 | // "SELECT id, count(id)..." and clicking to sort |
||
1941 | // on id or on count(id). |
||
1942 | // Another query to test this: |
||
1943 | // SELECT p.*, FROM_UNIXTIME(p.temps) FROM mytable AS p |
||
1944 | // (and try clicking on each column's header twice) |
||
1945 | $noSortTable = empty($sort_tbl) || mb_strpos( |
||
1946 | $sort_expression_nodirection[$index_in_expression], |
||
1947 | $sort_tbl |
||
1948 | ) === false; |
||
1949 | $noOpenParenthesis = mb_strpos( |
||
1950 | $sort_expression_nodirection[$index_in_expression], |
||
1951 | '(' |
||
1952 | ) === false; |
||
1953 | if (! empty($sort_tbl) && $noSortTable && $noOpenParenthesis) { |
||
1954 | $new_sort_expression_nodirection = $sort_tbl |
||
1955 | . $sort_expression_nodirection[$index_in_expression]; |
||
1956 | } else { |
||
1957 | $new_sort_expression_nodirection |
||
1958 | = $sort_expression_nodirection[$index_in_expression]; |
||
1959 | } |
||
1960 | |||
1961 | //Back quotes are removed in next comparison, so remove them from value |
||
1962 | //to compare. |
||
1963 | $name_to_use_in_sort = str_replace('`', '', $name_to_use_in_sort); |
||
1964 | |||
1965 | $is_in_sort = false; |
||
1966 | $sort_name = str_replace('`', '', $sort_tbl) . $name_to_use_in_sort; |
||
1967 | |||
1968 | if ($sort_name == str_replace('`', '', $new_sort_expression_nodirection) |
||
1969 | || $sort_name == str_replace('`', '', $sort_expression_nodirection[$index_in_expression]) |
||
1970 | ) { |
||
1971 | $is_in_sort = true; |
||
1972 | } |
||
1973 | } |
||
1974 | |||
1975 | return $is_in_sort; |
||
1976 | } |
||
1977 | |||
1978 | /** |
||
1979 | * Get sort url parameters - sort order and order image |
||
1980 | * |
||
1981 | * @param array $sort_direction the sort direction |
||
1982 | * @param string $sort_order the sorting order |
||
1983 | * @param integer $index the index of sort direction array. |
||
1984 | * |
||
1985 | * @return array 2 element array - $sort_order, $order_img |
||
1986 | * |
||
1987 | * @access private |
||
1988 | * |
||
1989 | * @see _getSingleAndMultiSortUrls() |
||
1990 | */ |
||
1991 | private function _getSortingUrlParams(array $sort_direction, $sort_order, $index) |
||
1992 | { |
||
1993 | if (strtoupper(trim($sort_direction[$index])) == self::DESCENDING_SORT_DIR) { |
||
1994 | $sort_order .= ' ASC'; |
||
1995 | $order_img = ' ' . Util::getImage( |
||
1996 | 's_desc', |
||
1997 | __('Descending'), |
||
1998 | [ |
||
1999 | 'class' => "soimg", |
||
2000 | 'title' => '', |
||
2001 | ] |
||
2002 | ); |
||
2003 | $order_img .= ' ' . Util::getImage( |
||
2004 | 's_asc', |
||
2005 | __('Ascending'), |
||
2006 | [ |
||
2007 | 'class' => "soimg hide", |
||
2008 | 'title' => '', |
||
2009 | ] |
||
2010 | ); |
||
2011 | } else { |
||
2012 | $sort_order .= ' DESC'; |
||
2013 | $order_img = ' ' . Util::getImage( |
||
2014 | 's_asc', |
||
2015 | __('Ascending'), |
||
2016 | [ |
||
2017 | 'class' => "soimg", |
||
2018 | 'title' => '', |
||
2019 | ] |
||
2020 | ); |
||
2021 | $order_img .= ' ' . Util::getImage( |
||
2022 | 's_desc', |
||
2023 | __('Descending'), |
||
2024 | [ |
||
2025 | 'class' => "soimg hide", |
||
2026 | 'title' => '', |
||
2027 | ] |
||
2028 | ); |
||
2029 | } |
||
2030 | return [ |
||
2031 | $sort_order, |
||
2032 | $order_img, |
||
2033 | ]; |
||
2034 | } |
||
2035 | |||
2036 | /** |
||
2037 | * Get sort order link |
||
2038 | * |
||
2039 | * @param string $order_img the sort order image |
||
2040 | * @param stdClass $fields_meta set of field properties |
||
2041 | * @param string $order_url the url for sort |
||
2042 | * @param string $multi_order_url the url for sort |
||
2043 | * |
||
2044 | * @return string the sort order link |
||
2045 | * |
||
2046 | * @access private |
||
2047 | * |
||
2048 | * @see _getTableHeaders() |
||
2049 | */ |
||
2050 | private function _getSortOrderLink( |
||
2051 | $order_img, |
||
2052 | $fields_meta, |
||
2053 | $order_url, |
||
2054 | $multi_order_url |
||
2055 | ) { |
||
2056 | $order_link_params = [ |
||
2057 | 'class' => 'sortlink', |
||
2058 | ]; |
||
2059 | |||
2060 | $order_link_content = htmlspecialchars($fields_meta->name); |
||
2061 | $inner_link_content = $order_link_content . $order_img |
||
2062 | . '<input type="hidden" value="' . $multi_order_url . '">'; |
||
2063 | |||
2064 | return Util::linkOrButton( |
||
2065 | $order_url, |
||
2066 | $inner_link_content, |
||
2067 | $order_link_params |
||
2068 | ); |
||
2069 | } |
||
2070 | |||
2071 | /** |
||
2072 | * Check if the column contains numeric data. If yes, then set the |
||
2073 | * column header's alignment right |
||
2074 | * |
||
2075 | * @param stdClass $fields_meta set of field properties |
||
2076 | * @param array $th_class array containing classes |
||
2077 | * |
||
2078 | * @return void |
||
2079 | * |
||
2080 | * @see _getDraggableClassForSortableColumns() |
||
2081 | */ |
||
2082 | private function _getClassForNumericColumnType($fields_meta, array &$th_class) |
||
2083 | { |
||
2084 | if (preg_match( |
||
2085 | '@int|decimal|float|double|real|bit|boolean|serial@i', |
||
2086 | (string) $fields_meta->type |
||
2087 | )) { |
||
2088 | $th_class[] = 'right'; |
||
2089 | } |
||
2090 | } |
||
2091 | |||
2092 | /** |
||
2093 | * Prepare columns to draggable effect for sortable columns |
||
2094 | * |
||
2095 | * @param boolean $col_visib the column is visible (false) |
||
2096 | * array the column is not visible (string array) |
||
2097 | * @param string $col_visib_j element of $col_visib array |
||
2098 | * @param stdClass $fields_meta set of field properties |
||
2099 | * @param string $order_link the order link |
||
2100 | * @param string $comments the comment for the column |
||
2101 | * |
||
2102 | * @return string html content |
||
2103 | * |
||
2104 | * @access private |
||
2105 | * |
||
2106 | * @see _getTableHeaders() |
||
2107 | */ |
||
2108 | private function _getDraggableClassForSortableColumns( |
||
2109 | $col_visib, |
||
2110 | $col_visib_j, |
||
2111 | $fields_meta, |
||
2112 | $order_link, |
||
2113 | $comments |
||
2114 | ) { |
||
2115 | |||
2116 | $draggable_html = '<th'; |
||
2117 | $th_class = []; |
||
2118 | $th_class[] = 'draggable'; |
||
2119 | $this->_getClassForNumericColumnType($fields_meta, $th_class); |
||
2120 | if ($col_visib && ! $col_visib_j) { |
||
2121 | $th_class[] = 'hide'; |
||
2122 | } |
||
2123 | |||
2124 | $th_class[] = 'column_heading'; |
||
2125 | if ($GLOBALS['cfg']['BrowsePointerEnable'] == true) { |
||
2126 | $th_class[] = 'pointer'; |
||
2127 | } |
||
2128 | |||
2129 | if ($GLOBALS['cfg']['BrowseMarkerEnable'] == true) { |
||
2130 | $th_class[] = 'marker'; |
||
2131 | } |
||
2132 | |||
2133 | $draggable_html .= ' class="' . implode(' ', $th_class) . '"'; |
||
2134 | |||
2135 | $draggable_html .= ' data-column="' . htmlspecialchars($fields_meta->name) |
||
2136 | . '">' . $order_link . $comments . '</th>'; |
||
2137 | |||
2138 | return $draggable_html; |
||
2139 | } |
||
2140 | |||
2141 | /** |
||
2142 | * Prepare columns to draggable effect for non sortable columns |
||
2143 | * |
||
2144 | * @param boolean $col_visib the column is visible (false) |
||
2145 | * array the column is not visible (string array) |
||
2146 | * @param string $col_visib_j element of $col_visib array |
||
2147 | * @param boolean $condition_field whether to add CSS class condition |
||
2148 | * @param stdClass $fields_meta set of field properties |
||
2149 | * @param string $comments the comment for the column |
||
2150 | * |
||
2151 | * @return string html content |
||
2152 | * |
||
2153 | * @access private |
||
2154 | * |
||
2155 | * @see _getTableHeaders() |
||
2156 | */ |
||
2157 | private function _getDraggableClassForNonSortableColumns( |
||
2158 | $col_visib, |
||
2159 | $col_visib_j, |
||
2160 | $condition_field, |
||
2161 | $fields_meta, |
||
2162 | $comments |
||
2163 | ) { |
||
2164 | |||
2165 | $draggable_html = '<th'; |
||
2166 | $th_class = []; |
||
2167 | $th_class[] = 'draggable'; |
||
2168 | $this->_getClassForNumericColumnType($fields_meta, $th_class); |
||
2169 | if ($col_visib && ! $col_visib_j) { |
||
2170 | $th_class[] = 'hide'; |
||
2171 | } |
||
2172 | |||
2173 | if ($condition_field) { |
||
2174 | $th_class[] = 'condition'; |
||
2175 | } |
||
2176 | |||
2177 | $draggable_html .= ' class="' . implode(' ', $th_class) . '"'; |
||
2178 | |||
2179 | $draggable_html .= ' data-column="' |
||
2180 | . htmlspecialchars((string) $fields_meta->name) . '">'; |
||
2181 | |||
2182 | $draggable_html .= htmlspecialchars((string) $fields_meta->name); |
||
2183 | |||
2184 | $draggable_html .= "\n" . $comments . '</th>'; |
||
2185 | |||
2186 | return $draggable_html; |
||
2187 | } |
||
2188 | |||
2189 | /** |
||
2190 | * Prepare column to show at right side - check boxes or empty column |
||
2191 | * |
||
2192 | * @param array $displayParts which elements to display |
||
2193 | * @param string $full_or_partial_text_link full/partial link or text button |
||
2194 | * @param string $colspan column span of table header |
||
2195 | * |
||
2196 | * @return string html content |
||
2197 | * |
||
2198 | * @access private |
||
2199 | * |
||
2200 | * @see _getTableHeaders() |
||
2201 | */ |
||
2202 | private function _getColumnAtRightSide( |
||
2203 | array &$displayParts, |
||
2204 | $full_or_partial_text_link, |
||
2205 | $colspan |
||
2206 | ) { |
||
2207 | |||
2208 | $right_column_html = ''; |
||
2209 | $display_params = $this->__get('display_params'); |
||
2210 | |||
2211 | // Displays the needed checkboxes at the right |
||
2212 | // column of the result table header if possible and required... |
||
2213 | if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) |
||
2214 | || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH) |
||
2215 | && (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2216 | || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE)) |
||
2217 | && ($displayParts['text_btn'] == '1') |
||
2218 | ) { |
||
2219 | $display_params['emptyafter'] |
||
2220 | = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2221 | && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE) ? 4 : 1; |
||
2222 | |||
2223 | $right_column_html .= "\n" |
||
2224 | . '<th class="column_action print_ignore" ' . $colspan . '>' |
||
2225 | . $full_or_partial_text_link |
||
2226 | . '</th>'; |
||
2227 | } elseif (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) |
||
2228 | || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH) |
||
2229 | && (($displayParts['edit_lnk'] == self::NO_EDIT_OR_DELETE) |
||
2230 | && ($displayParts['del_lnk'] == self::NO_EDIT_OR_DELETE)) |
||
2231 | && (! isset($GLOBALS['is_header_sent']) || ! $GLOBALS['is_header_sent']) |
||
2232 | ) { |
||
2233 | // ... elseif no button, displays empty columns if required |
||
2234 | // (unless coming from Browse mode print view) |
||
2235 | |||
2236 | $display_params['emptyafter'] |
||
2237 | = ($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2238 | && ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE) ? 4 : 1; |
||
2239 | |||
2240 | $right_column_html .= "\n" . '<td class="print_ignore" ' . $colspan |
||
2241 | . '></td>'; |
||
2242 | } |
||
2243 | |||
2244 | $this->__set('display_params', $display_params); |
||
2245 | |||
2246 | return $right_column_html; |
||
2247 | } |
||
2248 | |||
2249 | /** |
||
2250 | * Prepares the display for a value |
||
2251 | * |
||
2252 | * @param string $class class of table cell |
||
2253 | * @param bool $conditionField whether to add CSS class condition |
||
2254 | * @param string $value value to display |
||
2255 | * |
||
2256 | * @return string the td |
||
2257 | * |
||
2258 | * @access private |
||
2259 | * |
||
2260 | * @see _getDataCellForGeometryColumns(), |
||
2261 | * _getDataCellForNonNumericColumns() |
||
2262 | */ |
||
2263 | private function _buildValueDisplay($class, $conditionField, $value) |
||
2264 | { |
||
2265 | return $this->template->render('display/results/value_display', [ |
||
2266 | 'class' => $class, |
||
2267 | 'condition_field' => $conditionField, |
||
2268 | 'value' => $value, |
||
2269 | ]); |
||
2270 | } |
||
2271 | |||
2272 | /** |
||
2273 | * Prepares the display for a null value |
||
2274 | * |
||
2275 | * @param string $class class of table cell |
||
2276 | * @param bool $conditionField whether to add CSS class condition |
||
2277 | * @param stdClass $meta the meta-information about this field |
||
2278 | * @param string $align cell alignment |
||
2279 | * |
||
2280 | * @return string the td |
||
2281 | * |
||
2282 | * @access private |
||
2283 | * |
||
2284 | * @see _getDataCellForNumericColumns(), |
||
2285 | * _getDataCellForGeometryColumns(), |
||
2286 | * _getDataCellForNonNumericColumns() |
||
2287 | */ |
||
2288 | private function _buildNullDisplay($class, $conditionField, $meta, $align = '') |
||
2289 | { |
||
2290 | $classes = $this->_addClass($class, $conditionField, $meta, ''); |
||
2291 | |||
2292 | return $this->template->render('display/results/null_display', [ |
||
2293 | 'align' => $align, |
||
2294 | 'meta' => $meta, |
||
2295 | 'classes' => $classes, |
||
2296 | ]); |
||
2297 | } |
||
2298 | |||
2299 | /** |
||
2300 | * Prepares the display for an empty value |
||
2301 | * |
||
2302 | * @param string $class class of table cell |
||
2303 | * @param bool $conditionField whether to add CSS class condition |
||
2304 | * @param stdClass $meta the meta-information about this field |
||
2305 | * @param string $align cell alignment |
||
2306 | * |
||
2307 | * @return string the td |
||
2308 | * |
||
2309 | * @access private |
||
2310 | * |
||
2311 | * @see _getDataCellForNumericColumns(), |
||
2312 | * _getDataCellForGeometryColumns(), |
||
2313 | * _getDataCellForNonNumericColumns() |
||
2314 | */ |
||
2315 | private function _buildEmptyDisplay($class, $conditionField, $meta, $align = '') |
||
2316 | { |
||
2317 | $classes = $this->_addClass($class, $conditionField, $meta, 'nowrap'); |
||
2318 | |||
2319 | return $this->template->render('display/results/empty_display', [ |
||
2320 | 'align' => $align, |
||
2321 | 'classes' => $classes, |
||
2322 | ]); |
||
2323 | } |
||
2324 | |||
2325 | /** |
||
2326 | * Adds the relevant classes. |
||
2327 | * |
||
2328 | * @param string $class class of table cell |
||
2329 | * @param bool $condition_field whether to add CSS class |
||
2330 | * condition |
||
2331 | * @param stdClass $meta the meta-information about the |
||
2332 | * field |
||
2333 | * @param string $nowrap avoid wrapping |
||
2334 | * @param bool $is_field_truncated is field truncated (display ...) |
||
2335 | * @param TransformationsPlugin|string $transformation_plugin transformation plugin. |
||
2336 | * Can also be the default function: |
||
2337 | * Core::mimeDefaultFunction |
||
2338 | * @param string $default_function default transformation function |
||
2339 | * |
||
2340 | * @return string the list of classes |
||
2341 | * |
||
2342 | * @access private |
||
2343 | * |
||
2344 | * @see _buildNullDisplay(), _getRowData() |
||
2345 | */ |
||
2346 | private function _addClass( |
||
2347 | $class, |
||
2348 | $condition_field, |
||
2349 | $meta, |
||
2350 | $nowrap, |
||
2351 | $is_field_truncated = false, |
||
2352 | $transformation_plugin = '', |
||
2353 | $default_function = '' |
||
2354 | ) { |
||
2355 | $classes = [ |
||
2356 | $class, |
||
2357 | $nowrap, |
||
2358 | ]; |
||
2359 | |||
2360 | if (isset($meta->mimetype)) { |
||
2361 | $classes[] = preg_replace('/\//', '_', $meta->mimetype); |
||
2362 | } |
||
2363 | |||
2364 | if ($condition_field) { |
||
2365 | $classes[] = 'condition'; |
||
2366 | } |
||
2367 | |||
2368 | if ($is_field_truncated) { |
||
2369 | $classes[] = 'truncated'; |
||
2370 | } |
||
2371 | |||
2372 | $mime_map = $this->__get('mime_map'); |
||
2373 | $orgFullColName = $this->__get('db') . '.' . $meta->orgtable |
||
2374 | . '.' . $meta->orgname; |
||
2375 | if ($transformation_plugin != $default_function |
||
2376 | || ! empty($mime_map[$orgFullColName]['input_transformation']) |
||
2377 | ) { |
||
2378 | $classes[] = 'transformed'; |
||
2379 | } |
||
2380 | |||
2381 | // Define classes to be added to this data field based on the type of data |
||
2382 | $matches = [ |
||
2383 | 'enum' => 'enum', |
||
2384 | 'set' => 'set', |
||
2385 | 'binary' => 'hex', |
||
2386 | ]; |
||
2387 | |||
2388 | foreach ($matches as $key => $value) { |
||
2389 | if (mb_strpos($meta->flags, $key) !== false) { |
||
2390 | $classes[] = $value; |
||
2391 | } |
||
2392 | } |
||
2393 | |||
2394 | if (mb_strpos($meta->type, 'bit') !== false) { |
||
2395 | $classes[] = 'bit'; |
||
2396 | } |
||
2397 | |||
2398 | return implode(' ', $classes); |
||
2399 | } |
||
2400 | |||
2401 | /** |
||
2402 | * Prepare the body of the results table |
||
2403 | * |
||
2404 | * @param integer $dt_result the link id associated to the query |
||
2405 | * which results have to be displayed |
||
2406 | * @param array $displayParts which elements to display |
||
2407 | * @param array $map the list of relations |
||
2408 | * @param array $analyzed_sql_results analyzed sql results |
||
2409 | * @param boolean $is_limited_display with limited operations or not |
||
2410 | * |
||
2411 | * @return string html content |
||
2412 | * |
||
2413 | * @global array $row current row data |
||
2414 | * |
||
2415 | * @access private |
||
2416 | * |
||
2417 | * @see getTable() |
||
2418 | */ |
||
2419 | private function _getTableBody( |
||
2420 | &$dt_result, |
||
2421 | array &$displayParts, |
||
2422 | array $map, |
||
2423 | array $analyzed_sql_results, |
||
2424 | $is_limited_display = false |
||
2425 | ) { |
||
2426 | global $row; // mostly because of browser transformations, |
||
2427 | // to make the row-data accessible in a plugin |
||
2428 | |||
2429 | $table_body_html = ''; |
||
2430 | |||
2431 | // query without conditions to shorten URLs when needed, 200 is just |
||
2432 | // guess, it should depend on remaining URL length |
||
2433 | $url_sql_query = $this->_getUrlSqlQuery($analyzed_sql_results); |
||
2434 | |||
2435 | $display_params = $this->__get('display_params'); |
||
2436 | |||
2437 | if (! is_array($map)) { |
||
2438 | $map = []; |
||
2439 | } |
||
2440 | |||
2441 | $row_no = 0; |
||
2442 | $display_params['edit'] = []; |
||
2443 | $display_params['copy'] = []; |
||
2444 | $display_params['delete'] = []; |
||
2445 | $display_params['data'] = []; |
||
2446 | $display_params['row_delete'] = []; |
||
2447 | $this->__set('display_params', $display_params); |
||
2448 | |||
2449 | // name of the class added to all grid editable elements; |
||
2450 | // if we don't have all the columns of a unique key in the result set, |
||
2451 | // do not permit grid editing |
||
2452 | if ($is_limited_display || ! $this->__get('editable')) { |
||
2453 | $grid_edit_class = ''; |
||
2454 | } else { |
||
2455 | switch ($GLOBALS['cfg']['GridEditing']) { |
||
2456 | case 'double-click': |
||
2457 | // trying to reduce generated HTML by using shorter |
||
2458 | // classes like click1 and click2 |
||
2459 | $grid_edit_class = 'grid_edit click2'; |
||
2460 | break; |
||
2461 | case 'click': |
||
2462 | $grid_edit_class = 'grid_edit click1'; |
||
2463 | break; |
||
2464 | default: // 'disabled' |
||
2465 | $grid_edit_class = ''; |
||
2466 | break; |
||
2467 | } |
||
2468 | } |
||
2469 | |||
2470 | // prepare to get the column order, if available |
||
2471 | list($col_order, $col_visib) = $this->_getColumnParams( |
||
2472 | $analyzed_sql_results |
||
2473 | ); |
||
2474 | |||
2475 | // Correction University of Virginia 19991216 in the while below |
||
2476 | // Previous code assumed that all tables have keys, specifically that |
||
2477 | // the phpMyAdmin GUI should support row delete/edit only for such |
||
2478 | // tables. |
||
2479 | // Although always using keys is arguably the prescribed way of |
||
2480 | // defining a relational table, it is not required. This will in |
||
2481 | // particular be violated by the novice. |
||
2482 | // We want to encourage phpMyAdmin usage by such novices. So the code |
||
2483 | // below has been changed to conditionally work as before when the |
||
2484 | // table being displayed has one or more keys; but to display |
||
2485 | // delete/edit options correctly for tables without keys. |
||
2486 | |||
2487 | $whereClauseMap = $this->__get('whereClauseMap'); |
||
2488 | while ($row = $GLOBALS['dbi']->fetchRow($dt_result)) { |
||
2489 | // add repeating headers |
||
2490 | if (($row_no != 0) && ($_SESSION['tmpval']['repeat_cells'] != 0) |
||
2491 | && ! ($row_no % $_SESSION['tmpval']['repeat_cells']) |
||
2492 | ) { |
||
2493 | $table_body_html .= $this->_getRepeatingHeaders( |
||
2494 | $display_params |
||
2495 | ); |
||
2496 | } |
||
2497 | |||
2498 | $tr_class = []; |
||
2499 | if ($GLOBALS['cfg']['BrowsePointerEnable'] != true) { |
||
2500 | $tr_class[] = 'nopointer'; |
||
2501 | } |
||
2502 | if ($GLOBALS['cfg']['BrowseMarkerEnable'] != true) { |
||
2503 | $tr_class[] = 'nomarker'; |
||
2504 | } |
||
2505 | |||
2506 | // pointer code part |
||
2507 | $classes = (empty($tr_class) ? ' ' : 'class="' . implode(' ', $tr_class) . '"'); |
||
2508 | $table_body_html .= '<tr ' . $classes . ' >'; |
||
2509 | |||
2510 | // 1. Prepares the row |
||
2511 | |||
2512 | // In print view these variable needs to be initialized |
||
2513 | $del_url = $del_str = $edit_anchor_class |
||
2514 | = $edit_str = $js_conf = $copy_url = $copy_str = $edit_url = null; |
||
2515 | |||
2516 | // 1.2 Defines the URLs for the modify/delete link(s) |
||
2517 | |||
2518 | if (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2519 | || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2520 | ) { |
||
2521 | // Results from a "SELECT" statement -> builds the |
||
2522 | // WHERE clause to use in links (a unique key if possible) |
||
2523 | /** |
||
2524 | * @todo $where_clause could be empty, for example a table |
||
2525 | * with only one field and it's a BLOB; in this case, |
||
2526 | * avoid to display the delete and edit links |
||
2527 | */ |
||
2528 | list($where_clause, $clause_is_unique, $condition_array) |
||
2529 | = Util::getUniqueCondition( |
||
2530 | $dt_result, // handle |
||
2531 | $this->__get('fields_cnt'), // fields_cnt |
||
2532 | $this->__get('fields_meta'), // fields_meta |
||
2533 | $row, // row |
||
2534 | false, // force_unique |
||
2535 | $this->__get('table'), // restrict_to_table |
||
2536 | $analyzed_sql_results // analyzed_sql_results |
||
2537 | ); |
||
2538 | $whereClauseMap[$row_no][$this->__get('table')] = $where_clause; |
||
2539 | $this->__set('whereClauseMap', $whereClauseMap); |
||
2540 | |||
2541 | $where_clause_html = htmlspecialchars($where_clause); |
||
2542 | |||
2543 | // 1.2.1 Modify link(s) - update row case |
||
2544 | if ($displayParts['edit_lnk'] == self::UPDATE_ROW) { |
||
2545 | list($edit_url, $copy_url, $edit_str, $copy_str, |
||
2546 | $edit_anchor_class) |
||
2547 | = $this->_getModifiedLinks( |
||
2548 | $where_clause, |
||
2549 | $clause_is_unique, |
||
2550 | $url_sql_query |
||
2551 | ); |
||
2552 | } // end if (1.2.1) |
||
2553 | |||
2554 | // 1.2.2 Delete/Kill link(s) |
||
2555 | list($del_url, $del_str, $js_conf) |
||
2556 | = $this->_getDeleteAndKillLinks( |
||
2557 | $where_clause, |
||
2558 | $clause_is_unique, |
||
2559 | $url_sql_query, |
||
2560 | $displayParts['del_lnk'], |
||
2561 | $row |
||
2562 | ); |
||
2563 | |||
2564 | // 1.3 Displays the links at left if required |
||
2565 | if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_LEFT) |
||
2566 | || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH) |
||
2567 | ) { |
||
2568 | $table_body_html .= $this->_getPlacedLinks( |
||
2569 | self::POSITION_LEFT, |
||
2570 | $del_url, |
||
2571 | $displayParts, |
||
2572 | $row_no, |
||
2573 | $where_clause, |
||
2574 | $where_clause_html, |
||
2575 | $condition_array, |
||
2576 | $edit_url, |
||
2577 | $copy_url, |
||
2578 | $edit_anchor_class, |
||
2579 | $edit_str, |
||
2580 | $copy_str, |
||
2581 | $del_str, |
||
2582 | $js_conf |
||
2583 | ); |
||
2584 | } elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) { |
||
2585 | $table_body_html .= $this->_getPlacedLinks( |
||
2586 | self::POSITION_NONE, |
||
2587 | $del_url, |
||
2588 | $displayParts, |
||
2589 | $row_no, |
||
2590 | $where_clause, |
||
2591 | $where_clause_html, |
||
2592 | $condition_array, |
||
2593 | $edit_url, |
||
2594 | $copy_url, |
||
2595 | $edit_anchor_class, |
||
2596 | $edit_str, |
||
2597 | $copy_str, |
||
2598 | $del_str, |
||
2599 | $js_conf |
||
2600 | ); |
||
2601 | } // end if (1.3) |
||
2602 | } // end if (1) |
||
2603 | |||
2604 | // 2. Displays the rows' values |
||
2605 | if ($this->__get('mime_map') === null) { |
||
2606 | $this->_setMimeMap(); |
||
2607 | } |
||
2608 | $table_body_html .= $this->_getRowValues( |
||
2609 | $dt_result, |
||
2610 | $row, |
||
2611 | $row_no, |
||
2612 | $col_order, |
||
2613 | $map, |
||
2614 | $grid_edit_class, |
||
2615 | $col_visib, |
||
2616 | $url_sql_query, |
||
2617 | $analyzed_sql_results |
||
2618 | ); |
||
2619 | |||
2620 | // 3. Displays the modify/delete links on the right if required |
||
2621 | if (($displayParts['edit_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2622 | || ($displayParts['del_lnk'] != self::NO_EDIT_OR_DELETE) |
||
2623 | ) { |
||
2624 | if (($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_RIGHT) |
||
2625 | || ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_BOTH) |
||
2626 | ) { |
||
2627 | $table_body_html .= $this->_getPlacedLinks( |
||
2628 | self::POSITION_RIGHT, |
||
2629 | $del_url, |
||
2630 | $displayParts, |
||
2631 | $row_no, |
||
2632 | $where_clause ?? '', |
||
2633 | $where_clause_html ?? '', |
||
2634 | $condition_array ?? [], |
||
2635 | $edit_url, |
||
2636 | $copy_url, |
||
2637 | $edit_anchor_class, |
||
2638 | $edit_str, |
||
2639 | $copy_str, |
||
2640 | $del_str, |
||
2641 | $js_conf |
||
2642 | ); |
||
2643 | } |
||
2644 | } // end if (3) |
||
2645 | |||
2646 | $table_body_html .= '</tr>'; |
||
2647 | $table_body_html .= "\n"; |
||
2648 | $row_no++; |
||
2649 | } // end while |
||
2650 | |||
2651 | return $table_body_html; |
||
2652 | } |
||
2653 | |||
2654 | /** |
||
2655 | * Sets the MIME details of the columns in the results set |
||
2656 | * |
||
2657 | * @return void |
||
2658 | */ |
||
2659 | private function _setMimeMap() |
||
2660 | { |
||
2661 | $fields_meta = $this->__get('fields_meta'); |
||
2662 | $mimeMap = []; |
||
2663 | $added = []; |
||
2664 | |||
2665 | for ($currentColumn = 0; $currentColumn < $this->__get('fields_cnt'); ++$currentColumn) { |
||
2666 | $meta = $fields_meta[$currentColumn]; |
||
2667 | $orgFullTableName = $this->__get('db') . '.' . $meta->orgtable; |
||
2668 | |||
2669 | if ($GLOBALS['cfgRelation']['commwork'] |
||
2670 | && $GLOBALS['cfgRelation']['mimework'] |
||
2671 | && $GLOBALS['cfg']['BrowseMIME'] |
||
2672 | && ! $_SESSION['tmpval']['hide_transformation'] |
||
2673 | && empty($added[$orgFullTableName]) |
||
2674 | ) { |
||
2675 | $mimeMap = array_merge( |
||
2676 | $mimeMap, |
||
2677 | $this->transformations->getMime($this->__get('db'), $meta->orgtable, false, true) |
||
2678 | ); |
||
2679 | $added[$orgFullTableName] = true; |
||
2680 | } |
||
2681 | } |
||
2682 | |||
2683 | // special browser transformation for some SHOW statements |
||
2684 | if ($this->__get('is_show') |
||
2685 | && ! $_SESSION['tmpval']['hide_transformation'] |
||
2686 | ) { |
||
2687 | preg_match( |
||
2688 | '@^SHOW[[:space:]]+(VARIABLES|(FULL[[:space:]]+)?' |
||
2689 | . 'PROCESSLIST|STATUS|TABLE|GRANTS|CREATE|LOGS|DATABASES|FIELDS' |
||
2690 | . ')@i', |
||
2691 | $this->__get('sql_query'), |
||
2692 | $which |
||
2693 | ); |
||
2694 | |||
2695 | if (isset($which[1])) { |
||
2696 | $str = ' ' . strtoupper($which[1]); |
||
2697 | $isShowProcessList = strpos($str, 'PROCESSLIST') > 0; |
||
2698 | if ($isShowProcessList) { |
||
2699 | $mimeMap['..Info'] = [ |
||
2700 | 'mimetype' => 'Text_Plain', |
||
2701 | 'transformation' => 'output/Text_Plain_Sql.php', |
||
2702 | ]; |
||
2703 | } |
||
2704 | |||
2705 | $isShowCreateTable = preg_match( |
||
2706 | '@CREATE[[:space:]]+TABLE@i', |
||
2707 | $this->__get('sql_query') |
||
2708 | ); |
||
2709 | if ($isShowCreateTable) { |
||
2710 | $mimeMap['..Create Table'] = [ |
||
2711 | 'mimetype' => 'Text_Plain', |
||
2712 | 'transformation' => 'output/Text_Plain_Sql.php', |
||
2713 | ]; |
||
2714 | } |
||
2715 | } |
||
2716 | } |
||
2717 | |||
2718 | $this->__set('mime_map', $mimeMap); |
||
2719 | } |
||
2720 | |||
2721 | /** |
||
2722 | * Get the values for one data row |
||
2723 | * |
||
2724 | * @param integer $dt_result the link id associated to |
||
2725 | * the query which results |
||
2726 | * have to be displayed |
||
2727 | * @param array $row current row data |
||
2728 | * @param integer $row_no the index of current row |
||
2729 | * @param array|boolean $col_order the column order false when |
||
2730 | * a property not found false |
||
2731 | * when a property not found |
||
2732 | * @param array $map the list of relations |
||
2733 | * @param string $grid_edit_class the class for all editable |
||
2734 | * columns |
||
2735 | * @param boolean|array|string $col_visib column is visible(false); |
||
2736 | * column isn't visible(string |
||
2737 | * array) |
||
2738 | * @param string $url_sql_query the analyzed sql query |
||
2739 | * @param array $analyzed_sql_results analyzed sql results |
||
2740 | * |
||
2741 | * @return string html content |
||
2742 | * |
||
2743 | * @access private |
||
2744 | * |
||
2745 | * @see _getTableBody() |
||
2746 | */ |
||
2747 | private function _getRowValues( |
||
2748 | &$dt_result, |
||
2749 | array $row, |
||
2750 | $row_no, |
||
2751 | $col_order, |
||
2752 | array $map, |
||
2753 | $grid_edit_class, |
||
2754 | $col_visib, |
||
2755 | $url_sql_query, |
||
2756 | array $analyzed_sql_results |
||
2757 | ) { |
||
2758 | $row_values_html = ''; |
||
2759 | |||
2760 | // Following variable are needed for use in isset/empty or |
||
2761 | // use with array indexes/safe use in foreach |
||
2762 | $sql_query = $this->__get('sql_query'); |
||
2763 | $fields_meta = $this->__get('fields_meta'); |
||
2764 | $highlight_columns = $this->__get('highlight_columns'); |
||
2765 | $mime_map = $this->__get('mime_map'); |
||
2766 | |||
2767 | $row_info = $this->_getRowInfoForSpecialLinks($row, $col_order); |
||
2768 | |||
2769 | $whereClauseMap = $this->__get('whereClauseMap'); |
||
2770 | |||
2771 | $columnCount = $this->__get('fields_cnt'); |
||
2772 | for ($currentColumn = 0; $currentColumn < $columnCount; ++$currentColumn) { |
||
2773 | // assign $i with appropriate column order |
||
2774 | $i = $col_order ? $col_order[$currentColumn] : $currentColumn; |
||
2775 | |||
2776 | $meta = $fields_meta[$i]; |
||
2777 | $orgFullColName |
||
2778 | = $this->__get('db') . '.' . $meta->orgtable . '.' . $meta->orgname; |
||
2779 | |||
2780 | $not_null_class = $meta->not_null ? 'not_null' : ''; |
||
2781 | $relation_class = isset($map[$meta->name]) ? 'relation' : ''; |
||
2782 | $hide_class = $col_visib && ! $col_visib[$currentColumn] |
||
2783 | ? 'hide' |
||
2784 | : ''; |
||
2785 | $grid_edit = $meta->orgtable != '' ? $grid_edit_class : ''; |
||
2786 | |||
2787 | // handle datetime-related class, for grid editing |
||
2788 | $field_type_class |
||
2789 | = $this->_getClassForDateTimeRelatedFields($meta->type); |
||
2790 | |||
2791 | $is_field_truncated = false; |
||
2792 | // combine all the classes applicable to this column's value |
||
2793 | $class = $this->_getClassesForColumn( |
||
2794 | $grid_edit, |
||
2795 | $not_null_class, |
||
2796 | $relation_class, |
||
2797 | $hide_class, |
||
2798 | $field_type_class |
||
2799 | ); |
||
2800 | |||
2801 | // See if this column should get highlight because it's used in the |
||
2802 | // where-query. |
||
2803 | $condition_field = isset($highlight_columns) |
||
2804 | && (isset($highlight_columns[$meta->name]) |
||
2805 | || isset($highlight_columns[Util::backquote($meta->name)])) |
||
2806 | ? true |
||
2807 | : false; |
||
2808 | |||
2809 | // Wrap MIME-transformations. [MIME] |
||
2810 | $default_function = [ |
||
2811 | Core::class, |
||
2812 | 'mimeDefaultFunction', |
||
2813 | ]; // default_function |
||
2814 | $transformation_plugin = $default_function; |
||
2815 | $transform_options = []; |
||
2816 | |||
2817 | if ($GLOBALS['cfgRelation']['mimework'] |
||
2818 | && $GLOBALS['cfg']['BrowseMIME'] |
||
2819 | ) { |
||
2820 | if (isset($mime_map[$orgFullColName]['mimetype']) |
||
2821 | && ! empty($mime_map[$orgFullColName]['transformation']) |
||
2822 | ) { |
||
2823 | $file = $mime_map[$orgFullColName]['transformation']; |
||
2824 | $include_file = 'libraries/classes/Plugins/Transformations/' . $file; |
||
2825 | |||
2826 | if (@file_exists($include_file)) { |
||
2827 | $class_name = $this->transformations->getClassName($include_file); |
||
2828 | if (class_exists($class_name)) { |
||
2829 | // todo add $plugin_manager |
||
2830 | $plugin_manager = null; |
||
2831 | $transformation_plugin = new $class_name( |
||
2832 | $plugin_manager |
||
2833 | ); |
||
2834 | |||
2835 | $transform_options = $this->transformations->getOptions( |
||
2836 | isset( |
||
2837 | $mime_map[$orgFullColName]['transformation_options'] |
||
2838 | ) |
||
2839 | ? $mime_map[$orgFullColName]['transformation_options'] |
||
2840 | : '' |
||
2841 | ); |
||
2842 | |||
2843 | $meta->mimetype = str_replace( |
||
2844 | '_', |
||
2845 | '/', |
||
2846 | $mime_map[$orgFullColName]['mimetype'] |
||
2847 | ); |
||
2848 | } |
||
2849 | } // end if file_exists |
||
2850 | } // end if transformation is set |
||
2851 | } // end if mime/transformation works. |
||
2852 | |||
2853 | // Check whether the field needs to display with syntax highlighting |
||
2854 | |||
2855 | $dbLower = mb_strtolower($this->__get('db')); |
||
2856 | $tblLower = mb_strtolower($meta->orgtable); |
||
2857 | $nameLower = mb_strtolower($meta->orgname); |
||
2858 | if (! empty($this->transformation_info[$dbLower][$tblLower][$nameLower]) |
||
2859 | && (trim($row[$i]) != '') |
||
2860 | && ! $_SESSION['tmpval']['hide_transformation'] |
||
2861 | ) { |
||
2862 | include_once $this->transformation_info[$dbLower][$tblLower][$nameLower][0]; |
||
2863 | $transformation_plugin = new $this->transformation_info[$dbLower][$tblLower][$nameLower][1](null); |
||
2864 | |||
2865 | $transform_options = $this->transformations->getOptions( |
||
2866 | isset($mime_map[$orgFullColName]['transformation_options']) |
||
2867 | ? $mime_map[$orgFullColName]['transformation_options'] |
||
2868 | : '' |
||
2869 | ); |
||
2870 | |||
2871 | $meta->mimetype = str_replace( |
||
2872 | '_', |
||
2873 | '/', |
||
2874 | $this->transformation_info[$dbLower][mb_strtolower($meta->orgtable)][mb_strtolower($meta->orgname)][2] |
||
2875 | ); |
||
2876 | } |
||
2877 | |||
2878 | // Check for the predefined fields need to show as link in schemas |
||
2879 | $specialSchemaLinks = SpecialSchemaLinks::get(); |
||
2880 | |||
2881 | if (! empty($specialSchemaLinks[$dbLower][$tblLower][$nameLower])) { |
||
2882 | $linking_url = $this->_getSpecialLinkUrl( |
||
2883 | $specialSchemaLinks, |
||
2884 | $row[$i], |
||
2885 | $row_info, |
||
2886 | mb_strtolower($meta->orgname) |
||
2887 | ); |
||
2888 | $transformation_plugin = new Text_Plain_Link(); |
||
2889 | |||
2890 | $transform_options = [ |
||
2891 | 0 => $linking_url, |
||
2892 | 2 => true, |
||
2893 | ]; |
||
2894 | |||
2895 | $meta->mimetype = str_replace( |
||
2896 | '_', |
||
2897 | '/', |
||
2898 | 'Text/Plain' |
||
2899 | ); |
||
2900 | } |
||
2901 | |||
2902 | /* |
||
2903 | * The result set can have columns from more than one table, |
||
2904 | * this is why we have to check for the unique conditions |
||
2905 | * related to this table; however getUniqueCondition() is |
||
2906 | * costly and does not need to be called if we already know |
||
2907 | * the conditions for the current table. |
||
2908 | */ |
||
2909 | if (! isset($whereClauseMap[$row_no][$meta->orgtable])) { |
||
2910 | $unique_conditions = Util::getUniqueCondition( |
||
2911 | $dt_result, // handle |
||
2912 | $this->__get('fields_cnt'), // fields_cnt |
||
2913 | $this->__get('fields_meta'), // fields_meta |
||
2914 | $row, // row |
||
2915 | false, // force_unique |
||
2916 | $meta->orgtable, // restrict_to_table |
||
2917 | $analyzed_sql_results // analyzed_sql_results |
||
2918 | ); |
||
2919 | $whereClauseMap[$row_no][$meta->orgtable] = $unique_conditions[0]; |
||
2920 | } |
||
2921 | |||
2922 | $_url_params = [ |
||
2923 | 'db' => $this->__get('db'), |
||
2924 | 'table' => $meta->orgtable, |
||
2925 | 'where_clause' => $whereClauseMap[$row_no][$meta->orgtable], |
||
2926 | 'transform_key' => $meta->orgname, |
||
2927 | ]; |
||
2928 | |||
2929 | if (! empty($sql_query)) { |
||
2930 | $_url_params['sql_query'] = $url_sql_query; |
||
2931 | } |
||
2932 | |||
2933 | $transform_options['wrapper_link'] = Url::getCommon($_url_params); |
||
2934 | |||
2935 | $display_params = $this->__get('display_params'); |
||
2936 | |||
2937 | // in some situations (issue 11406), numeric returns 1 |
||
2938 | // even for a string type |
||
2939 | // for decimal numeric is returning 1 |
||
2940 | // have to improve logic |
||
2941 | if (($meta->numeric == 1 && $meta->type != 'string') || $meta->type == 'real') { |
||
2942 | // n u m e r i c |
||
2943 | |||
2944 | $display_params['data'][$row_no][$i] |
||
2945 | = $this->_getDataCellForNumericColumns( |
||
2946 | (string) $row[$i], |
||
2947 | $class, |
||
2948 | $condition_field, |
||
2949 | $meta, |
||
2950 | $map, |
||
2951 | $is_field_truncated, |
||
2952 | $analyzed_sql_results, |
||
2953 | $transformation_plugin, |
||
2954 | $default_function, |
||
2955 | $transform_options |
||
2956 | ); |
||
2957 | } elseif ($meta->type == self::GEOMETRY_FIELD) { |
||
2958 | // g e o m e t r y |
||
2959 | |||
2960 | // Remove 'grid_edit' from $class as we do not allow to |
||
2961 | // inline-edit geometry data. |
||
2962 | $class = str_replace('grid_edit', '', $class); |
||
2963 | |||
2964 | $display_params['data'][$row_no][$i] |
||
2965 | = $this->_getDataCellForGeometryColumns( |
||
2966 | $row[$i], |
||
2967 | $class, |
||
2968 | $meta, |
||
2969 | $map, |
||
2970 | $_url_params, |
||
2971 | $condition_field, |
||
2972 | $transformation_plugin, |
||
2973 | $default_function, |
||
2974 | $transform_options, |
||
2975 | $analyzed_sql_results |
||
2976 | ); |
||
2977 | } else { |
||
2978 | // n o t n u m e r i c |
||
2979 | |||
2980 | $display_params['data'][$row_no][$i] |
||
2981 | = $this->_getDataCellForNonNumericColumns( |
||
2982 | $row[$i], |
||
2983 | $class, |
||
2984 | $meta, |
||
2985 | $map, |
||
2986 | $_url_params, |
||
2987 | $condition_field, |
||
2988 | $transformation_plugin, |
||
2989 | $default_function, |
||
2990 | $transform_options, |
||
2991 | $is_field_truncated, |
||
2992 | $analyzed_sql_results, |
||
2993 | $dt_result, |
||
2994 | $i |
||
2995 | ); |
||
2996 | } |
||
2997 | |||
2998 | // output stored cell |
||
2999 | $row_values_html .= $display_params['data'][$row_no][$i]; |
||
3000 | |||
3001 | if (isset($display_params['rowdata'][$i][$row_no])) { |
||
3002 | $display_params['rowdata'][$i][$row_no] |
||
3003 | .= $display_params['data'][$row_no][$i]; |
||
3004 | } else { |
||
3005 | $display_params['rowdata'][$i][$row_no] |
||
3006 | = $display_params['data'][$row_no][$i]; |
||
3007 | } |
||
3008 | |||
3009 | $this->__set('display_params', $display_params); |
||
3010 | } // end for |
||
3011 | |||
3012 | return $row_values_html; |
||
3013 | } |
||
3014 | |||
3015 | /** |
||
3016 | * Get link for display special schema links |
||
3017 | * |
||
3018 | * @param array $specialSchemaLinks special schema links |
||
3019 | * @param string $column_value column value |
||
3020 | * @param array $row_info information about row |
||
3021 | * @param string $field_name column name |
||
3022 | * |
||
3023 | * @return string generated link |
||
3024 | */ |
||
3025 | private function _getSpecialLinkUrl( |
||
3026 | array $specialSchemaLinks, |
||
3027 | $column_value, |
||
3028 | array $row_info, |
||
3029 | $field_name |
||
3030 | ) { |
||
3031 | $linking_url_params = []; |
||
3032 | $link_relations = $specialSchemaLinks[mb_strtolower($this->__get('db'))][mb_strtolower($this->__get('table'))][$field_name]; |
||
3033 | |||
3034 | if (! is_array($link_relations['link_param'])) { |
||
3035 | $linking_url_params[$link_relations['link_param']] = $column_value; |
||
3036 | } else { |
||
3037 | // Consider only the case of creating link for column field |
||
3038 | // sql query that needs to be passed as url param |
||
3039 | $sql = 'SELECT `' . $column_value . '` FROM `' |
||
3040 | . $row_info[$link_relations['link_param'][1]] . '`.`' |
||
3041 | . $row_info[$link_relations['link_param'][2]] . '`'; |
||
3042 | $linking_url_params[$link_relations['link_param'][0]] = $sql; |
||
3043 | } |
||
3044 | |||
3045 | $divider = strpos($link_relations['default_page'], '?') ? '&' : '?'; |
||
3046 | if (empty($link_relations['link_dependancy_params'])) { |
||
3047 | return $link_relations['default_page'] |
||
3048 | . Url::getCommonRaw($linking_url_params, $divider); |
||
3049 | } |
||
3050 | |||
3051 | foreach ($link_relations['link_dependancy_params'] as $new_param) { |
||
3052 | // If param_info is an array, set the key and value |
||
3053 | // from that array |
||
3054 | if (is_array($new_param['param_info'])) { |
||
3055 | $linking_url_params[$new_param['param_info'][0]] |
||
3056 | = $new_param['param_info'][1]; |
||
3057 | continue; |
||
3058 | } |
||
3059 | |||
3060 | $linking_url_params[$new_param['param_info']] |
||
3061 | = $row_info[mb_strtolower($new_param['column_name'])]; |
||
3062 | |||
3063 | // Special case 1 - when executing routines, according |
||
3064 | // to the type of the routine, url param changes |
||
3065 | if (empty($row_info['routine_type'])) { |
||
3066 | continue; |
||
3067 | } |
||
3068 | } |
||
3069 | |||
3070 | return $link_relations['default_page'] |
||
3071 | . Url::getCommonRaw($linking_url_params, $divider); |
||
3072 | } |
||
3073 | |||
3074 | /** |
||
3075 | * Prepare row information for display special links |
||
3076 | * |
||
3077 | * @param array $row current row data |
||
3078 | * @param array|boolean $col_order the column order |
||
3079 | * |
||
3080 | * @return array associative array with column nama -> value |
||
3081 | */ |
||
3082 | private function _getRowInfoForSpecialLinks(array $row, $col_order) |
||
3083 | { |
||
3084 | |||
3085 | $row_info = []; |
||
3086 | $fields_meta = $this->__get('fields_meta'); |
||
3087 | |||
3088 | for ($n = 0; $n < $this->__get('fields_cnt'); ++$n) { |
||
3089 | $m = $col_order ? $col_order[$n] : $n; |
||
3090 | $row_info[mb_strtolower($fields_meta[$m]->orgname)] |
||
3091 | = $row[$m]; |
||
3092 | } |
||
3093 | |||
3094 | return $row_info; |
||
3095 | } |
||
3096 | |||
3097 | /** |
||
3098 | * Get url sql query without conditions to shorten URLs |
||
3099 | * |
||
3100 | * @param array $analyzed_sql_results analyzed sql results |
||
3101 | * |
||
3102 | * @return string analyzed sql query |
||
3103 | * |
||
3104 | * @access private |
||
3105 | * |
||
3106 | * @see _getTableBody() |
||
3107 | */ |
||
3108 | private function _getUrlSqlQuery(array $analyzed_sql_results) |
||
3109 | { |
||
3110 | if (($analyzed_sql_results['querytype'] != 'SELECT') |
||
3111 | || (mb_strlen($this->__get('sql_query')) < 200) |
||
3112 | ) { |
||
3113 | return $this->__get('sql_query'); |
||
3114 | } |
||
3115 | |||
3116 | $query = 'SELECT ' . Query::getClause( |
||
3117 | $analyzed_sql_results['statement'], |
||
3118 | $analyzed_sql_results['parser']->list, |
||
3119 | 'SELECT' |
||
3120 | ); |
||
3121 | |||
3122 | $from_clause = Query::getClause( |
||
3123 | $analyzed_sql_results['statement'], |
||
3124 | $analyzed_sql_results['parser']->list, |
||
3125 | 'FROM' |
||
3126 | ); |
||
3127 | |||
3128 | if (! empty($from_clause)) { |
||
3129 | $query .= ' FROM ' . $from_clause; |
||
3130 | } |
||
3131 | |||
3132 | return $query; |
||
3133 | } |
||
3134 | |||
3135 | /** |
||
3136 | * Get column order and column visibility |
||
3137 | * |
||
3138 | * @param array $analyzed_sql_results analyzed sql results |
||
3139 | * |
||
3140 | * @return array 2 element array - $col_order, $col_visib |
||
3141 | * |
||
3142 | * @access private |
||
3143 | * |
||
3144 | * @see _getTableBody() |
||
3145 | */ |
||
3146 | private function _getColumnParams(array $analyzed_sql_results) |
||
3147 | { |
||
3148 | if ($this->_isSelect($analyzed_sql_results)) { |
||
3149 | $pmatable = new Table($this->__get('table'), $this->__get('db')); |
||
3150 | $col_order = $pmatable->getUiProp(Table::PROP_COLUMN_ORDER); |
||
3151 | /* Validate the value */ |
||
3152 | if ($col_order !== false) { |
||
3153 | $fields_cnt = $this->__get('fields_cnt'); |
||
3154 | foreach ($col_order as $value) { |
||
3155 | if ($value >= $fields_cnt) { |
||
3156 | $pmatable->removeUiProp(Table::PROP_COLUMN_ORDER); |
||
3157 | $fields_cnt = false; |
||
3158 | } |
||
3159 | } |
||
3160 | } |
||
3161 | $col_visib = $pmatable->getUiProp(Table::PROP_COLUMN_VISIB); |
||
3162 | } else { |
||
3163 | $col_order = false; |
||
3164 | $col_visib = false; |
||
3165 | } |
||
3166 | |||
3167 | return [ |
||
3168 | $col_order, |
||
3169 | $col_visib, |
||
3170 | ]; |
||
3171 | } |
||
3172 | |||
3173 | /** |
||
3174 | * Get HTML for repeating headers |
||
3175 | * |
||
3176 | * @param array $display_params holds various display info |
||
3177 | * |
||
3178 | * @return string html content |
||
3179 | * |
||
3180 | * @access private |
||
3181 | * |
||
3182 | * @see _getTableBody() |
||
3183 | */ |
||
3184 | private function _getRepeatingHeaders( |
||
3185 | array $display_params |
||
3186 | ) { |
||
3187 | $header_html = '<tr>' . "\n"; |
||
3188 | |||
3189 | if ($display_params['emptypre'] > 0) { |
||
3190 | $header_html .= ' <th colspan="' |
||
3191 | . $display_params['emptypre'] . '">' |
||
3192 | . "\n" . ' </th>' . "\n"; |
||
3193 | } elseif ($GLOBALS['cfg']['RowActionLinks'] == self::POSITION_NONE) { |
||
3194 | $header_html .= ' <th></th>' . "\n"; |
||
3195 | } |
||
3196 | |||
3197 | foreach ($display_params['desc'] as $val) { |
||
3198 | $header_html .= $val; |
||
3199 | } |
||
3200 | |||
3201 | if ($display_params['emptyafter'] > 0) { |
||
3202 | $header_html |
||
3203 | .= ' <th colspan="' . $display_params['emptyafter'] |
||
3204 | . '">' |
||
3205 | . "\n" . ' </th>' . "\n"; |
||
3206 | } |
||
3207 | $header_html .= '</tr>' . "\n"; |
||
3208 | |||
3209 | return $header_html; |
||
3210 | } |
||
3211 | |||
3212 | /** |
||
3213 | * Get modified links |
||
3214 | * |
||
3215 | * @param string $where_clause the where clause of the sql |
||
3216 | * @param boolean $clause_is_unique the unique condition of clause |
||
3217 | * @param string $url_sql_query the analyzed sql query |
||
3218 | * |
||
3219 | * @return array 5 element array - $edit_url, $copy_url, |
||
3220 | * $edit_str, $copy_str, $edit_anchor_class |
||
3221 | * |
||
3222 | * @access private |
||
3223 | * |
||
3224 | * @see _getTableBody() |
||
3225 | */ |
||
3226 | private function _getModifiedLinks( |
||
3227 | $where_clause, |
||
3228 | $clause_is_unique, |
||
3229 | $url_sql_query |
||
3230 | ) { |
||
3231 | |||
3232 | $_url_params = [ |
||
3233 | 'db' => $this->__get('db'), |
||
3234 | 'table' => $this->__get('table'), |
||
3235 | 'where_clause' => $where_clause, |
||
3236 | 'clause_is_unique' => $clause_is_unique, |
||
3237 | 'sql_query' => $url_sql_query, |
||
3238 | 'goto' => 'sql.php', |
||
3239 | ]; |
||
3240 | |||
3241 | $edit_url = Url::getFromRoute( |
||
3242 | '/table/change', |
||
3243 | $_url_params + ['default_action' => 'update'] |
||
3244 | ); |
||
3245 | |||
3246 | $copy_url = Url::getFromRoute( |
||
3247 | '/table/change', |
||
3248 | $_url_params + ['default_action' => 'insert'] |
||
3249 | ); |
||
3250 | |||
3251 | $edit_str = $this->_getActionLinkContent( |
||
3252 | 'b_edit', |
||
3253 | __('Edit') |
||
3254 | ); |
||
3255 | $copy_str = $this->_getActionLinkContent( |
||
3256 | 'b_insrow', |
||
3257 | __('Copy') |
||
3258 | ); |
||
3259 | |||
3260 | // Class definitions required for grid editing jQuery scripts |
||
3261 | $edit_anchor_class = "edit_row_anchor"; |
||
3262 | if ($clause_is_unique == 0) { |
||
3263 | $edit_anchor_class .= ' nonunique'; |
||
3264 | } |
||
3265 | |||
3266 | return [ |
||
3267 | $edit_url, |
||
3268 | $copy_url, |
||
3269 | $edit_str, |
||
3270 | $copy_str, |
||
3271 | $edit_anchor_class, |
||
3272 | ]; |
||
3273 | } |
||
3274 | |||
3275 | /** |
||
3276 | * Get delete and kill links |
||
3277 | * |
||
3278 | * @param string $where_clause the where clause of the sql |
||
3279 | * @param boolean $clause_is_unique the unique condition of clause |
||
3280 | * @param string $url_sql_query the analyzed sql query |
||
3281 | * @param string $del_lnk the delete link of current row |
||
3282 | * @param array $row the current row |
||
3283 | * |
||
3284 | * @return array 3 element array |
||
3285 | * $del_url, $del_str, $js_conf |
||
3286 | * |
||
3287 | * @access private |
||
3288 | * |
||
3289 | * @see _getTableBody() |
||
3290 | */ |
||
3291 | private function _getDeleteAndKillLinks( |
||
3292 | $where_clause, |
||
3293 | $clause_is_unique, |
||
3294 | $url_sql_query, |
||
3295 | $del_lnk, |
||
3296 | array $row |
||
3297 | ) { |
||
3298 | |||
3299 | $goto = $this->__get('goto'); |
||
3300 | |||
3301 | if ($del_lnk == self::DELETE_ROW) { // delete row case |
||
3302 | $_url_params = [ |
||
3303 | 'db' => $this->__get('db'), |
||
3304 | 'table' => $this->__get('table'), |
||
3305 | 'sql_query' => $url_sql_query, |
||
3306 | 'message_to_show' => __('The row has been deleted.'), |
||
3307 | 'goto' => empty($goto) ? 'tbl_sql.php' : $goto, |
||
3308 | ]; |
||
3309 | |||
3310 | $lnk_goto = 'sql.php' . Url::getCommonRaw($_url_params); |
||
3311 | |||
3312 | $del_query = 'DELETE FROM ' |
||
3313 | . Util::backquote($this->__get('table')) |
||
3314 | . ' WHERE ' . $where_clause . |
||
3315 | ($clause_is_unique ? '' : ' LIMIT 1'); |
||
3316 | |||
3317 | $_url_params = [ |
||
3318 | 'db' => $this->__get('db'), |
||
3319 | 'table' => $this->__get('table'), |
||
3320 | 'sql_query' => $del_query, |
||
3321 | 'message_to_show' => __('The row has been deleted.'), |
||
3322 | 'goto' => $lnk_goto, |
||
3323 | ]; |
||
3324 | $del_url = 'sql.php' . Url::getCommon($_url_params); |
||
3325 | |||
3326 | $js_conf = 'DELETE FROM ' . Sanitize::jsFormat($this->__get('table')) |
||
3327 | . ' WHERE ' . Sanitize::jsFormat($where_clause, false) |
||
3328 | . ($clause_is_unique ? '' : ' LIMIT 1'); |
||
3329 | |||
3330 | $del_str = $this->_getActionLinkContent('b_drop', __('Delete')); |
||
3331 | } elseif ($del_lnk == self::KILL_PROCESS) { // kill process case |
||
3332 | $_url_params = [ |
||
3333 | 'db' => $this->__get('db'), |
||
3334 | 'table' => $this->__get('table'), |
||
3335 | 'sql_query' => $url_sql_query, |
||
3336 | 'goto' => 'index.php', |
||
3337 | ]; |
||
3338 | |||
3339 | $lnk_goto = 'sql.php' . Url::getCommonRaw($_url_params); |
||
3340 | |||
3341 | $kill = $GLOBALS['dbi']->getKillQuery($row[0]); |
||
3342 | |||
3343 | $_url_params = [ |
||
3344 | 'db' => 'mysql', |
||
3345 | 'sql_query' => $kill, |
||
3346 | 'goto' => $lnk_goto, |
||
3347 | ]; |
||
3348 | |||
3349 | $del_url = 'sql.php' . Url::getCommon($_url_params); |
||
3350 | $js_conf = $kill; |
||
3351 | $del_str = Util::getIcon( |
||
3352 | 'b_drop', |
||
3353 | __('Kill') |
||
3354 | ); |
||
3355 | } else { |
||
3356 | $del_url = $del_str = $js_conf = null; |
||
3357 | } |
||
3358 | |||
3359 | return [ |
||
3360 | $del_url, |
||
3361 | $del_str, |
||
3362 | $js_conf, |
||
3363 | ]; |
||
3364 | } |
||
3365 | |||
3366 | /** |
||
3367 | * Get content inside the table row action links (Edit/Copy/Delete) |
||
3368 | * |
||
3369 | * @param string $icon The name of the file to get |
||
3370 | * @param string $display_text The text displaying after the image icon |
||
3371 | * |
||
3372 | * @return string |
||
3373 | * |
||
3374 | * @access private |
||
3375 | * |
||
3376 | * @see _getModifiedLinks(), _getDeleteAndKillLinks() |
||
3377 | */ |
||
3378 | private function _getActionLinkContent($icon, $display_text) |
||
3379 | { |
||
3380 | |||
3381 | $linkContent = ''; |
||
3382 | |||
3383 | if (isset($GLOBALS['cfg']['RowActionType']) |
||
3384 | && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_ICONS |
||
3385 | ) { |
||
3386 | $linkContent .= '<span class="nowrap">' |
||
3387 | . Util::getImage( |
||
3388 | $icon, |
||
3389 | $display_text |
||
3390 | ) |
||
3391 | . '</span>'; |
||
3392 | } elseif (isset($GLOBALS['cfg']['RowActionType']) |
||
3393 | && $GLOBALS['cfg']['RowActionType'] == self::ACTION_LINK_CONTENT_TEXT |
||
3394 | ) { |
||
3395 | $linkContent .= '<span class="nowrap">' . $display_text . '</span>'; |
||
3396 | } else { |
||
3397 | $linkContent .= Util::getIcon( |
||
3398 | $icon, |
||
3399 | $display_text |
||
3400 | ); |
||
3401 | } |
||
3402 | |||
3403 | return $linkContent; |
||
3404 | } |
||
3405 | |||
3406 | /** |
||
3407 | * Prepare placed links |
||
3408 | * |
||
3409 | * @param string $dir the direction of links should place |
||
3410 | * @param string $del_url the url for delete row |
||
3411 | * @param array $displayParts which elements to display |
||
3412 | * @param integer $row_no the index of current row |
||
3413 | * @param string $where_clause the where clause of the sql |
||
3414 | * @param string $where_clause_html the html encoded where clause |
||
3415 | * @param array $condition_array array of keys (primary, unique, condition) |
||
3416 | * @param string $edit_url the url for edit row |
||
3417 | * @param string $copy_url the url for copy row |
||
3418 | * @param string $edit_anchor_class the class for html element for edit |
||
3419 | * @param string $edit_str the label for edit row |
||
3420 | * @param string $copy_str the label for copy row |
||
3421 | * @param string $del_str the label for delete row |
||
3422 | * @param string|null $js_conf text for the JS confirmation |
||
3423 | * |
||
3424 | * @return string html content |
||
3425 | * |
||
3426 | * @access private |
||
3427 | * |
||
3428 | * @see _getTableBody() |
||
3429 | */ |
||
3430 | private function _getPlacedLinks( |
||
3431 | $dir, |
||
3432 | $del_url, |
||
3433 | array $displayParts, |
||
3434 | $row_no, |
||
3435 | $where_clause, |
||
3436 | $where_clause_html, |
||
3437 | array $condition_array, |
||
3438 | $edit_url, |
||
3439 | $copy_url, |
||
3440 | $edit_anchor_class, |
||
3441 | $edit_str, |
||
3442 | $copy_str, |
||
3443 | $del_str, |
||
3444 | ?string $js_conf |
||
3445 | ) { |
||
3446 | |||
3447 | if (! isset($js_conf)) { |
||
3448 | $js_conf = ''; |
||
3449 | } |
||
3450 | |||
3451 | return $this->_getCheckboxAndLinks( |
||
3452 | $dir, |
||
3453 | $del_url, |
||
3454 | $displayParts, |
||
3455 | $row_no, |
||
3456 | $where_clause, |
||
3457 | $where_clause_html, |
||
3458 | $condition_array, |
||
3459 | $edit_url, |
||
3460 | $copy_url, |
||
3461 | $edit_anchor_class, |
||
3462 | $edit_str, |
||
3463 | $copy_str, |
||
3464 | $del_str, |
||
3465 | $js_conf |
||
3466 | ); |
||
3467 | } |
||
3468 | |||
3469 | /** |
||
3470 | * Get the combined classes for a column |
||
3471 | * |
||
3472 | * @param string $grid_edit_class the class for all editable columns |
||
3473 | * @param string $not_null_class the class for not null columns |
||
3474 | * @param string $relation_class the class for relations in a column |
||
3475 | * @param string $hide_class the class for visibility of a column |
||
3476 | * @param string $field_type_class the class related to type of the field |
||
3477 | * |
||
3478 | * @return string the combined classes |
||
3479 | * |
||
3480 | * @access private |
||
3481 | * |
||
3482 | * @see _getTableBody() |
||
3483 | */ |
||
3484 | private function _getClassesForColumn( |
||
3485 | $grid_edit_class, |
||
3486 | $not_null_class, |
||
3487 | $relation_class, |
||
3488 | $hide_class, |
||
3489 | $field_type_class |
||
3490 | ) { |
||
3491 | return 'data ' . $grid_edit_class . ' ' . $not_null_class . ' ' |
||
3492 | . $relation_class . ' ' . $hide_class . ' ' . $field_type_class; |
||
3493 | } |
||
3494 | |||
3495 | /** |
||
3496 | * Get class for datetime related fields |
||
3497 | * |
||
3498 | * @param string $type the type of the column field |
||
3499 | * |
||
3500 | * @return string the class for the column |
||
3501 | * |
||
3502 | * @access private |
||
3503 | * |
||
3504 | * @see _getTableBody() |
||
3505 | */ |
||
3506 | private function _getClassForDateTimeRelatedFields($type) |
||
3507 | { |
||
3508 | if ((substr($type, 0, 9) == self::TIMESTAMP_FIELD) |
||
3509 | || ($type == self::DATETIME_FIELD) |
||
3510 | ) { |
||
3511 | $field_type_class = 'datetimefield'; |
||
3512 | } elseif ($type == self::DATE_FIELD) { |
||
3513 | $field_type_class = 'datefield'; |
||
3514 | } elseif ($type == self::TIME_FIELD) { |
||
3515 | $field_type_class = 'timefield'; |
||
3516 | } elseif ($type == self::STRING_FIELD) { |
||
3517 | $field_type_class = 'text'; |
||
3518 | } else { |
||
3519 | $field_type_class = ''; |
||
3520 | } |
||
3521 | return $field_type_class; |
||
3522 | } |
||
3523 | |||
3524 | /** |
||
3525 | * Prepare data cell for numeric type fields |
||
3526 | * |
||
3527 | * @param string|null $column the column's value |
||
3528 | * @param string $class the html class for column |
||
3529 | * @param boolean $condition_field the column should highlighted |
||
3530 | * or not |
||
3531 | * @param stdClass $meta the meta-information about this |
||
3532 | * field |
||
3533 | * @param array $map the list of relations |
||
3534 | * @param boolean $is_field_truncated the condition for blob data |
||
3535 | * replacements |
||
3536 | * @param array $analyzed_sql_results the analyzed query |
||
3537 | * @param TransformationsPlugin $transformation_plugin the name of transformation plugin |
||
3538 | * @param string $default_function the default transformation |
||
3539 | * function |
||
3540 | * @param array $transform_options the transformation parameters |
||
3541 | * |
||
3542 | * @return string the prepared cell, html content |
||
3543 | * |
||
3544 | * @access private |
||
3545 | * |
||
3546 | * @see _getTableBody() |
||
3547 | */ |
||
3548 | private function _getDataCellForNumericColumns( |
||
3549 | ?string $column, |
||
3550 | $class, |
||
3551 | $condition_field, |
||
3552 | $meta, |
||
3553 | array $map, |
||
3554 | $is_field_truncated, |
||
3555 | array $analyzed_sql_results, |
||
3556 | $transformation_plugin, |
||
3557 | $default_function, |
||
3558 | array $transform_options |
||
3559 | ) { |
||
3560 | |||
3561 | if (! isset($column) || $column === null) { |
||
3562 | $cell = $this->_buildNullDisplay( |
||
3563 | 'right ' . $class, |
||
3564 | $condition_field, |
||
3565 | $meta, |
||
3566 | '' |
||
3567 | ); |
||
3568 | } elseif ($column != '') { |
||
3569 | $nowrap = ' nowrap'; |
||
3570 | $where_comparison = ' = ' . $column; |
||
3571 | |||
3572 | $cell = $this->_getRowData( |
||
3573 | 'right ' . $class, |
||
3574 | $condition_field, |
||
3575 | $analyzed_sql_results, |
||
3576 | $meta, |
||
3577 | $map, |
||
3578 | $column, |
||
3579 | $transformation_plugin, |
||
3580 | $default_function, |
||
3581 | $nowrap, |
||
3582 | $where_comparison, |
||
3583 | $transform_options, |
||
3584 | $is_field_truncated, |
||
3585 | '' |
||
3586 | ); |
||
3587 | } else { |
||
3588 | $cell = $this->_buildEmptyDisplay( |
||
3589 | 'right ' . $class, |
||
3590 | $condition_field, |
||
3591 | $meta, |
||
3592 | '' |
||
3593 | ); |
||
3594 | } |
||
3595 | |||
3596 | return $cell; |
||
3597 | } |
||
3598 | |||
3599 | /** |
||
3600 | * Get data cell for geometry type fields |
||
3601 | * |
||
3602 | * @param string|null $column the relevant column in data row |
||
3603 | * @param string $class the html class for column |
||
3604 | * @param stdClass $meta the meta-information about |
||
3605 | * this field |
||
3606 | * @param array $map the list of relations |
||
3607 | * @param array $_url_params the parameters for generate url |
||
3608 | * @param boolean $condition_field the column should highlighted |
||
3609 | * or not |
||
3610 | * @param TransformationsPlugin $transformation_plugin the name of transformation |
||
3611 | * function |
||
3612 | * @param string $default_function the default transformation |
||
3613 | * function |
||
3614 | * @param string $transform_options the transformation parameters |
||
3615 | * @param array $analyzed_sql_results the analyzed query |
||
3616 | * |
||
3617 | * @return string the prepared data cell, html content |
||
3618 | * |
||
3619 | * @access private |
||
3620 | * |
||
3621 | * @see _getTableBody() |
||
3622 | */ |
||
3623 | private function _getDataCellForGeometryColumns( |
||
3624 | ?string $column, |
||
3625 | $class, |
||
3626 | $meta, |
||
3627 | array $map, |
||
3628 | array $_url_params, |
||
3629 | $condition_field, |
||
3630 | $transformation_plugin, |
||
3631 | $default_function, |
||
3632 | $transform_options, |
||
3633 | array $analyzed_sql_results |
||
3634 | ) { |
||
3635 | if (! isset($column) || $column === null) { |
||
3636 | $cell = $this->_buildNullDisplay($class, $condition_field, $meta); |
||
3637 | return $cell; |
||
3638 | } |
||
3639 | |||
3640 | if ($column == '') { |
||
3641 | $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta); |
||
3642 | return $cell; |
||
3643 | } |
||
3644 | |||
3645 | // Display as [GEOMETRY - (size)] |
||
3646 | if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_GEOM) { |
||
3647 | $geometry_text = $this->_handleNonPrintableContents( |
||
3648 | strtoupper(self::GEOMETRY_FIELD), |
||
3649 | $column, |
||
3650 | $transformation_plugin, |
||
3651 | $transform_options, |
||
3652 | $default_function, |
||
3653 | $meta, |
||
3654 | $_url_params |
||
3655 | ); |
||
3656 | |||
3657 | $cell = $this->_buildValueDisplay( |
||
3658 | $class, |
||
3659 | $condition_field, |
||
3660 | $geometry_text |
||
3661 | ); |
||
3662 | return $cell; |
||
3663 | } |
||
3664 | |||
3665 | if ($_SESSION['tmpval']['geoOption'] == self::GEOMETRY_DISP_WKT) { |
||
3666 | // Prepare in Well Known Text(WKT) format. |
||
3667 | $where_comparison = ' = ' . $column; |
||
3668 | |||
3669 | // Convert to WKT format |
||
3670 | $wktval = Util::asWKT($column); |
||
3671 | list( |
||
3672 | $is_field_truncated, |
||
3673 | $wktval, |
||
3674 | // skip 3rd param |
||
3675 | ) = $this->_getPartialText($wktval); |
||
3676 | |||
3677 | $cell = $this->_getRowData( |
||
3678 | $class, |
||
3679 | $condition_field, |
||
3680 | $analyzed_sql_results, |
||
3681 | $meta, |
||
3682 | $map, |
||
3683 | $wktval, |
||
3684 | $transformation_plugin, |
||
3685 | $default_function, |
||
3686 | '', |
||
3687 | $where_comparison, |
||
3688 | $transform_options, |
||
3689 | $is_field_truncated, |
||
3690 | '' |
||
3691 | ); |
||
3692 | return $cell; |
||
3693 | } |
||
3694 | |||
3695 | // Prepare in Well Known Binary (WKB) format. |
||
3696 | |||
3697 | if ($_SESSION['tmpval']['display_binary']) { |
||
3698 | $where_comparison = ' = ' . $column; |
||
3699 | |||
3700 | $wkbval = substr(bin2hex($column), 8); |
||
3701 | list( |
||
3702 | $is_field_truncated, |
||
3703 | $wkbval, |
||
3704 | // skip 3rd param |
||
3705 | ) = $this->_getPartialText($wkbval); |
||
3706 | |||
3707 | $cell = $this->_getRowData( |
||
3708 | $class, |
||
3709 | $condition_field, |
||
3710 | $analyzed_sql_results, |
||
3711 | $meta, |
||
3712 | $map, |
||
3713 | $wkbval, |
||
3714 | $transformation_plugin, |
||
3715 | $default_function, |
||
3716 | '', |
||
3717 | $where_comparison, |
||
3718 | $transform_options, |
||
3719 | $is_field_truncated, |
||
3720 | '' |
||
3721 | ); |
||
3722 | return $cell; |
||
3723 | } |
||
3724 | |||
3725 | $wkbval = $this->_handleNonPrintableContents( |
||
3726 | self::BINARY_FIELD, |
||
3727 | $column, |
||
3728 | $transformation_plugin, |
||
3729 | $transform_options, |
||
3730 | $default_function, |
||
3731 | $meta, |
||
3732 | $_url_params |
||
3733 | ); |
||
3734 | |||
3735 | $cell = $this->_buildValueDisplay( |
||
3736 | $class, |
||
3737 | $condition_field, |
||
3738 | $wkbval |
||
3739 | ); |
||
3740 | |||
3741 | return $cell; |
||
3742 | } |
||
3743 | |||
3744 | /** |
||
3745 | * Get data cell for non numeric type fields |
||
3746 | * |
||
3747 | * @param string|null $column the relevant column in data row |
||
3748 | * @param string $class the html class for column |
||
3749 | * @param stdClass $meta the meta-information about |
||
3750 | * the field |
||
3751 | * @param array $map the list of relations |
||
3752 | * @param array $_url_params the parameters for generate |
||
3753 | * url |
||
3754 | * @param boolean $condition_field the column should highlighted |
||
3755 | * or not |
||
3756 | * @param TransformationsPlugin $transformation_plugin the name of transformation |
||
3757 | * function |
||
3758 | * @param string $default_function the default transformation |
||
3759 | * function |
||
3760 | * @param string $transform_options the transformation parameters |
||
3761 | * @param boolean $is_field_truncated is data truncated due to |
||
3762 | * LimitChars |
||
3763 | * @param array $analyzed_sql_results the analyzed query |
||
3764 | * @param integer $dt_result the link id associated to |
||
3765 | * the query which results |
||
3766 | * have to be displayed |
||
3767 | * @param integer $col_index the column index |
||
3768 | * |
||
3769 | * @return string the prepared data cell, html content |
||
3770 | * |
||
3771 | * @access private |
||
3772 | * |
||
3773 | * @see _getTableBody() |
||
3774 | */ |
||
3775 | private function _getDataCellForNonNumericColumns( |
||
3776 | ?string $column, |
||
3777 | $class, |
||
3778 | $meta, |
||
3779 | array $map, |
||
3780 | array $_url_params, |
||
3781 | $condition_field, |
||
3782 | $transformation_plugin, |
||
3783 | $default_function, |
||
3784 | $transform_options, |
||
3785 | $is_field_truncated, |
||
3786 | array $analyzed_sql_results, |
||
3787 | &$dt_result, |
||
3788 | $col_index |
||
3789 | ) { |
||
3790 | $original_length = 0; |
||
3791 | |||
3792 | $is_analyse = $this->__get('is_analyse'); |
||
3793 | $field_flags = $GLOBALS['dbi']->fieldFlags($dt_result, $col_index); |
||
3794 | |||
3795 | $bIsText = gettype($transformation_plugin) === 'object' |
||
3796 | && strpos($transformation_plugin->getMIMEType(), 'Text') |
||
3797 | === false; |
||
3798 | |||
3799 | // disable inline grid editing |
||
3800 | // if binary fields are protected |
||
3801 | // or transformation plugin is of non text type |
||
3802 | // such as image |
||
3803 | if ((false !== stripos($field_flags, self::BINARY_FIELD) |
||
3804 | && ($GLOBALS['cfg']['ProtectBinary'] === 'all' |
||
3805 | || ($GLOBALS['cfg']['ProtectBinary'] === 'noblob' |
||
3806 | && false === stripos($meta->type, self::BLOB_FIELD)) |
||
3807 | || ($GLOBALS['cfg']['ProtectBinary'] === 'blob' |
||
3808 | && false !== stripos($meta->type, self::BLOB_FIELD)))) |
||
3809 | || $bIsText |
||
3810 | ) { |
||
3811 | $class = str_replace('grid_edit', '', $class); |
||
3812 | } |
||
3813 | |||
3814 | if (! isset($column) || $column === null) { |
||
3815 | $cell = $this->_buildNullDisplay($class, $condition_field, $meta); |
||
3816 | return $cell; |
||
3817 | } |
||
3818 | |||
3819 | if ($column == '') { |
||
3820 | $cell = $this->_buildEmptyDisplay($class, $condition_field, $meta); |
||
3821 | return $cell; |
||
3822 | } |
||
3823 | |||
3824 | // Cut all fields to $GLOBALS['cfg']['LimitChars'] |
||
3825 | // (unless it's a link-type transformation or binary) |
||
3826 | if (! (gettype($transformation_plugin) === "object" |
||
3827 | && strpos($transformation_plugin->getName(), 'Link') !== false) |
||
3828 | && false === stripos($field_flags, self::BINARY_FIELD) |
||
3829 | ) { |
||
3830 | list( |
||
3831 | $is_field_truncated, |
||
3832 | $column, |
||
3833 | $original_length |
||
3834 | ) = $this->_getPartialText($column); |
||
3835 | } |
||
3836 | |||
3837 | $formatted = false; |
||
3838 | if (isset($meta->_type) && $meta->_type === MYSQLI_TYPE_BIT) { |
||
3839 | $column = Util::printableBitValue( |
||
3840 | (int) $column, |
||
3841 | (int) $meta->length |
||
3842 | ); |
||
3843 | |||
3844 | // some results of PROCEDURE ANALYSE() are reported as |
||
3845 | // being BINARY but they are quite readable, |
||
3846 | // so don't treat them as BINARY |
||
3847 | } elseif (false !== stripos($field_flags, self::BINARY_FIELD) |
||
3848 | && ! (isset($is_analyse) && $is_analyse) |
||
3849 | ) { |
||
3850 | // we show the BINARY or BLOB message and field's size |
||
3851 | // (or maybe use a transformation) |
||
3852 | $binary_or_blob = self::BLOB_FIELD; |
||
3853 | if ($meta->type === self::STRING_FIELD) { |
||
3854 | $binary_or_blob = self::BINARY_FIELD; |
||
3855 | } |
||
3856 | $column = $this->_handleNonPrintableContents( |
||
3857 | $binary_or_blob, |
||
3858 | $column, |
||
3859 | $transformation_plugin, |
||
3860 | $transform_options, |
||
3861 | $default_function, |
||
3862 | $meta, |
||
3863 | $_url_params, |
||
3864 | $is_field_truncated |
||
3865 | ); |
||
3866 | $class = $this->_addClass( |
||
3867 | $class, |
||
3868 | $condition_field, |
||
3869 | $meta, |
||
3870 | '', |
||
3871 | $is_field_truncated, |
||
3872 | $transformation_plugin, |
||
3873 | $default_function |
||
3874 | ); |
||
3875 | $result = strip_tags($column); |
||
3876 | // disable inline grid editing |
||
3877 | // if binary or blob data is not shown |
||
3878 | if (false !== stripos($result, $binary_or_blob)) { |
||
3879 | $class = str_replace('grid_edit', '', $class); |
||
3880 | } |
||
3881 | $formatted = true; |
||
3882 | } |
||
3883 | |||
3884 | if ($formatted) { |
||
3885 | $cell = $this->_buildValueDisplay( |
||
3886 | $class, |
||
3887 | $condition_field, |
||
3888 | $column |
||
3889 | ); |
||
3890 | return $cell; |
||
3891 | } |
||
3892 | |||
3893 | // transform functions may enable no-wrapping: |
||
3894 | $function_nowrap = 'applyTransformationNoWrap'; |
||
3895 | |||
3896 | $bool_nowrap = ($default_function != $transformation_plugin) |
||
3897 | && function_exists((string) $transformation_plugin->$function_nowrap()) |
||
3898 | ? $transformation_plugin->$function_nowrap($transform_options) |
||
3899 | : false; |
||
3900 | |||
3901 | // do not wrap if date field type |
||
3902 | $nowrap = preg_match('@DATE|TIME@i', $meta->type) |
||
3903 | || $bool_nowrap ? ' nowrap' : ''; |
||
3904 | |||
3905 | $where_comparison = ' = \'' |
||
3906 | . $GLOBALS['dbi']->escapeString($column) |
||
3907 | . '\''; |
||
3908 | |||
3909 | $cell = $this->_getRowData( |
||
3910 | $class, |
||
3911 | $condition_field, |
||
3912 | $analyzed_sql_results, |
||
3913 | $meta, |
||
3914 | $map, |
||
3915 | $column, |
||
3916 | $transformation_plugin, |
||
3917 | $default_function, |
||
3918 | $nowrap, |
||
3919 | $where_comparison, |
||
3920 | $transform_options, |
||
3921 | $is_field_truncated, |
||
3922 | $original_length |
||
3923 | ); |
||
3924 | |||
3925 | return $cell; |
||
3926 | } |
||
3927 | |||
3928 | /** |
||
3929 | * Checks the posted options for viewing query results |
||
3930 | * and sets appropriate values in the session. |
||
3931 | * |
||
3932 | * @todo make maximum remembered queries configurable |
||
3933 | * @todo move/split into SQL class!? |
||
3934 | * @todo currently this is called twice unnecessary |
||
3935 | * @todo ignore LIMIT and ORDER in query!? |
||
3936 | * |
||
3937 | * @return void |
||
3938 | * |
||
3939 | * @access public |
||
3940 | * |
||
3941 | * @see sql.php file |
||
3942 | */ |
||
3943 | public function setConfigParamsForDisplayTable() |
||
3944 | { |
||
3945 | |||
3946 | $sql_md5 = md5($this->__get('sql_query')); |
||
3947 | $query = []; |
||
3948 | if (isset($_SESSION['tmpval']['query'][$sql_md5])) { |
||
3949 | $query = $_SESSION['tmpval']['query'][$sql_md5]; |
||
3950 | } |
||
3951 | |||
3952 | $query['sql'] = $this->__get('sql_query'); |
||
3953 | |||
3954 | if (empty($query['repeat_cells'])) { |
||
3955 | $query['repeat_cells'] = $GLOBALS['cfg']['RepeatCells']; |
||
3956 | } |
||
3957 | |||
3958 | // as this is a form value, the type is always string so we cannot |
||
3959 | // use Core::isValid($_POST['session_max_rows'], 'integer') |
||
3960 | if (Core::isValid($_POST['session_max_rows'], 'numeric')) { |
||
3961 | $query['max_rows'] = (int) $_POST['session_max_rows']; |
||
3962 | unset($_POST['session_max_rows']); |
||
3963 | } elseif ($_POST['session_max_rows'] == self::ALL_ROWS) { |
||
3964 | $query['max_rows'] = self::ALL_ROWS; |
||
3965 | unset($_POST['session_max_rows']); |
||
3966 | } elseif (empty($query['max_rows'])) { |
||
3967 | $query['max_rows'] = intval($GLOBALS['cfg']['MaxRows']); |
||
3968 | } |
||
3969 | |||
3970 | if (Core::isValid($_REQUEST['pos'], 'numeric')) { |
||
3971 | $query['pos'] = $_REQUEST['pos']; |
||
3972 | unset($_REQUEST['pos']); |
||
3973 | } elseif (empty($query['pos'])) { |
||
3974 | $query['pos'] = 0; |
||
3975 | } |
||
3976 | |||
3977 | if (Core::isValid( |
||
3978 | $_REQUEST['pftext'], |
||
3979 | [ |
||
3980 | self::DISPLAY_PARTIAL_TEXT, |
||
3981 | self::DISPLAY_FULL_TEXT, |
||
3982 | ] |
||
3983 | ) |
||
3984 | ) { |
||
3985 | $query['pftext'] = $_REQUEST['pftext']; |
||
3986 | unset($_REQUEST['pftext']); |
||
3987 | } elseif (empty($query['pftext'])) { |
||
3988 | $query['pftext'] = self::DISPLAY_PARTIAL_TEXT; |
||
3989 | } |
||
3990 | |||
3991 | if (Core::isValid( |
||
3992 | $_REQUEST['relational_display'], |
||
3993 | [ |
||
3994 | self::RELATIONAL_KEY, |
||
3995 | self::RELATIONAL_DISPLAY_COLUMN, |
||
3996 | ] |
||
3997 | ) |
||
3998 | ) { |
||
3999 | $query['relational_display'] = $_REQUEST['relational_display']; |
||
4000 | unset($_REQUEST['relational_display']); |
||
4001 | } elseif (empty($query['relational_display'])) { |
||
4002 | // The current session value has priority over a |
||
4003 | // change via Settings; this change will be apparent |
||
4004 | // starting from the next session |
||
4005 | $query['relational_display'] = $GLOBALS['cfg']['RelationalDisplay']; |
||
4006 | } |
||
4007 | |||
4008 | if (Core::isValid( |
||
4009 | $_REQUEST['geoOption'], |
||
4010 | [ |
||
4011 | self::GEOMETRY_DISP_WKT, |
||
4012 | self::GEOMETRY_DISP_WKB, |
||
4013 | self::GEOMETRY_DISP_GEOM, |
||
4014 | ] |
||
4015 | ) |
||
4016 | ) { |
||
4017 | $query['geoOption'] = $_REQUEST['geoOption']; |
||
4018 | unset($_REQUEST['geoOption']); |
||
4019 | } elseif (empty($query['geoOption'])) { |
||
4020 | $query['geoOption'] = self::GEOMETRY_DISP_GEOM; |
||
4021 | } |
||
4022 | |||
4023 | if (isset($_REQUEST['display_binary'])) { |
||
4024 | $query['display_binary'] = true; |
||
4025 | unset($_REQUEST['display_binary']); |
||
4026 | } elseif (isset($_REQUEST['display_options_form'])) { |
||
4027 | // we know that the checkbox was unchecked |
||
4028 | unset($query['display_binary']); |
||
4029 | } elseif (! isset($_REQUEST['full_text_button'])) { |
||
4030 | // selected by default because some operations like OPTIMIZE TABLE |
||
4031 | // and all queries involving functions return "binary" contents, |
||
4032 | // according to low-level field flags |
||
4033 | $query['display_binary'] = true; |
||
4034 | } |
||
4035 | |||
4036 | if (isset($_REQUEST['display_blob'])) { |
||
4037 | $query['display_blob'] = true; |
||
4038 | unset($_REQUEST['display_blob']); |
||
4039 | } elseif (isset($_REQUEST['display_options_form'])) { |
||
4040 | // we know that the checkbox was unchecked |
||
4041 | unset($query['display_blob']); |
||
4042 | } |
||
4043 | |||
4044 | if (isset($_REQUEST['hide_transformation'])) { |
||
4045 | $query['hide_transformation'] = true; |
||
4046 | unset($_REQUEST['hide_transformation']); |
||
4047 | } elseif (isset($_REQUEST['display_options_form'])) { |
||
4048 | // we know that the checkbox was unchecked |
||
4049 | unset($query['hide_transformation']); |
||
4050 | } |
||
4051 | |||
4052 | // move current query to the last position, to be removed last |
||
4053 | // so only least executed query will be removed if maximum remembered |
||
4054 | // queries limit is reached |
||
4055 | unset($_SESSION['tmpval']['query'][$sql_md5]); |
||
4056 | $_SESSION['tmpval']['query'][$sql_md5] = $query; |
||
4057 | |||
4058 | // do not exceed a maximum number of queries to remember |
||
4059 | if (count($_SESSION['tmpval']['query']) > 10) { |
||
4060 | array_shift($_SESSION['tmpval']['query']); |
||
4061 | //echo 'deleting one element ...'; |
||
4062 | } |
||
4063 | |||
4064 | // populate query configuration |
||
4065 | $_SESSION['tmpval']['pftext'] |
||
4066 | = $query['pftext']; |
||
4067 | $_SESSION['tmpval']['relational_display'] |
||
4068 | = $query['relational_display']; |
||
4069 | $_SESSION['tmpval']['geoOption'] |
||
4070 | = $query['geoOption']; |
||
4071 | $_SESSION['tmpval']['display_binary'] = isset( |
||
4072 | $query['display_binary'] |
||
4073 | ); |
||
4074 | $_SESSION['tmpval']['display_blob'] = isset( |
||
4075 | $query['display_blob'] |
||
4076 | ); |
||
4077 | $_SESSION['tmpval']['hide_transformation'] = isset( |
||
4078 | $query['hide_transformation'] |
||
4079 | ); |
||
4080 | $_SESSION['tmpval']['pos'] |
||
4081 | = $query['pos']; |
||
4082 | $_SESSION['tmpval']['max_rows'] |
||
4083 | = $query['max_rows']; |
||
4084 | $_SESSION['tmpval']['repeat_cells'] |
||
4085 | = $query['repeat_cells']; |
||
4086 | } |
||
4087 | |||
4088 | /** |
||
4089 | * Prepare a table of results returned by a SQL query. |
||
4090 | * |
||
4091 | * @param integer $dt_result the link id associated to the query |
||
4092 | * which results have to be displayed |
||
4093 | * @param array $displayParts the parts to display |
||
4094 | * @param array $analyzed_sql_results analyzed sql results |
||
4095 | * @param boolean $is_limited_display With limited operations or not |
||
4096 | * |
||
4097 | * @return string Generated HTML content for resulted table |
||
4098 | * |
||
4099 | * @access public |
||
4100 | * |
||
4101 | * @see sql.php file |
||
4102 | */ |
||
4103 | public function getTable( |
||
4104 | &$dt_result, |
||
4105 | array &$displayParts, |
||
4106 | array $analyzed_sql_results, |
||
4107 | $is_limited_display = false |
||
4108 | ) { |
||
4109 | /** |
||
4110 | * The statement this table is built for. |
||
4111 | * @var SelectStatement |
||
4112 | */ |
||
4113 | if (isset($analyzed_sql_results['statement'])) { |
||
4114 | $statement = $analyzed_sql_results['statement']; |
||
4115 | } else { |
||
4116 | $statement = null; |
||
4117 | } |
||
4118 | |||
4119 | // Following variable are needed for use in isset/empty or |
||
4120 | // use with array indexes/safe use in foreach |
||
4121 | $fields_meta = $this->__get('fields_meta'); |
||
4122 | $showtable = $this->__get('showtable'); |
||
4123 | $printview = $this->__get('printview'); |
||
4124 | |||
4125 | /** |
||
4126 | * @todo move this to a central place |
||
4127 | * @todo for other future table types |
||
4128 | */ |
||
4129 | $is_innodb = (isset($showtable['Type']) |
||
4130 | && $showtable['Type'] == self::TABLE_TYPE_INNO_DB); |
||
4131 | |||
4132 | $sql = new Sql(); |
||
4133 | if ($is_innodb && $sql->isJustBrowsing($analyzed_sql_results, true)) { |
||
4134 | $pre_count = '~'; |
||
4135 | $after_count = Util::showHint( |
||
4136 | Sanitize::sanitizeMessage( |
||
4137 | __('May be approximate. See [doc@faq3-11]FAQ 3.11[/doc].') |
||
4138 | ) |
||
4139 | ); |
||
4140 | } else { |
||
4141 | $pre_count = ''; |
||
4142 | $after_count = ''; |
||
4143 | } |
||
4144 | |||
4145 | // 1. ----- Prepares the work ----- |
||
4146 | |||
4147 | // 1.1 Gets the information about which functionalities should be |
||
4148 | // displayed |
||
4149 | |||
4150 | list( |
||
4151 | $displayParts, |
||
4152 | $total |
||
4153 | ) = $this->_setDisplayPartsAndTotal($displayParts); |
||
4154 | |||
4155 | // 1.2 Defines offsets for the next and previous pages |
||
4156 | $pos_next = null; |
||
4157 | $pos_prev = null; |
||
4158 | if ($displayParts['nav_bar'] == '1') { |
||
4159 | list($pos_next, $pos_prev) = $this->_getOffsets(); |
||
4160 | } // end if |
||
4161 | |||
4162 | // 1.3 Extract sorting expressions. |
||
4163 | // we need $sort_expression and $sort_expression_nodirection |
||
4164 | // even if there are many table references |
||
4165 | $sort_expression = []; |
||
4166 | $sort_expression_nodirection = []; |
||
4167 | $sort_direction = []; |
||
4168 | |||
4169 | if ($statement !== null && ! empty($statement->order)) { |
||
4170 | foreach ($statement->order as $o) { |
||
4171 | $sort_expression[] = $o->expr->expr . ' ' . $o->type; |
||
4172 | $sort_expression_nodirection[] = $o->expr->expr; |
||
4173 | $sort_direction[] = $o->type; |
||
4174 | } |
||
4175 | } else { |
||
4176 | $sort_expression[] = ''; |
||
4177 | $sort_expression_nodirection[] = ''; |
||
4178 | $sort_direction[] = ''; |
||
4179 | } |
||
4180 | |||
4181 | $number_of_columns = count($sort_expression_nodirection); |
||
4182 | |||
4183 | // 1.4 Prepares display of first and last value of the sorted column |
||
4184 | $sorted_column_message = ''; |
||
4185 | for ($i = 0; $i < $number_of_columns; $i++) { |
||
4186 | $sorted_column_message .= $this->_getSortedColumnMessage( |
||
4187 | $dt_result, |
||
4188 | $sort_expression_nodirection[$i] |
||
4189 | ); |
||
4190 | } |
||
4191 | |||
4192 | // 2. ----- Prepare to display the top of the page ----- |
||
4193 | |||
4194 | // 2.1 Prepares a messages with position information |
||
4195 | $sqlQueryMessage = ''; |
||
4196 | if (($displayParts['nav_bar'] == '1') && isset($pos_next)) { |
||
4197 | $message = $this->_setMessageInformation( |
||
4198 | $sorted_column_message, |
||
4199 | $analyzed_sql_results, |
||
4200 | $total, |
||
4201 | $pos_next, |
||
4202 | $pre_count, |
||
4203 | $after_count |
||
4204 | ); |
||
4205 | |||
4206 | $sqlQueryMessage = Util::getMessage( |
||
4207 | $message, |
||
4208 | $this->__get('sql_query'), |
||
4209 | 'success' |
||
4210 | ); |
||
4211 | } elseif ((! isset($printview) || ($printview != '1')) && ! $is_limited_display) { |
||
4212 | $sqlQueryMessage = Util::getMessage( |
||
4213 | __('Your SQL query has been executed successfully.'), |
||
4214 | $this->__get('sql_query'), |
||
4215 | 'success' |
||
4216 | ); |
||
4217 | } |
||
4218 | |||
4219 | // 2.3 Prepare the navigation bars |
||
4220 | if (strlen($this->__get('table')) === 0) { |
||
4221 | if ($analyzed_sql_results['querytype'] == 'SELECT') { |
||
4222 | // table does not always contain a real table name, |
||
4223 | // for example in MySQL 5.0.x, the query SHOW STATUS |
||
4224 | // returns STATUS as a table name |
||
4225 | $this->__set('table', $fields_meta[0]->table); |
||
4226 | } else { |
||
4227 | $this->__set('table', ''); |
||
4228 | } |
||
4229 | } |
||
4230 | |||
4231 | // can the result be sorted? |
||
4232 | if ($displayParts['sort_lnk'] == '1' && $analyzed_sql_results['statement'] !== null) { |
||
4233 | // At this point, $sort_expression is an array |
||
4234 | list($unsorted_sql_query, $sort_by_key_html) |
||
4235 | = $this->_getUnsortedSqlAndSortByKeyDropDown( |
||
4236 | $analyzed_sql_results, |
||
4237 | $sort_expression |
||
4238 | ); |
||
4239 | } else { |
||
4240 | $sort_by_key_html = $unsorted_sql_query = ''; |
||
4241 | } |
||
4242 | |||
4243 | $navigation = ''; |
||
4244 | if ($displayParts['nav_bar'] == '1' && $statement !== null && empty($statement->limit)) { |
||
4245 | $navigation = $this->_getTableNavigation( |
||
4246 | $pos_next, |
||
4247 | $pos_prev, |
||
4248 | $is_innodb, |
||
4249 | $sort_by_key_html |
||
4250 | ); |
||
4251 | } |
||
4252 | |||
4253 | // 2b ----- Get field references from Database ----- |
||
4254 | // (see the 'relation' configuration variable) |
||
4255 | |||
4256 | // initialize map |
||
4257 | $map = []; |
||
4258 | |||
4259 | if (strlen($this->__get('table')) > 0) { |
||
4260 | // This method set the values for $map array |
||
4261 | $this->_setParamForLinkForeignKeyRelatedTables($map); |
||
4262 | |||
4263 | // Coming from 'Distinct values' action of structure page |
||
4264 | // We manipulate relations mechanism to show a link to related rows. |
||
4265 | if ($this->__get('is_browse_distinct')) { |
||
4266 | $map[$fields_meta[1]->name] = [ |
||
4267 | $this->__get('table'), |
||
4268 | $fields_meta[1]->name, |
||
4269 | '', |
||
4270 | $this->__get('db'), |
||
4271 | ]; |
||
4272 | } |
||
4273 | } // end if |
||
4274 | // end 2b |
||
4275 | |||
4276 | // 3. ----- Prepare the results table ----- |
||
4277 | $headers = $this->_getTableHeaders( |
||
4278 | $displayParts, |
||
4279 | $analyzed_sql_results, |
||
4280 | $unsorted_sql_query, |
||
4281 | $sort_expression, |
||
4282 | $sort_expression_nodirection, |
||
4283 | $sort_direction, |
||
4284 | $is_limited_display |
||
4285 | ); |
||
4286 | |||
4287 | $body = $this->_getTableBody( |
||
4288 | $dt_result, |
||
4289 | $displayParts, |
||
4290 | $map, |
||
4291 | $analyzed_sql_results, |
||
4292 | $is_limited_display |
||
4293 | ); |
||
4294 | |||
4295 | $this->__set('display_params', null); |
||
4296 | |||
4297 | // 4. ----- Prepares the link for multi-fields edit and delete |
||
4298 | $multiRowOperationLinks = ''; |
||
4299 | if ($displayParts['del_lnk'] == self::DELETE_ROW |
||
4300 | && $displayParts['del_lnk'] != self::KILL_PROCESS |
||
4301 | ) { |
||
4302 | $multiRowOperationLinks = $this->_getMultiRowOperationLinks( |
||
4303 | $dt_result, |
||
4304 | $analyzed_sql_results, |
||
4305 | $displayParts['del_lnk'] |
||
4306 | ); |
||
4307 | } |
||
4308 | |||
4309 | // 5. ----- Prepare "Query results operations" |
||
4310 | $operations = ''; |
||
4311 | if ((! isset($printview) || ($printview != '1')) && ! $is_limited_display) { |
||
4312 | $operations = $this->_getResultsOperations( |
||
4313 | $displayParts, |
||
4314 | $analyzed_sql_results |
||
4315 | ); |
||
4316 | } |
||
4317 | |||
4318 | return $this->template->render('display/results/table', [ |
||
4319 | 'sql_query_message' => $sqlQueryMessage, |
||
4320 | 'navigation' => $navigation, |
||
4321 | 'headers' => $headers, |
||
4322 | 'body' => $body, |
||
4323 | 'multi_row_operation_links' => $multiRowOperationLinks, |
||
4324 | 'operations' => $operations, |
||
4325 | ]); |
||
4326 | } |
||
4327 | |||
4328 | /** |
||
4329 | * Get offsets for next page and previous page |
||
4330 | * |
||
4331 | * @return array array with two elements - $pos_next, $pos_prev |
||
4332 | * |
||
4333 | * @access private |
||
4334 | * |
||
4335 | * @see getTable() |
||
4336 | */ |
||
4337 | private function _getOffsets() |
||
4338 | { |
||
4339 | |||
4340 | if ($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) { |
||
4341 | $pos_next = 0; |
||
4342 | $pos_prev = 0; |
||
4343 | } else { |
||
4344 | $pos_next = $_SESSION['tmpval']['pos'] |
||
4345 | + $_SESSION['tmpval']['max_rows']; |
||
4346 | |||
4347 | $pos_prev = $_SESSION['tmpval']['pos'] |
||
4348 | - $_SESSION['tmpval']['max_rows']; |
||
4349 | |||
4350 | if ($pos_prev < 0) { |
||
4351 | $pos_prev = 0; |
||
4352 | } |
||
4353 | } |
||
4354 | |||
4355 | return [ |
||
4356 | $pos_next, |
||
4357 | $pos_prev, |
||
4358 | ]; |
||
4359 | } |
||
4360 | |||
4361 | /** |
||
4362 | * Prepare sorted column message |
||
4363 | * |
||
4364 | * @param integer $dt_result the link id associated to the |
||
4365 | * query which results have to |
||
4366 | * be displayed |
||
4367 | * @param string $sort_expression_nodirection sort expression without direction |
||
4368 | * |
||
4369 | * @return string|null html content, null if not found sorted column |
||
4370 | * |
||
4371 | * @access private |
||
4372 | * |
||
4373 | * @see getTable() |
||
4374 | */ |
||
4375 | private function _getSortedColumnMessage( |
||
4376 | &$dt_result, |
||
4377 | $sort_expression_nodirection |
||
4378 | ) { |
||
4379 | $fields_meta = $this->__get('fields_meta'); // To use array indexes |
||
4380 | |||
4381 | if (empty($sort_expression_nodirection)) { |
||
4382 | return null; |
||
4383 | } |
||
4384 | |||
4385 | if (mb_strpos($sort_expression_nodirection, '.') === false) { |
||
4386 | $sort_table = $this->__get('table'); |
||
4387 | $sort_column = $sort_expression_nodirection; |
||
4388 | } else { |
||
4389 | list($sort_table, $sort_column) |
||
4390 | = explode('.', $sort_expression_nodirection); |
||
4391 | } |
||
4392 | |||
4393 | $sort_table = Util::unQuote($sort_table); |
||
4394 | $sort_column = Util::unQuote($sort_column); |
||
4395 | |||
4396 | // find the sorted column index in row result |
||
4397 | // (this might be a multi-table query) |
||
4398 | $sorted_column_index = false; |
||
4399 | |||
4400 | foreach ($fields_meta as $key => $meta) { |
||
4401 | if (($meta->table == $sort_table) && ($meta->name == $sort_column)) { |
||
4402 | $sorted_column_index = $key; |
||
4403 | break; |
||
4404 | } |
||
4405 | } |
||
4406 | |||
4407 | if ($sorted_column_index === false) { |
||
4408 | return null; |
||
4409 | } |
||
4410 | |||
4411 | // fetch first row of the result set |
||
4412 | $row = $GLOBALS['dbi']->fetchRow($dt_result); |
||
4413 | |||
4414 | // initializing default arguments |
||
4415 | $default_function = [ |
||
4416 | Core::class, |
||
4417 | 'mimeDefaultFunction', |
||
4418 | ]; |
||
4419 | $transformation_plugin = $default_function; |
||
4420 | $transform_options = []; |
||
4421 | |||
4422 | // check for non printable sorted row data |
||
4423 | $meta = $fields_meta[$sorted_column_index]; |
||
4424 | |||
4425 | if (false !== stripos($meta->type, self::BLOB_FIELD) |
||
4426 | || ($meta->type == self::GEOMETRY_FIELD) |
||
4427 | ) { |
||
4428 | $column_for_first_row = $this->_handleNonPrintableContents( |
||
4429 | $meta->type, |
||
4430 | $row[$sorted_column_index], |
||
4431 | $transformation_plugin, |
||
4432 | $transform_options, |
||
4433 | $default_function, |
||
4434 | $meta |
||
4435 | ); |
||
4436 | } else { |
||
4437 | $column_for_first_row = $row[$sorted_column_index]; |
||
4438 | } |
||
4439 | |||
4440 | $column_for_first_row = mb_strtoupper( |
||
4441 | mb_substr( |
||
4442 | (string) $column_for_first_row, |
||
4443 | 0, |
||
4444 | $GLOBALS['cfg']['LimitChars'] |
||
4445 | ) . '...' |
||
4446 | ); |
||
4447 | |||
4448 | // fetch last row of the result set |
||
4449 | $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1); |
||
4450 | $row = $GLOBALS['dbi']->fetchRow($dt_result); |
||
4451 | |||
4452 | // check for non printable sorted row data |
||
4453 | $meta = $fields_meta[$sorted_column_index]; |
||
4454 | if (false !== stripos($meta->type, self::BLOB_FIELD) |
||
4455 | || ($meta->type == self::GEOMETRY_FIELD) |
||
4456 | ) { |
||
4457 | $column_for_last_row = $this->_handleNonPrintableContents( |
||
4458 | $meta->type, |
||
4459 | $row[$sorted_column_index], |
||
4460 | $transformation_plugin, |
||
4461 | $transform_options, |
||
4462 | $default_function, |
||
4463 | $meta |
||
4464 | ); |
||
4465 | } else { |
||
4466 | $column_for_last_row = $row[$sorted_column_index]; |
||
4467 | } |
||
4468 | |||
4469 | $column_for_last_row = mb_strtoupper( |
||
4470 | mb_substr( |
||
4471 | (string) $column_for_last_row, |
||
4472 | 0, |
||
4473 | $GLOBALS['cfg']['LimitChars'] |
||
4474 | ) . '...' |
||
4475 | ); |
||
4476 | |||
4477 | // reset to first row for the loop in _getTableBody() |
||
4478 | $GLOBALS['dbi']->dataSeek($dt_result, 0); |
||
4479 | |||
4480 | // we could also use here $sort_expression_nodirection |
||
4481 | return ' [' . htmlspecialchars($sort_column) |
||
4482 | . ': <strong>' . htmlspecialchars($column_for_first_row) . ' - ' |
||
4483 | . htmlspecialchars($column_for_last_row) . '</strong>]'; |
||
4484 | } |
||
4485 | |||
4486 | /** |
||
4487 | * Set the content that needs to be shown in message |
||
4488 | * |
||
4489 | * @param string $sorted_column_message the message for sorted column |
||
4490 | * @param array $analyzed_sql_results the analyzed query |
||
4491 | * @param integer $total the total number of rows returned by |
||
4492 | * the SQL query without any |
||
4493 | * programmatically appended LIMIT clause |
||
4494 | * @param integer $pos_next the offset for next page |
||
4495 | * @param string $pre_count the string renders before row count |
||
4496 | * @param string $after_count the string renders after row count |
||
4497 | * |
||
4498 | * @return Message an object of Message |
||
4499 | * |
||
4500 | * @access private |
||
4501 | * |
||
4502 | * @see getTable() |
||
4503 | */ |
||
4504 | private function _setMessageInformation( |
||
4505 | $sorted_column_message, |
||
4506 | array $analyzed_sql_results, |
||
4507 | $total, |
||
4508 | $pos_next, |
||
4509 | $pre_count, |
||
4510 | $after_count |
||
4511 | ) { |
||
4512 | |||
4513 | $unlim_num_rows = $this->__get('unlim_num_rows'); // To use in isset() |
||
4514 | |||
4515 | if (! empty($analyzed_sql_results['statement']->limit)) { |
||
4516 | $first_shown_rec = $analyzed_sql_results['statement']->limit->offset; |
||
4517 | $row_count = $analyzed_sql_results['statement']->limit->rowCount; |
||
4518 | |||
4519 | if ($row_count < $total) { |
||
4520 | $last_shown_rec = $first_shown_rec + $row_count - 1; |
||
4521 | } else { |
||
4522 | $last_shown_rec = $first_shown_rec + $total - 1; |
||
4523 | } |
||
4524 | } elseif (($_SESSION['tmpval']['max_rows'] == self::ALL_ROWS) |
||
4525 | || ($pos_next > $total) |
||
4526 | ) { |
||
4527 | $first_shown_rec = $_SESSION['tmpval']['pos']; |
||
4528 | $last_shown_rec = $total - 1; |
||
4529 | } else { |
||
4530 | $first_shown_rec = $_SESSION['tmpval']['pos']; |
||
4531 | $last_shown_rec = $pos_next - 1; |
||
4532 | } |
||
4533 | |||
4534 | $table = new Table($this->__get('table'), $this->__get('db')); |
||
4535 | if ($table->isView() |
||
4536 | && ($total == $GLOBALS['cfg']['MaxExactCountViews']) |
||
4537 | ) { |
||
4538 | $message = Message::notice( |
||
4539 | __( |
||
4540 | 'This view has at least this number of rows. ' |
||
4541 | . 'Please refer to %sdocumentation%s.' |
||
4542 | ) |
||
4543 | ); |
||
4544 | |||
4545 | $message->addParam('[doc@cfg_MaxExactCount]'); |
||
4546 | $message->addParam('[/doc]'); |
||
4547 | $message_view_warning = Util::showHint($message); |
||
4548 | } else { |
||
4549 | $message_view_warning = false; |
||
4550 | } |
||
4551 | |||
4552 | $message = Message::success(__('Showing rows %1s - %2s')); |
||
4553 | $message->addParam($first_shown_rec); |
||
4554 | |||
4555 | if ($message_view_warning !== false) { |
||
4556 | $message->addParamHtml('... ' . $message_view_warning); |
||
4557 | } else { |
||
4558 | $message->addParam($last_shown_rec); |
||
4559 | } |
||
4560 | |||
4561 | $message->addText('('); |
||
4562 | |||
4563 | if ($message_view_warning === false) { |
||
4564 | if (isset($unlim_num_rows) && ($unlim_num_rows != $total)) { |
||
4565 | $message_total = Message::notice( |
||
4566 | $pre_count . __('%1$d total, %2$d in query') |
||
4567 | ); |
||
4568 | $message_total->addParam($total); |
||
4569 | $message_total->addParam($unlim_num_rows); |
||
4570 | } else { |
||
4571 | $message_total = Message::notice($pre_count . __('%d total')); |
||
4572 | $message_total->addParam($total); |
||
4573 | } |
||
4574 | |||
4575 | if (! empty($after_count)) { |
||
4576 | $message_total->addHtml($after_count); |
||
4577 | } |
||
4578 | $message->addMessage($message_total, ''); |
||
4579 | |||
4580 | $message->addText(', ', ''); |
||
4581 | } |
||
4582 | |||
4583 | $message_qt = Message::notice(__('Query took %01.4f seconds.') . ')'); |
||
4584 | $message_qt->addParam($this->__get('querytime')); |
||
4585 | |||
4586 | $message->addMessage($message_qt, ''); |
||
4587 | if ($sorted_column_message !== null) { |
||
4588 | $message->addHtml($sorted_column_message, ''); |
||
4589 | } |
||
4590 | |||
4591 | return $message; |
||
4592 | } |
||
4593 | |||
4594 | /** |
||
4595 | * Set the value of $map array for linking foreign key related tables |
||
4596 | * |
||
4597 | * @param array $map the list of relations |
||
4598 | * |
||
4599 | * @return void |
||
4600 | * |
||
4601 | * @access private |
||
4602 | * |
||
4603 | * @see getTable() |
||
4604 | */ |
||
4605 | private function _setParamForLinkForeignKeyRelatedTables(array &$map) |
||
4606 | { |
||
4607 | // To be able to later display a link to the related table, |
||
4608 | // we verify both types of relations: either those that are |
||
4609 | // native foreign keys or those defined in the phpMyAdmin |
||
4610 | // configuration storage. If no PMA storage, we won't be able |
||
4611 | // to use the "column to display" notion (for example show |
||
4612 | // the name related to a numeric id). |
||
4613 | $exist_rel = $this->relation->getForeigners( |
||
4614 | $this->__get('db'), |
||
4615 | $this->__get('table'), |
||
4616 | '', |
||
4617 | self::POSITION_BOTH |
||
4618 | ); |
||
4619 | |||
4620 | if (! empty($exist_rel)) { |
||
4621 | foreach ($exist_rel as $master_field => $rel) { |
||
4622 | if ($master_field != 'foreign_keys_data') { |
||
4623 | $display_field = $this->relation->getDisplayField( |
||
4624 | $rel['foreign_db'], |
||
4625 | $rel['foreign_table'] |
||
4626 | ); |
||
4627 | $map[$master_field] = [ |
||
4628 | $rel['foreign_table'], |
||
4629 | $rel['foreign_field'], |
||
4630 | $display_field, |
||
4631 | $rel['foreign_db'], |
||
4632 | ]; |
||
4633 | } else { |
||
4634 | foreach ($rel as $key => $one_key) { |
||
4635 | foreach ($one_key['index_list'] as $index => $one_field) { |
||
4636 | $display_field = $this->relation->getDisplayField( |
||
4637 | isset($one_key['ref_db_name']) |
||
4638 | ? $one_key['ref_db_name'] |
||
4639 | : $GLOBALS['db'], |
||
4640 | $one_key['ref_table_name'] |
||
4641 | ); |
||
4642 | |||
4643 | $map[$one_field] = [ |
||
4644 | $one_key['ref_table_name'], |
||
4645 | $one_key['ref_index_list'][$index], |
||
4646 | $display_field, |
||
4647 | isset($one_key['ref_db_name']) |
||
4648 | ? $one_key['ref_db_name'] |
||
4649 | : $GLOBALS['db'], |
||
4650 | ]; |
||
4651 | } |
||
4652 | } |
||
4653 | } |
||
4654 | } |
||
4655 | } |
||
4656 | } |
||
4657 | |||
4658 | /** |
||
4659 | * Prepare multi field edit/delete links |
||
4660 | * |
||
4661 | * @param integer $dt_result the link id associated to the query which |
||
4662 | * results have to be displayed |
||
4663 | * @param array $analyzed_sql_results analyzed sql results |
||
4664 | * @param string $del_link the display element - 'del_link' |
||
4665 | * |
||
4666 | * @return string html content |
||
4667 | * |
||
4668 | * @access private |
||
4669 | * |
||
4670 | * @see getTable() |
||
4671 | */ |
||
4672 | private function _getMultiRowOperationLinks( |
||
4673 | &$dt_result, |
||
4674 | array $analyzed_sql_results, |
||
4675 | $del_link |
||
4676 | ) { |
||
4677 | $links_html = '<div class="print_ignore" >'; |
||
4678 | $url_query = $this->__get('url_query'); |
||
4679 | $delete_text = $del_link == self::DELETE_ROW ? __('Delete') : __('Kill'); |
||
4680 | |||
4681 | $links_html .= $this->template->render('select_all', [ |
||
4682 | 'pma_theme_image' => $this->__get('pma_theme_image'), |
||
4683 | 'text_dir' => $this->__get('text_dir'), |
||
4684 | 'form_name' => 'resultsForm_' . $this->__get('unique_id'), |
||
4685 | ]); |
||
4686 | |||
4687 | $links_html .= Util::getButtonOrImage( |
||
4688 | 'submit_mult', |
||
4689 | 'mult_submit', |
||
4690 | __('Edit'), |
||
4691 | 'b_edit', |
||
4692 | 'edit' |
||
4693 | ); |
||
4694 | |||
4695 | $links_html .= Util::getButtonOrImage( |
||
4696 | 'submit_mult', |
||
4697 | 'mult_submit', |
||
4698 | __('Copy'), |
||
4699 | 'b_insrow', |
||
4700 | 'copy' |
||
4701 | ); |
||
4702 | |||
4703 | $links_html .= Util::getButtonOrImage( |
||
4704 | 'submit_mult', |
||
4705 | 'mult_submit', |
||
4706 | $delete_text, |
||
4707 | 'b_drop', |
||
4708 | 'delete' |
||
4709 | ); |
||
4710 | |||
4711 | if ($analyzed_sql_results['querytype'] == 'SELECT') { |
||
4712 | $links_html .= Util::getButtonOrImage( |
||
4713 | 'submit_mult', |
||
4714 | 'mult_submit', |
||
4715 | __('Export'), |
||
4716 | 'b_tblexport', |
||
4717 | 'export' |
||
4718 | ); |
||
4719 | } |
||
4720 | |||
4721 | $links_html .= "</div>\n"; |
||
4722 | |||
4723 | $links_html .= '<input type="hidden" name="sql_query"' |
||
4724 | . ' value="' . htmlspecialchars($this->__get('sql_query')) . '">' |
||
4725 | . "\n"; |
||
4726 | |||
4727 | if (! empty($url_query)) { |
||
4728 | $links_html .= '<input type="hidden" name="url_query"' |
||
4729 | . ' value="' . $url_query . '">' . "\n"; |
||
4730 | } |
||
4731 | |||
4732 | // fetch last row of the result set |
||
4733 | $GLOBALS['dbi']->dataSeek($dt_result, $this->__get('num_rows') - 1); |
||
4734 | $row = $GLOBALS['dbi']->fetchRow($dt_result); |
||
4735 | |||
4736 | // @see DbiMysqi::fetchRow & DatabaseInterface::fetchRow |
||
4737 | if (! is_array($row)) { |
||
4738 | $row = []; |
||
4739 | } |
||
4740 | |||
4741 | // $clause_is_unique is needed by getTable() to generate the proper param |
||
4742 | // in the multi-edit and multi-delete form |
||
4743 | list($where_clause, $clause_is_unique, $condition_array) |
||
4744 | = Util::getUniqueCondition( |
||
4745 | $dt_result, // handle |
||
4746 | $this->__get('fields_cnt'), // fields_cnt |
||
4747 | $this->__get('fields_meta'), // fields_meta |
||
4748 | $row, // row |
||
4749 | false, // force_unique |
||
4750 | false, // restrict_to_table |
||
4751 | $analyzed_sql_results // analyzed_sql_results |
||
4752 | ); |
||
4753 | unset($where_clause, $condition_array); |
||
4754 | |||
4755 | // reset to first row for the loop in _getTableBody() |
||
4756 | $GLOBALS['dbi']->dataSeek($dt_result, 0); |
||
4757 | |||
4758 | $links_html .= '<input type="hidden" name="clause_is_unique"' |
||
4759 | . ' value="' . $clause_is_unique . '">' . "\n"; |
||
4760 | |||
4761 | $links_html .= '</form>' . "\n"; |
||
4762 | |||
4763 | return $links_html; |
||
4764 | } |
||
4765 | |||
4766 | /** |
||
4767 | * Generates HTML to display the Create view in span tag |
||
4768 | * |
||
4769 | * @param array $analyzed_sql_results analyzed sql results |
||
4770 | * @param string $url_query String with URL Parameters |
||
4771 | * |
||
4772 | * @return string |
||
4773 | * |
||
4774 | * @access private |
||
4775 | * |
||
4776 | * @see _getResultsOperations() |
||
4777 | */ |
||
4778 | private function _getLinkForCreateView(array $analyzed_sql_results, $url_query) |
||
4779 | { |
||
4780 | $results_operations_html = ''; |
||
4781 | if (empty($analyzed_sql_results['procedure'])) { |
||
4782 | $results_operations_html .= '<span>' |
||
4783 | . Util::linkOrButton( |
||
4784 | 'view_create.php' . $url_query, |
||
4785 | Util::getIcon( |
||
4786 | 'b_view_add', |
||
4787 | __('Create view'), |
||
4788 | true |
||
4789 | ), |
||
4790 | ['class' => 'create_view ajax'] |
||
4791 | ) |
||
4792 | . '</span>' . "\n"; |
||
4793 | } |
||
4794 | return $results_operations_html; |
||
4795 | } |
||
4796 | |||
4797 | /** |
||
4798 | * Calls the _getResultsOperations with $only_view as true |
||
4799 | * |
||
4800 | * @param array $analyzed_sql_results analyzed sql results |
||
4801 | * |
||
4802 | * @return string |
||
4803 | * |
||
4804 | * @access public |
||
4805 | * |
||
4806 | */ |
||
4807 | public function getCreateViewQueryResultOp(array $analyzed_sql_results) |
||
4808 | { |
||
4809 | $results_operations_html = ''; |
||
4810 | //calling to _getResultOperations with a fake $displayParts |
||
4811 | //and setting only_view parameter to be true to generate just view |
||
4812 | $results_operations_html .= $this->_getResultsOperations( |
||
4813 | [], |
||
4814 | $analyzed_sql_results, |
||
4815 | true |
||
4816 | ); |
||
4817 | return $results_operations_html; |
||
4818 | } |
||
4819 | |||
4820 | /** |
||
4821 | * Get copy to clipboard links for results operations |
||
4822 | * |
||
4823 | * @return string |
||
4824 | * |
||
4825 | * @access private |
||
4826 | */ |
||
4827 | private function _getCopytoclipboardLinks() |
||
4828 | { |
||
4829 | return Util::linkOrButton( |
||
4830 | '#', |
||
4831 | Util::getIcon( |
||
4832 | 'b_insrow', |
||
4833 | __('Copy to clipboard'), |
||
4834 | true |
||
4835 | ), |
||
4836 | ['id' => 'copyToClipBoard'] |
||
4837 | ); |
||
4838 | } |
||
4839 | |||
4840 | /** |
||
4841 | * Get printview links for results operations |
||
4842 | * |
||
4843 | * @return string |
||
4844 | * |
||
4845 | * @access private |
||
4846 | */ |
||
4847 | private function _getPrintviewLinks() |
||
4848 | { |
||
4849 | return Util::linkOrButton( |
||
4850 | '#', |
||
4851 | Util::getIcon( |
||
4852 | 'b_print', |
||
4853 | __('Print'), |
||
4854 | true |
||
4855 | ), |
||
4856 | ['id' => 'printView'], |
||
4857 | 'print_view' |
||
4858 | ); |
||
4859 | } |
||
4860 | |||
4861 | /** |
||
4862 | * Get operations that are available on results. |
||
4863 | * |
||
4864 | * @param array $displayParts the parts to display |
||
4865 | * @param array $analyzed_sql_results analyzed sql results |
||
4866 | * @param boolean $only_view Whether to show only view |
||
4867 | * |
||
4868 | * @return string html content |
||
4869 | * |
||
4870 | * @access private |
||
4871 | * |
||
4872 | * @see getTable() |
||
4873 | */ |
||
4874 | private function _getResultsOperations( |
||
4875 | array $displayParts, |
||
4876 | array $analyzed_sql_results, |
||
4877 | $only_view = false |
||
4878 | ) { |
||
4879 | global $printview; |
||
4880 | |||
4881 | $results_operations_html = ''; |
||
4882 | $fields_meta = $this->__get('fields_meta'); // To safe use in foreach |
||
4883 | $header_shown = false; |
||
4884 | $header = '<fieldset class="print_ignore" ><legend>' |
||
4885 | . __('Query results operations') . '</legend>'; |
||
4886 | |||
4887 | $_url_params = [ |
||
4888 | 'db' => $this->__get('db'), |
||
4889 | 'table' => $this->__get('table'), |
||
4890 | 'printview' => '1', |
||
4891 | 'sql_query' => $this->__get('sql_query'), |
||
4892 | ]; |
||
4893 | $url_query = Url::getCommon($_url_params); |
||
4894 | |||
4895 | if (! $header_shown) { |
||
4896 | $results_operations_html .= $header; |
||
4897 | $header_shown = true; |
||
4898 | } |
||
4899 | // if empty result set was produced we need to |
||
4900 | // show only view and not other options |
||
4901 | if ($only_view) { |
||
4902 | $results_operations_html .= $this->_getLinkForCreateView( |
||
4903 | $analyzed_sql_results, |
||
4904 | $url_query |
||
4905 | ); |
||
4906 | |||
4907 | if ($header_shown) { |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
4908 | $results_operations_html .= '</fieldset><br>'; |
||
4909 | } |
||
4910 | return $results_operations_html; |
||
4911 | } |
||
4912 | |||
4913 | // Displays "printable view" link if required |
||
4914 | if ($displayParts['pview_lnk'] == '1') { |
||
4915 | $results_operations_html .= $this->_getPrintviewLinks(); |
||
4916 | $results_operations_html .= $this->_getCopytoclipboardLinks(); |
||
4917 | } // end displays "printable view" |
||
4918 | |||
4919 | // Export link |
||
4920 | // (the url_query has extra parameters that won't be used to export) |
||
4921 | // (the single_table parameter is used in \PhpMyAdmin\Export->getDisplay() |
||
4922 | // to hide the SQL and the structure export dialogs) |
||
4923 | // If the parser found a PROCEDURE clause |
||
4924 | // (most probably PROCEDURE ANALYSE()) it makes no sense to |
||
4925 | // display the Export link). |
||
4926 | if (($analyzed_sql_results['querytype'] == self::QUERY_TYPE_SELECT) |
||
4927 | && ! isset($printview) |
||
4928 | && empty($analyzed_sql_results['procedure']) |
||
4929 | ) { |
||
4930 | if (count($analyzed_sql_results['select_tables']) === 1) { |
||
4931 | $_url_params['single_table'] = 'true'; |
||
4932 | } |
||
4933 | |||
4934 | if (! $header_shown) { |
||
4935 | $results_operations_html .= $header; |
||
4936 | $header_shown = true; |
||
4937 | } |
||
4938 | |||
4939 | $_url_params['unlim_num_rows'] = $this->__get('unlim_num_rows'); |
||
4940 | |||
4941 | /** |
||
4942 | * At this point we don't know the table name; this can happen |
||
4943 | * for example with a query like |
||
4944 | * SELECT bike_code FROM (SELECT bike_code FROM bikes) tmp |
||
4945 | * As a workaround we set in the table parameter the name of the |
||
4946 | * first table of this database, so that tbl_export.php and |
||
4947 | * the script it calls do not fail |
||
4948 | */ |
||
4949 | if (empty($_url_params['table']) && ! empty($_url_params['db'])) { |
||
4950 | $_url_params['table'] = $GLOBALS['dbi']->fetchValue("SHOW TABLES"); |
||
4951 | /* No result (probably no database selected) */ |
||
4952 | if ($_url_params['table'] === false) { |
||
4953 | unset($_url_params['table']); |
||
4954 | } |
||
4955 | } |
||
4956 | |||
4957 | $results_operations_html .= Util::linkOrButton( |
||
4958 | 'tbl_export.php' . Url::getCommon($_url_params), |
||
4959 | Util::getIcon( |
||
4960 | 'b_tblexport', |
||
4961 | __('Export'), |
||
4962 | true |
||
4963 | ) |
||
4964 | ) |
||
4965 | . "\n"; |
||
4966 | |||
4967 | // prepare chart |
||
4968 | $results_operations_html .= Util::linkOrButton( |
||
4969 | 'tbl_chart.php' . Url::getCommon($_url_params), |
||
4970 | Util::getIcon( |
||
4971 | 'b_chart', |
||
4972 | __('Display chart'), |
||
4973 | true |
||
4974 | ) |
||
4975 | ) |
||
4976 | . "\n"; |
||
4977 | |||
4978 | // prepare GIS chart |
||
4979 | $geometry_found = false; |
||
4980 | // If at least one geometry field is found |
||
4981 | foreach ($fields_meta as $meta) { |
||
4982 | if ($meta->type == self::GEOMETRY_FIELD) { |
||
4983 | $geometry_found = true; |
||
4984 | break; |
||
4985 | } |
||
4986 | } |
||
4987 | |||
4988 | if ($geometry_found) { |
||
4989 | $results_operations_html |
||
4990 | .= Util::linkOrButton( |
||
4991 | 'tbl_gis_visualization.php' |
||
4992 | . Url::getCommon($_url_params), |
||
4993 | Util::getIcon( |
||
4994 | 'b_globe', |
||
4995 | __('Visualize GIS data'), |
||
4996 | true |
||
4997 | ) |
||
4998 | ) |
||
4999 | . "\n"; |
||
5000 | } |
||
5001 | } |
||
5002 | |||
5003 | // CREATE VIEW |
||
5004 | /** |
||
5005 | * |
||
5006 | * @todo detect privileges to create a view |
||
5007 | * (but see 2006-01-19 note in PhpMyAdmin\Display\CreateTable, |
||
5008 | * I think we cannot detect db-specific privileges reliably) |
||
5009 | * Note: we don't display a Create view link if we found a PROCEDURE clause |
||
5010 | */ |
||
5011 | if (! $header_shown) { |
||
5012 | $results_operations_html .= $header; |
||
5013 | $header_shown = true; |
||
5014 | } |
||
5015 | |||
5016 | $results_operations_html .= $this->_getLinkForCreateView( |
||
5017 | $analyzed_sql_results, |
||
5018 | $url_query |
||
5019 | ); |
||
5020 | |||
5021 | if ($header_shown) { |
||
5022 | $results_operations_html .= '</fieldset><br>'; |
||
5023 | } |
||
5024 | |||
5025 | return $results_operations_html; |
||
5026 | } |
||
5027 | |||
5028 | /** |
||
5029 | * Verifies what to do with non-printable contents (binary or BLOB) |
||
5030 | * in Browse mode. |
||
5031 | * |
||
5032 | * @param string $category BLOB|BINARY|GEOMETRY |
||
5033 | * @param string|null $content the binary content |
||
5034 | * @param mixed $transformation_plugin transformation plugin. |
||
5035 | * Can also be the |
||
5036 | * default function: |
||
5037 | * Core::mimeDefaultFunction |
||
5038 | * @param string $transform_options transformation parameters |
||
5039 | * @param string $default_function default transformation function |
||
5040 | * @param stdClass $meta the meta-information about the field |
||
5041 | * @param array $url_params parameters that should go to the |
||
5042 | * download link |
||
5043 | * @param boolean $is_truncated the result is truncated or not |
||
5044 | * |
||
5045 | * @return mixed string or float |
||
5046 | * |
||
5047 | * @access private |
||
5048 | * |
||
5049 | * @see _getDataCellForGeometryColumns(), |
||
5050 | * _getDataCellForNonNumericColumns(), |
||
5051 | * _getSortedColumnMessage() |
||
5052 | */ |
||
5053 | private function _handleNonPrintableContents( |
||
5054 | $category, |
||
5055 | ?string $content, |
||
5056 | $transformation_plugin, |
||
5057 | $transform_options, |
||
5058 | $default_function, |
||
5059 | $meta, |
||
5060 | array $url_params = [], |
||
5061 | &$is_truncated = null |
||
5062 | ) { |
||
5063 | $is_truncated = false; |
||
5064 | $result = '[' . $category; |
||
5065 | |||
5066 | if (isset($content)) { |
||
5067 | $size = strlen($content); |
||
5068 | $display_size = Util::formatByteDown($size, 3, 1); |
||
5069 | $result .= ' - ' . $display_size[0] . ' ' . $display_size[1]; |
||
5070 | } else { |
||
5071 | $result .= ' - NULL'; |
||
5072 | $size = 0; |
||
5073 | } |
||
5074 | |||
5075 | $result .= ']'; |
||
5076 | |||
5077 | // if we want to use a text transformation on a BLOB column |
||
5078 | if (gettype($transformation_plugin) === "object") { |
||
5079 | $posMimeOctetstream = strpos( |
||
5080 | $transformation_plugin->getMIMESubtype(), |
||
5081 | 'Octetstream' |
||
5082 | ); |
||
5083 | $posMimeText = strpos($transformation_plugin->getMIMEtype(), 'Text'); |
||
5084 | if ($posMimeOctetstream |
||
5085 | || $posMimeText !== false |
||
5086 | ) { |
||
5087 | // Applying Transformations on hex string of binary data |
||
5088 | // seems more appropriate |
||
5089 | $result = pack("H*", bin2hex($content)); |
||
5090 | } |
||
5091 | } |
||
5092 | |||
5093 | if ($size <= 0) { |
||
5094 | return $result; |
||
5095 | } |
||
5096 | |||
5097 | if ($default_function != $transformation_plugin) { |
||
5098 | $result = $transformation_plugin->applyTransformation( |
||
5099 | $result, |
||
5100 | $transform_options, |
||
5101 | $meta |
||
5102 | ); |
||
5103 | return $result; |
||
5104 | } |
||
5105 | |||
5106 | $result = $default_function($result, [], $meta); |
||
5107 | if (($_SESSION['tmpval']['display_binary'] |
||
5108 | && $meta->type === self::STRING_FIELD) |
||
5109 | || ($_SESSION['tmpval']['display_blob'] |
||
5110 | && false !== stripos($meta->type, self::BLOB_FIELD)) |
||
5111 | ) { |
||
5112 | // in this case, restart from the original $content |
||
5113 | if (mb_check_encoding($content, 'utf-8') |
||
5114 | && ! preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', $content) |
||
5115 | ) { |
||
5116 | // show as text if it's valid utf-8 |
||
5117 | $result = htmlspecialchars($content); |
||
5118 | } else { |
||
5119 | $result = '0x' . bin2hex($content); |
||
5120 | } |
||
5121 | list( |
||
5122 | $is_truncated, |
||
5123 | $result, |
||
5124 | // skip 3rd param |
||
5125 | ) = $this->_getPartialText($result); |
||
5126 | } |
||
5127 | |||
5128 | /* Create link to download */ |
||
5129 | |||
5130 | // in PHP < 5.5, empty() only checks variables |
||
5131 | $tmpdb = $this->__get('db'); |
||
5132 | if (count($url_params) > 0 |
||
5133 | && (! empty($tmpdb) && ! empty($meta->orgtable)) |
||
5134 | ) { |
||
5135 | $result = '<a href="tbl_get_field.php' |
||
5136 | . Url::getCommon($url_params) |
||
5137 | . '" class="disableAjax">' |
||
5138 | . $result . '</a>'; |
||
5139 | } |
||
5140 | |||
5141 | return $result; |
||
5142 | } |
||
5143 | |||
5144 | /** |
||
5145 | * Retrieves the associated foreign key info for a data cell |
||
5146 | * |
||
5147 | * @param array $map the list of relations |
||
5148 | * @param stdClass $meta the meta-information about the field |
||
5149 | * @param string $where_comparison data for the where clause |
||
5150 | * |
||
5151 | * @return string formatted data |
||
5152 | * |
||
5153 | * @access private |
||
5154 | * |
||
5155 | */ |
||
5156 | private function _getFromForeign(array $map, $meta, $where_comparison) |
||
5157 | { |
||
5158 | $dispsql = 'SELECT ' |
||
5159 | . Util::backquote($map[$meta->name][2]) |
||
5160 | . ' FROM ' |
||
5161 | . Util::backquote($map[$meta->name][3]) |
||
5162 | . '.' |
||
5163 | . Util::backquote($map[$meta->name][0]) |
||
5164 | . ' WHERE ' |
||
5165 | . Util::backquote($map[$meta->name][1]) |
||
5166 | . $where_comparison; |
||
5167 | |||
5168 | $dispresult = $GLOBALS['dbi']->tryQuery( |
||
5169 | $dispsql, |
||
5170 | DatabaseInterface::CONNECT_USER, |
||
5171 | DatabaseInterface::QUERY_STORE |
||
5172 | ); |
||
5173 | |||
5174 | if ($dispresult && $GLOBALS['dbi']->numRows($dispresult) > 0) { |
||
5175 | list($dispval) = $GLOBALS['dbi']->fetchRow($dispresult, 0); |
||
5176 | } else { |
||
5177 | $dispval = __('Link not found!'); |
||
5178 | } |
||
5179 | |||
5180 | $GLOBALS['dbi']->freeResult($dispresult); |
||
5181 | |||
5182 | return $dispval; |
||
5183 | } |
||
5184 | |||
5185 | /** |
||
5186 | * Prepares the displayable content of a data cell in Browse mode, |
||
5187 | * taking into account foreign key description field and transformations |
||
5188 | * |
||
5189 | * @param string $class css classes for the td element |
||
5190 | * @param bool $condition_field whether the column is a part of |
||
5191 | * the where clause |
||
5192 | * @param array $analyzed_sql_results the analyzed query |
||
5193 | * @param stdClass $meta the meta-information about the |
||
5194 | * field |
||
5195 | * @param array $map the list of relations |
||
5196 | * @param string $data data |
||
5197 | * @param TransformationsPlugin $transformation_plugin transformation plugin. |
||
5198 | * Can also be the default function: |
||
5199 | * Core::mimeDefaultFunction |
||
5200 | * @param string $default_function default function |
||
5201 | * @param string $nowrap 'nowrap' if the content should |
||
5202 | * not be wrapped |
||
5203 | * @param string $where_comparison data for the where clause |
||
5204 | * @param array $transform_options options for transformation |
||
5205 | * @param bool $is_field_truncated whether the field is truncated |
||
5206 | * @param string $original_length of a truncated column, or '' |
||
5207 | * |
||
5208 | * @return string formatted data |
||
5209 | * |
||
5210 | * @access private |
||
5211 | * |
||
5212 | * @see _getDataCellForNumericColumns(), _getDataCellForGeometryColumns(), |
||
5213 | * _getDataCellForNonNumericColumns(), |
||
5214 | * |
||
5215 | */ |
||
5216 | private function _getRowData( |
||
5217 | $class, |
||
5218 | $condition_field, |
||
5219 | array $analyzed_sql_results, |
||
5220 | $meta, |
||
5221 | array $map, |
||
5222 | $data, |
||
5223 | $transformation_plugin, |
||
5224 | $default_function, |
||
5225 | $nowrap, |
||
5226 | $where_comparison, |
||
5227 | array $transform_options, |
||
5228 | $is_field_truncated, |
||
5229 | $original_length = '' |
||
5230 | ) { |
||
5231 | $relational_display = $_SESSION['tmpval']['relational_display']; |
||
5232 | $printview = $this->__get('printview'); |
||
5233 | $decimals = isset($meta->decimals) ? $meta->decimals : '-1'; |
||
5234 | $result = '<td data-decimals="' . $decimals . '"' |
||
5235 | . ' data-type="' . $meta->type . '"'; |
||
5236 | |||
5237 | if (! empty($original_length)) { |
||
5238 | // cannot use data-original-length |
||
5239 | $result .= ' data-originallength="' . $original_length . '"'; |
||
5240 | } |
||
5241 | |||
5242 | $result .= ' class="' |
||
5243 | . $this->_addClass( |
||
5244 | $class, |
||
5245 | $condition_field, |
||
5246 | $meta, |
||
5247 | $nowrap, |
||
5248 | $is_field_truncated, |
||
5249 | $transformation_plugin, |
||
5250 | $default_function |
||
5251 | ) |
||
5252 | . '">'; |
||
5253 | |||
5254 | if (! empty($analyzed_sql_results['statement']->expr)) { |
||
5255 | foreach ($analyzed_sql_results['statement']->expr as $expr) { |
||
5256 | if (empty($expr->alias) || empty($expr->column)) { |
||
5257 | continue; |
||
5258 | } |
||
5259 | if (strcasecmp($meta->name, $expr->alias) == 0) { |
||
5260 | $meta->name = $expr->column; |
||
5261 | } |
||
5262 | } |
||
5263 | } |
||
5264 | |||
5265 | if (isset($map[$meta->name])) { |
||
5266 | // Field to display from the foreign table? |
||
5267 | if (isset($map[$meta->name][2]) |
||
5268 | && strlen((string) $map[$meta->name][2]) > 0 |
||
5269 | ) { |
||
5270 | $dispval = $this->_getFromForeign( |
||
5271 | $map, |
||
5272 | $meta, |
||
5273 | $where_comparison |
||
5274 | ); |
||
5275 | } else { |
||
5276 | $dispval = ''; |
||
5277 | } // end if... else... |
||
5278 | |||
5279 | if (isset($printview) && ($printview == '1')) { |
||
5280 | $result .= ($transformation_plugin != $default_function |
||
5281 | ? $transformation_plugin->applyTransformation( |
||
5282 | $data, |
||
5283 | $transform_options, |
||
5284 | $meta |
||
5285 | ) |
||
5286 | : $default_function($data) |
||
5287 | ) |
||
5288 | . ' <code>[->' . $dispval . ']</code>'; |
||
5289 | } else { |
||
5290 | if ($relational_display == self::RELATIONAL_KEY) { |
||
5291 | // user chose "relational key" in the display options, so |
||
5292 | // the title contains the display field |
||
5293 | $title = ! empty($dispval) |
||
5294 | ? htmlspecialchars($dispval) |
||
5295 | : ''; |
||
5296 | } else { |
||
5297 | $title = htmlspecialchars($data); |
||
5298 | } |
||
5299 | |||
5300 | $sqlQuery = 'SELECT * FROM ' |
||
5301 | . Util::backquote($map[$meta->name][3]) . '.' |
||
5302 | . Util::backquote($map[$meta->name][0]) |
||
5303 | . ' WHERE ' |
||
5304 | . Util::backquote($map[$meta->name][1]) |
||
5305 | . $where_comparison; |
||
5306 | |||
5307 | $_url_params = [ |
||
5308 | 'db' => $map[$meta->name][3], |
||
5309 | 'table' => $map[$meta->name][0], |
||
5310 | 'pos' => '0', |
||
5311 | 'sql_signature' => Core::signSqlQuery($sqlQuery), |
||
5312 | 'sql_query' => $sqlQuery, |
||
5313 | ]; |
||
5314 | |||
5315 | if ($transformation_plugin != $default_function) { |
||
5316 | // always apply a transformation on the real data, |
||
5317 | // not on the display field |
||
5318 | $message = $transformation_plugin->applyTransformation( |
||
5319 | $data, |
||
5320 | $transform_options, |
||
5321 | $meta |
||
5322 | ); |
||
5323 | } else { |
||
5324 | if ($relational_display == self::RELATIONAL_DISPLAY_COLUMN |
||
5325 | && ! empty($map[$meta->name][2]) |
||
5326 | ) { |
||
5327 | // user chose "relational display field" in the |
||
5328 | // display options, so show display field in the cell |
||
5329 | $message = $default_function($dispval); |
||
5330 | } else { |
||
5331 | // otherwise display data in the cell |
||
5332 | $message = $default_function($data); |
||
5333 | } |
||
5334 | } |
||
5335 | |||
5336 | $tag_params = ['title' => $title]; |
||
5337 | if (strpos($class, 'grid_edit') !== false) { |
||
5338 | $tag_params['class'] = 'ajax'; |
||
5339 | } |
||
5340 | $result .= Util::linkOrButton( |
||
5341 | 'sql.php' . Url::getCommon($_url_params), |
||
5342 | $message, |
||
5343 | $tag_params |
||
5344 | ); |
||
5345 | } |
||
5346 | } else { |
||
5347 | $result .= ($transformation_plugin != $default_function |
||
5348 | ? $transformation_plugin->applyTransformation( |
||
5349 | $data, |
||
5350 | $transform_options, |
||
5351 | $meta |
||
5352 | ) |
||
5353 | : $default_function($data) |
||
5354 | ); |
||
5355 | } |
||
5356 | |||
5357 | $result .= '</td>' . "\n"; |
||
5358 | |||
5359 | return $result; |
||
5360 | } |
||
5361 | |||
5362 | /** |
||
5363 | * Prepares a checkbox for multi-row submits |
||
5364 | * |
||
5365 | * @param string $del_url delete url |
||
5366 | * @param array $displayParts array with explicit indexes for all |
||
5367 | * the display elements |
||
5368 | * @param string $row_no the row number |
||
5369 | * @param string $where_clause_html url encoded where clause |
||
5370 | * @param array $condition_array array of conditions in the where clause |
||
5371 | * @param string $id_suffix suffix for the id |
||
5372 | * @param string $class css classes for the td element |
||
5373 | * |
||
5374 | * @return string the generated HTML |
||
5375 | * |
||
5376 | * @access private |
||
5377 | * |
||
5378 | * @see _getTableBody(), _getCheckboxAndLinks() |
||
5379 | */ |
||
5380 | private function _getCheckboxForMultiRowSubmissions( |
||
5381 | $del_url, |
||
5382 | array $displayParts, |
||
5383 | $row_no, |
||
5384 | $where_clause_html, |
||
5385 | array $condition_array, |
||
5386 | $id_suffix, |
||
5387 | $class |
||
5388 | ) { |
||
5389 | $ret = ''; |
||
5390 | |||
5391 | if (! empty($del_url) && $displayParts['del_lnk'] != self::KILL_PROCESS) { |
||
5392 | $ret .= '<td '; |
||
5393 | if (! empty($class)) { |
||
5394 | $ret .= 'class="' . $class . '"'; |
||
5395 | } |
||
5396 | |||
5397 | $ret .= ' class="center print_ignore">' |
||
5398 | . '<input type="checkbox" id="id_rows_to_delete' |
||
5399 | . $row_no . $id_suffix |
||
5400 | . '" name="rows_to_delete[' . $row_no . ']"' |
||
5401 | . ' class="multi_checkbox checkall"' |
||
5402 | . ' value="' . $where_clause_html . '">' |
||
5403 | . '<input type="hidden" class="condition_array" value="' |
||
5404 | . htmlspecialchars(json_encode($condition_array)) . '">' |
||
5405 | . ' </td>'; |
||
5406 | } |
||
5407 | |||
5408 | return $ret; |
||
5409 | } |
||
5410 | |||
5411 | /** |
||
5412 | * Prepares an Edit link |
||
5413 | * |
||
5414 | * @param string $edit_url edit url |
||
5415 | * @param string $class css classes for td element |
||
5416 | * @param string $edit_str text for the edit link |
||
5417 | * @param string $where_clause where clause |
||
5418 | * @param string $where_clause_html url encoded where clause |
||
5419 | * |
||
5420 | * @return string the generated HTML |
||
5421 | * |
||
5422 | * @access private |
||
5423 | * |
||
5424 | * @see _getTableBody(), _getCheckboxAndLinks() |
||
5425 | */ |
||
5426 | private function _getEditLink( |
||
5427 | $edit_url, |
||
5428 | $class, |
||
5429 | $edit_str, |
||
5430 | $where_clause, |
||
5431 | $where_clause_html |
||
5432 | ) { |
||
5433 | $ret = ''; |
||
5434 | if (! empty($edit_url)) { |
||
5435 | $ret .= '<td class="' . $class . ' center print_ignore">' |
||
5436 | . '<span class="nowrap">' |
||
5437 | . Util::linkOrButton($edit_url, $edit_str); |
||
5438 | /* |
||
5439 | * Where clause for selecting this row uniquely is provided as |
||
5440 | * a hidden input. Used by jQuery scripts for handling grid editing |
||
5441 | */ |
||
5442 | if (! empty($where_clause)) { |
||
5443 | $ret .= '<input type="hidden" class="where_clause" value ="' |
||
5444 | . $where_clause_html . '">'; |
||
5445 | } |
||
5446 | $ret .= '</span></td>'; |
||
5447 | } |
||
5448 | |||
5449 | return $ret; |
||
5450 | } |
||
5451 | |||
5452 | /** |
||
5453 | * Prepares an Copy link |
||
5454 | * |
||
5455 | * @param string $copy_url copy url |
||
5456 | * @param string $copy_str text for the copy link |
||
5457 | * @param string $where_clause where clause |
||
5458 | * @param string $where_clause_html url encoded where clause |
||
5459 | * @param string $class css classes for the td element |
||
5460 | * |
||
5461 | * @return string the generated HTML |
||
5462 | * |
||
5463 | * @access private |
||
5464 | * |
||
5465 | * @see _getTableBody(), _getCheckboxAndLinks() |
||
5466 | */ |
||
5467 | private function _getCopyLink( |
||
5468 | $copy_url, |
||
5469 | $copy_str, |
||
5470 | $where_clause, |
||
5471 | $where_clause_html, |
||
5472 | $class |
||
5473 | ) { |
||
5474 | $ret = ''; |
||
5475 | if (! empty($copy_url)) { |
||
5476 | $ret .= '<td class="'; |
||
5477 | if (! empty($class)) { |
||
5478 | $ret .= $class . ' '; |
||
5479 | } |
||
5480 | |||
5481 | $ret .= 'center print_ignore"><span class="nowrap">' |
||
5482 | . Util::linkOrButton($copy_url, $copy_str); |
||
5483 | |||
5484 | /* |
||
5485 | * Where clause for selecting this row uniquely is provided as |
||
5486 | * a hidden input. Used by jQuery scripts for handling grid editing |
||
5487 | */ |
||
5488 | if (! empty($where_clause)) { |
||
5489 | $ret .= '<input type="hidden" class="where_clause" value="' |
||
5490 | . $where_clause_html . '">'; |
||
5491 | } |
||
5492 | $ret .= '</span></td>'; |
||
5493 | } |
||
5494 | |||
5495 | return $ret; |
||
5496 | } |
||
5497 | |||
5498 | /** |
||
5499 | * Prepares a Delete link |
||
5500 | * |
||
5501 | * @param string $del_url delete url |
||
5502 | * @param string $del_str text for the delete link |
||
5503 | * @param string $js_conf text for the JS confirmation |
||
5504 | * @param string $class css classes for the td element |
||
5505 | * |
||
5506 | * @return string the generated HTML |
||
5507 | * |
||
5508 | * @access private |
||
5509 | * |
||
5510 | * @see _getTableBody(), _getCheckboxAndLinks() |
||
5511 | */ |
||
5512 | private function _getDeleteLink($del_url, $del_str, $js_conf, $class) |
||
5513 | { |
||
5514 | |||
5515 | $ret = ''; |
||
5516 | if (empty($del_url)) { |
||
5517 | return $ret; |
||
5518 | } |
||
5519 | |||
5520 | $ret .= '<td class="'; |
||
5521 | if (! empty($class)) { |
||
5522 | $ret .= $class . ' '; |
||
5523 | } |
||
5524 | $ajax = Response::getInstance()->isAjax() ? ' ajax' : ''; |
||
5525 | $ret .= 'center print_ignore">' |
||
5526 | . Util::linkOrButton( |
||
5527 | $del_url, |
||
5528 | $del_str, |
||
5529 | ['class' => 'delete_row requireConfirm' . $ajax] |
||
5530 | ) |
||
5531 | . '<div class="hide">' . $js_conf . '</div>' |
||
5532 | . '</td>'; |
||
5533 | |||
5534 | return $ret; |
||
5535 | } |
||
5536 | |||
5537 | /** |
||
5538 | * Prepare checkbox and links at some position (left or right) |
||
5539 | * (only called for horizontal mode) |
||
5540 | * |
||
5541 | * @param string $position the position of the checkbox and links |
||
5542 | * @param string $del_url delete url |
||
5543 | * @param array $displayParts array with explicit indexes for all the |
||
5544 | * display elements |
||
5545 | * @param string $row_no row number |
||
5546 | * @param string $where_clause where clause |
||
5547 | * @param string $where_clause_html url encoded where clause |
||
5548 | * @param array $condition_array array of conditions in the where clause |
||
5549 | * @param string $edit_url edit url |
||
5550 | * @param string $copy_url copy url |
||
5551 | * @param string $class css classes for the td elements |
||
5552 | * @param string $edit_str text for the edit link |
||
5553 | * @param string $copy_str text for the copy link |
||
5554 | * @param string $del_str text for the delete link |
||
5555 | * @param string $js_conf text for the JS confirmation |
||
5556 | * |
||
5557 | * @return string the generated HTML |
||
5558 | * |
||
5559 | * @access private |
||
5560 | * |
||
5561 | * @see _getPlacedLinks() |
||
5562 | */ |
||
5563 | private function _getCheckboxAndLinks( |
||
5564 | $position, |
||
5565 | $del_url, |
||
5566 | array $displayParts, |
||
5567 | $row_no, |
||
5568 | $where_clause, |
||
5569 | $where_clause_html, |
||
5570 | array $condition_array, |
||
5571 | $edit_url, |
||
5572 | $copy_url, |
||
5573 | $class, |
||
5574 | $edit_str, |
||
5575 | $copy_str, |
||
5576 | $del_str, |
||
5577 | $js_conf |
||
5578 | ) { |
||
5579 | $ret = ''; |
||
5580 | |||
5581 | if ($position == self::POSITION_LEFT) { |
||
5582 | $ret .= $this->_getCheckboxForMultiRowSubmissions( |
||
5583 | $del_url, |
||
5584 | $displayParts, |
||
5585 | $row_no, |
||
5586 | $where_clause_html, |
||
5587 | $condition_array, |
||
5588 | '_left', |
||
5589 | '' |
||
5590 | ); |
||
5591 | |||
5592 | $ret .= $this->_getEditLink( |
||
5593 | $edit_url, |
||
5594 | $class, |
||
5595 | $edit_str, |
||
5596 | $where_clause, |
||
5597 | $where_clause_html |
||
5598 | ); |
||
5599 | |||
5600 | $ret .= $this->_getCopyLink( |
||
5601 | $copy_url, |
||
5602 | $copy_str, |
||
5603 | $where_clause, |
||
5604 | $where_clause_html, |
||
5605 | '' |
||
5606 | ); |
||
5607 | |||
5608 | $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, ''); |
||
5609 | } elseif ($position == self::POSITION_RIGHT) { |
||
5610 | $ret .= $this->_getDeleteLink($del_url, $del_str, $js_conf, ''); |
||
5611 | |||
5612 | $ret .= $this->_getCopyLink( |
||
5613 | $copy_url, |
||
5614 | $copy_str, |
||
5615 | $where_clause, |
||
5616 | $where_clause_html, |
||
5617 | '' |
||
5618 | ); |
||
5619 | |||
5620 | $ret .= $this->_getEditLink( |
||
5621 | $edit_url, |
||
5622 | $class, |
||
5623 | $edit_str, |
||
5624 | $where_clause, |
||
5625 | $where_clause_html |
||
5626 | ); |
||
5627 | |||
5628 | $ret .= $this->_getCheckboxForMultiRowSubmissions( |
||
5629 | $del_url, |
||
5630 | $displayParts, |
||
5631 | $row_no, |
||
5632 | $where_clause_html, |
||
5633 | $condition_array, |
||
5634 | '_right', |
||
5635 | '' |
||
5636 | ); |
||
5637 | } else { // $position == self::POSITION_NONE |
||
5638 | $ret .= $this->_getCheckboxForMultiRowSubmissions( |
||
5639 | $del_url, |
||
5640 | $displayParts, |
||
5641 | $row_no, |
||
5642 | $where_clause_html, |
||
5643 | $condition_array, |
||
5644 | '_left', |
||
5645 | '' |
||
5646 | ); |
||
5647 | } |
||
5648 | |||
5649 | return $ret; |
||
5650 | } |
||
5651 | |||
5652 | /** |
||
5653 | * Truncates given string based on LimitChars configuration |
||
5654 | * and Session pftext variable |
||
5655 | * (string is truncated only if necessary) |
||
5656 | * |
||
5657 | * @param string $str string to be truncated |
||
5658 | * |
||
5659 | * @return mixed |
||
5660 | * |
||
5661 | * @access private |
||
5662 | * |
||
5663 | * @see _handleNonPrintableContents(), _getDataCellForGeometryColumns(), |
||
5664 | * _getDataCellForNonNumericColumns |
||
5665 | */ |
||
5666 | private function _getPartialText($str) |
||
5667 | { |
||
5668 | $original_length = mb_strlen($str); |
||
5669 | if ($original_length > $GLOBALS['cfg']['LimitChars'] |
||
5670 | && $_SESSION['tmpval']['pftext'] === self::DISPLAY_PARTIAL_TEXT |
||
5671 | ) { |
||
5672 | $str = mb_substr( |
||
5673 | $str, |
||
5674 | 0, |
||
5675 | $GLOBALS['cfg']['LimitChars'] |
||
5676 | ) . '...'; |
||
5677 | $truncated = true; |
||
5678 | } else { |
||
5679 | $truncated = false; |
||
5680 | } |
||
5681 | |||
5682 | return [ |
||
5683 | $truncated, |
||
5684 | $str, |
||
5685 | $original_length, |
||
5686 | ]; |
||
5687 | } |
||
5688 | } |
||
5689 |