Total Complexity | 254 |
Total Lines | 1550 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like NavigationTree often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use NavigationTree, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class NavigationTree |
||
31 | { |
||
32 | /** |
||
33 | * @var Node Reference to the root node of the tree |
||
34 | */ |
||
35 | private $tree; |
||
36 | /** |
||
37 | * @var array The actual paths to all expanded nodes in the tree |
||
38 | * This does not include nodes created after the grouping |
||
39 | * of nodes has been performed |
||
40 | */ |
||
41 | private $aPath = []; |
||
42 | /** |
||
43 | * @var array The virtual paths to all expanded nodes in the tree |
||
44 | * This includes nodes created after the grouping of |
||
45 | * nodes has been performed |
||
46 | */ |
||
47 | private $vPath = []; |
||
48 | /** |
||
49 | * @var int Position in the list of databases, |
||
50 | * used for pagination |
||
51 | */ |
||
52 | private $pos; |
||
53 | /** |
||
54 | * @var array The names of the type of items that are being paginated on |
||
55 | * the second level of the navigation tree. These may be |
||
56 | * tables, views, functions, procedures or events. |
||
57 | */ |
||
58 | private $pos2Name = []; |
||
59 | /** |
||
60 | * @var array The positions of nodes in the lists of tables, views, |
||
61 | * routines or events used for pagination |
||
62 | */ |
||
63 | private $pos2Value = []; |
||
64 | /** |
||
65 | * @var array The names of the type of items that are being paginated |
||
66 | * on the second level of the navigation tree. |
||
67 | * These may be columns or indexes |
||
68 | */ |
||
69 | private $pos3Name = []; |
||
70 | /** |
||
71 | * @var array The positions of nodes in the lists of columns or indexes |
||
72 | * used for pagination |
||
73 | */ |
||
74 | private $pos3Value = []; |
||
75 | /** |
||
76 | * @var string The search clause to use in SQL queries for |
||
77 | * fetching databases |
||
78 | * Used by the asynchronous fast filter |
||
79 | */ |
||
80 | private $searchClause = ''; |
||
81 | /** |
||
82 | * @var string The search clause to use in SQL queries for |
||
83 | * fetching nodes |
||
84 | * Used by the asynchronous fast filter |
||
85 | */ |
||
86 | private $searchClause2 = ''; |
||
87 | /** |
||
88 | * @var bool Whether a warning was raised for large item groups |
||
89 | * which can affect performance. |
||
90 | */ |
||
91 | private $largeGroupWarning = false; |
||
92 | |||
93 | /** |
||
94 | * @var Template |
||
95 | */ |
||
96 | private $template; |
||
97 | |||
98 | /** |
||
99 | * @var DatabaseInterface |
||
100 | */ |
||
101 | private $dbi; |
||
102 | |||
103 | /** |
||
104 | * NavigationTree constructor. |
||
105 | * @param Template $template Template instance |
||
106 | * @param DatabaseInterface $dbi DatabaseInterface instance |
||
107 | */ |
||
108 | public function __construct($template, $dbi) |
||
109 | { |
||
110 | $this->template = $template; |
||
111 | $this->dbi = $dbi; |
||
112 | |||
113 | $checkUserPrivileges = new CheckUserPrivileges($this->dbi); |
||
114 | $checkUserPrivileges->getPrivileges(); |
||
115 | |||
116 | // Save the position at which we are in the database list |
||
117 | if (isset($_POST['pos'])) { |
||
118 | $this->pos = (int) $_POST['pos']; |
||
119 | } elseif (isset($_GET['pos'])) { |
||
120 | $this->pos = (int) $_GET['pos']; |
||
121 | } |
||
122 | if (! isset($this->pos)) { |
||
123 | $this->pos = $this->getNavigationDbPos(); |
||
124 | } |
||
125 | // Get the active node |
||
126 | if (isset($_REQUEST['aPath'])) { |
||
127 | $this->aPath[0] = $this->parsePath($_REQUEST['aPath']); |
||
128 | $this->pos2Name[0] = $_REQUEST['pos2_name']; |
||
129 | $this->pos2Value[0] = $_REQUEST['pos2_value']; |
||
130 | if (isset($_REQUEST['pos3_name'])) { |
||
131 | $this->pos3Name[0] = $_REQUEST['pos3_name']; |
||
132 | $this->pos3Value[0] = $_REQUEST['pos3_value']; |
||
133 | } |
||
134 | } else { |
||
135 | if (isset($_POST['n0_aPath'])) { |
||
136 | $count = 0; |
||
137 | while (isset($_POST['n' . $count . '_aPath'])) { |
||
138 | $this->aPath[$count] = $this->parsePath( |
||
139 | $_POST['n' . $count . '_aPath'] |
||
140 | ); |
||
141 | $index = 'n' . $count . '_pos2_'; |
||
142 | $this->pos2Name[$count] = $_POST[$index . 'name']; |
||
143 | $this->pos2Value[$count] = $_POST[$index . 'value']; |
||
144 | $index = 'n' . $count . '_pos3_'; |
||
145 | if (isset($_POST[$index])) { |
||
146 | $this->pos3Name[$count] = $_POST[$index . 'name']; |
||
147 | $this->pos3Value[$count] = $_POST[$index . 'value']; |
||
148 | } |
||
149 | $count++; |
||
150 | } |
||
151 | } |
||
152 | } |
||
153 | if (isset($_REQUEST['vPath'])) { |
||
154 | $this->vPath[0] = $this->parsePath($_REQUEST['vPath']); |
||
155 | } else { |
||
156 | if (isset($_POST['n0_vPath'])) { |
||
157 | $count = 0; |
||
158 | while (isset($_POST['n' . $count . '_vPath'])) { |
||
159 | $this->vPath[$count] = $this->parsePath( |
||
160 | $_POST['n' . $count . '_vPath'] |
||
161 | ); |
||
162 | $count++; |
||
163 | } |
||
164 | } |
||
165 | } |
||
166 | if (isset($_REQUEST['searchClause'])) { |
||
167 | $this->searchClause = $_REQUEST['searchClause']; |
||
168 | } |
||
169 | if (isset($_REQUEST['searchClause2'])) { |
||
170 | $this->searchClause2 = $_REQUEST['searchClause2']; |
||
171 | } |
||
172 | // Initialise the tree by creating a root node |
||
173 | $node = NodeFactory::getInstance('NodeDatabaseContainer', 'root'); |
||
174 | $this->tree = $node; |
||
175 | if ($GLOBALS['cfg']['NavigationTreeEnableGrouping'] |
||
176 | && $GLOBALS['cfg']['ShowDatabasesNavigationAsTree'] |
||
177 | ) { |
||
178 | $this->tree->separator = $GLOBALS['cfg']['NavigationTreeDbSeparator']; |
||
179 | $this->tree->separatorDepth = 10000; |
||
180 | } |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * Returns the database position for the page selector |
||
185 | * |
||
186 | * @return int |
||
187 | */ |
||
188 | private function getNavigationDbPos() |
||
189 | { |
||
190 | $retval = 0; |
||
191 | |||
192 | if (strlen($GLOBALS['db']) == 0) { |
||
193 | return $retval; |
||
194 | } |
||
195 | |||
196 | /* |
||
197 | * @todo describe a scenario where this code is executed |
||
198 | */ |
||
199 | if (! $GLOBALS['cfg']['Server']['DisableIS']) { |
||
200 | $dbSeparator = $this->dbi->escapeString( |
||
201 | $GLOBALS['cfg']['NavigationTreeDbSeparator'] |
||
202 | ); |
||
203 | $query = "SELECT (COUNT(DB_first_level) DIV %d) * %d "; |
||
204 | $query .= "from ( "; |
||
205 | $query .= " SELECT distinct SUBSTRING_INDEX(SCHEMA_NAME, "; |
||
206 | $query .= " '%s', 1) "; |
||
207 | $query .= " DB_first_level "; |
||
208 | $query .= " FROM INFORMATION_SCHEMA.SCHEMATA "; |
||
209 | $query .= " WHERE `SCHEMA_NAME` < '%s' "; |
||
210 | $query .= ") t "; |
||
211 | |||
212 | $retval = $this->dbi->fetchValue( |
||
213 | sprintf( |
||
214 | $query, |
||
215 | (int) $GLOBALS['cfg']['FirstLevelNavigationItems'], |
||
216 | (int) $GLOBALS['cfg']['FirstLevelNavigationItems'], |
||
217 | $dbSeparator, |
||
218 | $this->dbi->escapeString($GLOBALS['db']) |
||
219 | ) |
||
220 | ); |
||
221 | |||
222 | return $retval; |
||
|
|||
223 | } |
||
224 | |||
225 | $prefixMap = []; |
||
226 | if ($GLOBALS['dbs_to_test'] === false) { |
||
227 | $handle = $this->dbi->tryQuery("SHOW DATABASES"); |
||
228 | if ($handle !== false) { |
||
229 | while ($arr = $this->dbi->fetchArray($handle)) { |
||
230 | if (strcasecmp($arr[0], $GLOBALS['db']) >= 0) { |
||
231 | break; |
||
232 | } |
||
233 | |||
234 | $prefix = strstr( |
||
235 | $arr[0], |
||
236 | $GLOBALS['cfg']['NavigationTreeDbSeparator'], |
||
237 | true |
||
238 | ); |
||
239 | if ($prefix === false) { |
||
240 | $prefix = $arr[0]; |
||
241 | } |
||
242 | $prefixMap[$prefix] = 1; |
||
243 | } |
||
244 | } |
||
245 | } else { |
||
246 | $databases = []; |
||
247 | foreach ($GLOBALS['dbs_to_test'] as $db) { |
||
248 | $query = "SHOW DATABASES LIKE '" . $db . "'"; |
||
249 | $handle = $this->dbi->tryQuery($query); |
||
250 | if ($handle === false) { |
||
251 | continue; |
||
252 | } |
||
253 | while ($arr = $this->dbi->fetchArray($handle)) { |
||
254 | $databases[] = $arr[0]; |
||
255 | } |
||
256 | } |
||
257 | sort($databases); |
||
258 | foreach ($databases as $database) { |
||
259 | if (strcasecmp($database, $GLOBALS['db']) >= 0) { |
||
260 | break; |
||
261 | } |
||
262 | |||
263 | $prefix = strstr( |
||
264 | $database, |
||
265 | $GLOBALS['cfg']['NavigationTreeDbSeparator'], |
||
266 | true |
||
267 | ); |
||
268 | if ($prefix === false) { |
||
269 | $prefix = $database; |
||
270 | } |
||
271 | $prefixMap[$prefix] = 1; |
||
272 | } |
||
273 | } |
||
274 | |||
275 | $navItems = (int) $GLOBALS['cfg']['FirstLevelNavigationItems']; |
||
276 | $retval = floor(count($prefixMap) / $navItems) * $navItems; |
||
277 | |||
278 | return $retval; |
||
279 | } |
||
280 | |||
281 | /** |
||
282 | * Converts an encoded path to a node in string format to an array |
||
283 | * |
||
284 | * @param string $string The path to parse |
||
285 | * |
||
286 | * @return array |
||
287 | */ |
||
288 | private function parsePath($string) |
||
289 | { |
||
290 | $path = explode('.', $string); |
||
291 | foreach ($path as $key => $value) { |
||
292 | $path[$key] = base64_decode($value); |
||
293 | } |
||
294 | |||
295 | return $path; |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Generates the tree structure so that it can be rendered later |
||
300 | * |
||
301 | * @return Node|false The active node or false in case of failure |
||
302 | */ |
||
303 | private function buildPath() |
||
304 | { |
||
305 | $retval = $this->tree; |
||
306 | |||
307 | // Add all databases unconditionally |
||
308 | $data = $this->tree->getData( |
||
309 | 'databases', |
||
310 | $this->pos, |
||
311 | $this->searchClause |
||
312 | ); |
||
313 | $hiddenCounts = $this->tree->getNavigationHidingData(); |
||
314 | foreach ($data as $db) { |
||
315 | $node = NodeFactory::getInstance('NodeDatabase', $db); |
||
316 | if (isset($hiddenCounts[$db])) { |
||
317 | $node->setHiddenCount($hiddenCounts[$db]); |
||
318 | } |
||
319 | $this->tree->addChild($node); |
||
320 | } |
||
321 | |||
322 | // Whether build other parts of the tree depends |
||
323 | // on whether we have any paths in $this->_aPath |
||
324 | foreach ($this->aPath as $key => $path) { |
||
325 | $retval = $this->buildPathPart( |
||
326 | $path, |
||
327 | $this->pos2Name[$key], |
||
328 | $this->pos2Value[$key], |
||
329 | isset($this->pos3Name[$key]) ? $this->pos3Name[$key] : '', |
||
330 | isset($this->pos3Value[$key]) ? $this->pos3Value[$key] : '' |
||
331 | ); |
||
332 | } |
||
333 | |||
334 | return $retval; |
||
335 | } |
||
336 | |||
337 | /** |
||
338 | * Builds a branch of the tree |
||
339 | * |
||
340 | * @param array $path A paths pointing to the branch |
||
341 | * of the tree that needs to be built |
||
342 | * @param string $type2 The type of item being paginated on |
||
343 | * the second level of the tree |
||
344 | * @param int $pos2 The position for the pagination of |
||
345 | * the branch at the second level of the tree |
||
346 | * @param string $type3 The type of item being paginated on |
||
347 | * the third level of the tree |
||
348 | * @param int $pos3 The position for the pagination of |
||
349 | * the branch at the third level of the tree |
||
350 | * |
||
351 | * @return Node|false The active node or false in case of failure |
||
352 | */ |
||
353 | private function buildPathPart(array $path, $type2, $pos2, $type3, $pos3) |
||
354 | { |
||
355 | if (empty($pos2)) { |
||
356 | $pos2 = 0; |
||
357 | } |
||
358 | if (empty($pos3)) { |
||
359 | $pos3 = 0; |
||
360 | } |
||
361 | |||
362 | $retval = true; |
||
363 | if (count($path) <= 1) { |
||
364 | return $retval; |
||
365 | } |
||
366 | |||
367 | array_shift($path); // remove 'root' |
||
368 | /** @var NodeDatabase $db */ |
||
369 | $db = $this->tree->getChild($path[0]); |
||
370 | $retval = $db; |
||
371 | |||
372 | if ($db === false) { |
||
373 | return false; |
||
374 | } |
||
375 | |||
376 | $containers = $this->addDbContainers($db, $type2, $pos2); |
||
377 | |||
378 | array_shift($path); // remove db |
||
379 | |||
380 | if ((count($path) <= 0 || ! array_key_exists($path[0], $containers)) |
||
381 | && count($containers) != 1 |
||
382 | ) { |
||
383 | return $retval; |
||
384 | } |
||
385 | |||
386 | if (count($containers) == 1) { |
||
387 | $container = array_shift($containers); |
||
388 | } else { |
||
389 | $container = $db->getChild($path[0], true); |
||
390 | if ($container === false) { |
||
391 | return false; |
||
392 | } |
||
393 | } |
||
394 | $retval = $container; |
||
395 | |||
396 | if (count($container->children) <= 1) { |
||
397 | $dbData = $db->getData( |
||
398 | $container->realName, |
||
399 | $pos2, |
||
400 | $this->searchClause2 |
||
401 | ); |
||
402 | foreach ($dbData as $item) { |
||
403 | switch ($container->realName) { |
||
404 | case 'events': |
||
405 | $node = NodeFactory::getInstance( |
||
406 | 'NodeEvent', |
||
407 | $item |
||
408 | ); |
||
409 | break; |
||
410 | case 'functions': |
||
411 | $node = NodeFactory::getInstance( |
||
412 | 'NodeFunction', |
||
413 | $item |
||
414 | ); |
||
415 | break; |
||
416 | case 'procedures': |
||
417 | $node = NodeFactory::getInstance( |
||
418 | 'NodeProcedure', |
||
419 | $item |
||
420 | ); |
||
421 | break; |
||
422 | case 'tables': |
||
423 | $node = NodeFactory::getInstance( |
||
424 | 'NodeTable', |
||
425 | $item |
||
426 | ); |
||
427 | break; |
||
428 | case 'views': |
||
429 | $node = NodeFactory::getInstance( |
||
430 | 'NodeView', |
||
431 | $item |
||
432 | ); |
||
433 | break; |
||
434 | default: |
||
435 | break; |
||
436 | } |
||
437 | if (isset($node)) { |
||
438 | if ($type2 == $container->realName) { |
||
439 | $node->pos2 = $pos2; |
||
440 | } |
||
441 | $container->addChild($node); |
||
442 | } |
||
443 | } |
||
444 | } |
||
445 | if (count($path) > 1 && $path[0] != 'tables') { |
||
446 | $retval = false; |
||
447 | |||
448 | return $retval; |
||
449 | } |
||
450 | |||
451 | array_shift($path); // remove container |
||
452 | if (count($path) <= 0) { |
||
453 | return $retval; |
||
454 | } |
||
455 | |||
456 | /** @var NodeTable $table */ |
||
457 | $table = $container->getChild($path[0], true); |
||
458 | if ($table === false) { |
||
459 | if (! $db->getPresence('tables', $path[0])) { |
||
460 | return false; |
||
461 | } |
||
462 | |||
463 | $node = NodeFactory::getInstance( |
||
464 | 'NodeTable', |
||
465 | $path[0] |
||
466 | ); |
||
467 | if ($type2 == $container->realName) { |
||
468 | $node->pos2 = $pos2; |
||
469 | } |
||
470 | $container->addChild($node); |
||
471 | $table = $container->getChild($path[0], true); |
||
472 | } |
||
473 | $retval = $table; |
||
474 | $containers = $this->addTableContainers( |
||
475 | $table, |
||
476 | $pos2, |
||
477 | $type3, |
||
478 | $pos3 |
||
479 | ); |
||
480 | array_shift($path); // remove table |
||
481 | if (count($path) <= 0 |
||
482 | || ! array_key_exists($path[0], $containers) |
||
483 | ) { |
||
484 | return $retval; |
||
485 | } |
||
486 | |||
487 | $container = $table->getChild($path[0], true); |
||
488 | $retval = $container; |
||
489 | $tableData = $table->getData( |
||
490 | $container->realName, |
||
491 | $pos3 |
||
492 | ); |
||
493 | foreach ($tableData as $item) { |
||
494 | switch ($container->realName) { |
||
495 | case 'indexes': |
||
496 | $node = NodeFactory::getInstance( |
||
497 | 'NodeIndex', |
||
498 | $item |
||
499 | ); |
||
500 | break; |
||
501 | case 'columns': |
||
502 | $node = NodeFactory::getInstance( |
||
503 | 'NodeColumn', |
||
504 | $item |
||
505 | ); |
||
506 | break; |
||
507 | case 'triggers': |
||
508 | $node = NodeFactory::getInstance( |
||
509 | 'NodeTrigger', |
||
510 | $item |
||
511 | ); |
||
512 | break; |
||
513 | default: |
||
514 | break; |
||
515 | } |
||
516 | if (isset($node)) { |
||
517 | $node->pos2 = $container->parent->pos2; |
||
518 | if ($type3 == $container->realName) { |
||
519 | $node->pos3 = $pos3; |
||
520 | } |
||
521 | $container->addChild($node); |
||
522 | } |
||
523 | } |
||
524 | |||
525 | return $retval; |
||
526 | } |
||
527 | |||
528 | /** |
||
529 | * Adds containers to a node that is a table |
||
530 | * |
||
531 | * References to existing children are returned |
||
532 | * if this function is called twice on the same node |
||
533 | * |
||
534 | * @param NodeTable $table The table node, new containers will be |
||
535 | * attached to this node |
||
536 | * @param int $pos2 The position for the pagination of |
||
537 | * the branch at the second level of the tree |
||
538 | * @param string $type3 The type of item being paginated on |
||
539 | * the third level of the tree |
||
540 | * @param int $pos3 The position for the pagination of |
||
541 | * the branch at the third level of the tree |
||
542 | * |
||
543 | * @return array An array of new nodes |
||
544 | */ |
||
545 | private function addTableContainers($table, $pos2, $type3, $pos3) |
||
546 | { |
||
547 | $retval = []; |
||
548 | if ($table->hasChildren(true) == 0) { |
||
549 | if ($table->getPresence('columns')) { |
||
550 | $retval['columns'] = NodeFactory::getInstance( |
||
551 | 'NodeColumnContainer' |
||
552 | ); |
||
553 | } |
||
554 | if ($table->getPresence('indexes')) { |
||
555 | $retval['indexes'] = NodeFactory::getInstance( |
||
556 | 'NodeIndexContainer' |
||
557 | ); |
||
558 | } |
||
559 | if ($table->getPresence('triggers')) { |
||
560 | $retval['triggers'] = NodeFactory::getInstance( |
||
561 | 'NodeTriggerContainer' |
||
562 | ); |
||
563 | } |
||
564 | // Add all new Nodes to the tree |
||
565 | foreach ($retval as $node) { |
||
566 | $node->pos2 = $pos2; |
||
567 | if ($type3 == $node->realName) { |
||
568 | $node->pos3 = $pos3; |
||
569 | } |
||
570 | $table->addChild($node); |
||
571 | } |
||
572 | } else { |
||
573 | foreach ($table->children as $node) { |
||
574 | if ($type3 == $node->realName) { |
||
575 | $node->pos3 = $pos3; |
||
576 | } |
||
577 | $retval[$node->realName] = $node; |
||
578 | } |
||
579 | } |
||
580 | |||
581 | return $retval; |
||
582 | } |
||
583 | |||
584 | /** |
||
585 | * Adds containers to a node that is a database |
||
586 | * |
||
587 | * References to existing children are returned |
||
588 | * if this function is called twice on the same node |
||
589 | * |
||
590 | * @param NodeDatabase $db The database node, new containers will be |
||
591 | * attached to this node |
||
592 | * @param string $type The type of item being paginated on |
||
593 | * the second level of the tree |
||
594 | * @param int $pos2 The position for the pagination of |
||
595 | * the branch at the second level of the tree |
||
596 | * |
||
597 | * @return array An array of new nodes |
||
598 | */ |
||
599 | private function addDbContainers($db, $type, $pos2) |
||
600 | { |
||
601 | // Get items to hide |
||
602 | $hidden = $db->getHiddenItems('group'); |
||
603 | if (! $GLOBALS['cfg']['NavigationTreeShowTables'] |
||
604 | && ! in_array('tables', $hidden) |
||
605 | ) { |
||
606 | $hidden[] = 'tables'; |
||
607 | } |
||
608 | if (! $GLOBALS['cfg']['NavigationTreeShowViews'] |
||
609 | && ! in_array('views', $hidden) |
||
610 | ) { |
||
611 | $hidden[] = 'views'; |
||
612 | } |
||
613 | if (! $GLOBALS['cfg']['NavigationTreeShowFunctions'] |
||
614 | && ! in_array('functions', $hidden) |
||
615 | ) { |
||
616 | $hidden[] = 'functions'; |
||
617 | } |
||
618 | if (! $GLOBALS['cfg']['NavigationTreeShowProcedures'] |
||
619 | && ! in_array('procedures', $hidden) |
||
620 | ) { |
||
621 | $hidden[] = 'procedures'; |
||
622 | } |
||
623 | if (! $GLOBALS['cfg']['NavigationTreeShowEvents'] |
||
624 | && ! in_array('events', $hidden) |
||
625 | ) { |
||
626 | $hidden[] = 'events'; |
||
627 | } |
||
628 | |||
629 | $retval = []; |
||
630 | if ($db->hasChildren(true) == 0) { |
||
631 | if (! in_array('tables', $hidden) && $db->getPresence('tables')) { |
||
632 | $retval['tables'] = NodeFactory::getInstance( |
||
633 | 'NodeTableContainer' |
||
634 | ); |
||
635 | } |
||
636 | if (! in_array('views', $hidden) && $db->getPresence('views')) { |
||
637 | $retval['views'] = NodeFactory::getInstance( |
||
638 | 'NodeViewContainer' |
||
639 | ); |
||
640 | } |
||
641 | if (! in_array('functions', $hidden) && $db->getPresence('functions')) { |
||
642 | $retval['functions'] = NodeFactory::getInstance( |
||
643 | 'NodeFunctionContainer' |
||
644 | ); |
||
645 | } |
||
646 | if (! in_array('procedures', $hidden) && $db->getPresence('procedures')) { |
||
647 | $retval['procedures'] = NodeFactory::getInstance( |
||
648 | 'NodeProcedureContainer' |
||
649 | ); |
||
650 | } |
||
651 | if (! in_array('events', $hidden) && $db->getPresence('events')) { |
||
652 | $retval['events'] = NodeFactory::getInstance( |
||
653 | 'NodeEventContainer' |
||
654 | ); |
||
655 | } |
||
656 | // Add all new Nodes to the tree |
||
657 | foreach ($retval as $node) { |
||
658 | if ($type == $node->realName) { |
||
659 | $node->pos2 = $pos2; |
||
660 | } |
||
661 | $db->addChild($node); |
||
662 | } |
||
663 | } else { |
||
664 | foreach ($db->children as $node) { |
||
665 | if ($type == $node->realName) { |
||
666 | $node->pos2 = $pos2; |
||
667 | } |
||
668 | $retval[$node->realName] = $node; |
||
669 | } |
||
670 | } |
||
671 | |||
672 | return $retval; |
||
673 | } |
||
674 | |||
675 | /** |
||
676 | * Recursively groups tree nodes given a separator |
||
677 | * |
||
678 | * @param mixed $node The node to group or null |
||
679 | * to group the whole tree. If |
||
680 | * passed as an argument, $node |
||
681 | * must be of type CONTAINER |
||
682 | * |
||
683 | * @return void |
||
684 | */ |
||
685 | public function groupTree($node = null) |
||
693 | } |
||
694 | } |
||
695 | |||
696 | /** |
||
697 | * Recursively groups tree nodes given a separator |
||
698 | * |
||
699 | * @param Node $node The node to group |
||
700 | * |
||
701 | * @return void |
||
702 | */ |
||
703 | public function groupNode($node) |
||
704 | { |
||
705 | if ($node->type != Node::CONTAINER |
||
706 | || ! $GLOBALS['cfg']['NavigationTreeEnableExpansion'] |
||
707 | ) { |
||
708 | return; |
||
709 | } |
||
710 | |||
711 | $separators = []; |
||
712 | if (is_array($node->separator)) { |
||
713 | $separators = $node->separator; |
||
714 | } else { |
||
715 | if (strlen($node->separator)) { |
||
716 | $separators[] = $node->separator; |
||
717 | } |
||
718 | } |
||
719 | $prefixes = []; |
||
720 | if ($node->separatorDepth > 0) { |
||
721 | foreach ($node->children as $child) { |
||
722 | $prefixPos = false; |
||
723 | foreach ($separators as $separator) { |
||
724 | $sepPos = mb_strpos((string) $child->name, $separator); |
||
725 | if ($sepPos != false |
||
726 | && $sepPos != mb_strlen($child->name) |
||
727 | && $sepPos != 0 |
||
728 | && ($prefixPos == false || $sepPos < $prefixPos) |
||
729 | ) { |
||
730 | $prefixPos = $sepPos; |
||
731 | } |
||
732 | } |
||
733 | if ($prefixPos !== false) { |
||
734 | $prefix = mb_substr($child->name, 0, $prefixPos); |
||
735 | if (! isset($prefixes[$prefix])) { |
||
736 | $prefixes[$prefix] = 1; |
||
737 | } else { |
||
738 | $prefixes[$prefix]++; |
||
739 | } |
||
740 | } |
||
741 | //Bug #4375: Check if prefix is the name of a DB, to create a group. |
||
742 | foreach ($node->children as $otherChild) { |
||
743 | if (array_key_exists($otherChild->name, $prefixes)) { |
||
744 | $prefixes[$otherChild->name]++; |
||
745 | } |
||
746 | } |
||
747 | } |
||
748 | //Check if prefix is the name of a DB, to create a group. |
||
749 | foreach ($node->children as $child) { |
||
750 | if (array_key_exists($child->name, $prefixes)) { |
||
751 | $prefixes[$child->name]++; |
||
752 | } |
||
753 | } |
||
754 | } |
||
755 | // It is not a group if it has only one item |
||
756 | foreach ($prefixes as $key => $value) { |
||
757 | if ($value == 1) { |
||
758 | unset($prefixes[$key]); |
||
759 | } |
||
760 | } |
||
761 | // rfe #1634 Don't group if there's only one group and no other items |
||
762 | if (count($prefixes) == 1) { |
||
763 | $keys = array_keys($prefixes); |
||
764 | $key = $keys[0]; |
||
765 | if ($prefixes[$key] == count($node->children) - 1) { |
||
766 | unset($prefixes[$key]); |
||
767 | } |
||
768 | } |
||
769 | if (count($prefixes)) { |
||
770 | /** @var Node[] $groups */ |
||
771 | $groups = []; |
||
772 | foreach ($prefixes as $key => $value) { |
||
773 | // warn about large groups |
||
774 | if ($value > 500 && ! $this->largeGroupWarning) { |
||
775 | trigger_error( |
||
776 | __( |
||
777 | 'There are large item groups in navigation panel which ' |
||
778 | . 'may affect the performance. Consider disabling item ' |
||
779 | . 'grouping in the navigation panel.' |
||
780 | ), |
||
781 | E_USER_WARNING |
||
782 | ); |
||
783 | $this->largeGroupWarning = true; |
||
784 | } |
||
785 | |||
786 | $groups[$key] = new Node( |
||
787 | htmlspecialchars($key), |
||
788 | Node::CONTAINER, |
||
789 | true |
||
790 | ); |
||
791 | $groups[$key]->separator = $node->separator; |
||
792 | $groups[$key]->separatorDepth = $node->separatorDepth - 1; |
||
793 | $groups[$key]->icon = Util::getImage( |
||
794 | 'b_group', |
||
795 | __('Groups') |
||
796 | ); |
||
797 | $groups[$key]->pos2 = $node->pos2; |
||
798 | $groups[$key]->pos3 = $node->pos3; |
||
799 | if ($node instanceof NodeTableContainer |
||
800 | || $node instanceof NodeViewContainer |
||
801 | ) { |
||
802 | $tblGroup = '&tbl_group=' . urlencode($key); |
||
803 | $groups[$key]->links = [ |
||
804 | 'text' => $node->links['text'] . $tblGroup, |
||
805 | 'icon' => $node->links['icon'] . $tblGroup, |
||
806 | ]; |
||
807 | } |
||
808 | $node->addChild($groups[$key]); |
||
809 | foreach ($separators as $separator) { |
||
810 | $separatorLength = strlen($separator); |
||
811 | // FIXME: this could be more efficient |
||
812 | foreach ($node->children as $child) { |
||
813 | $keySeparatorLength = mb_strlen((string) $key) + $separatorLength; |
||
814 | $nameSubstring = mb_substr( |
||
815 | (string) $child->name, |
||
816 | 0, |
||
817 | $keySeparatorLength |
||
818 | ); |
||
819 | if (($nameSubstring != $key . $separator |
||
820 | && $child->name != $key) |
||
821 | || $child->type != Node::OBJECT |
||
822 | ) { |
||
823 | continue; |
||
824 | } |
||
825 | $class = get_class($child); |
||
826 | $className = substr($class, strrpos($class, '\\') + 1); |
||
827 | unset($class); |
||
828 | $newChild = NodeFactory::getInstance( |
||
829 | $className, |
||
830 | mb_substr( |
||
831 | $child->name, |
||
832 | $keySeparatorLength |
||
833 | ) |
||
834 | ); |
||
835 | |||
836 | if ($newChild instanceof NodeDatabase |
||
837 | && $child->getHiddenCount() > 0 |
||
838 | ) { |
||
839 | $newChild->setHiddenCount($child->getHiddenCount()); |
||
840 | } |
||
841 | |||
842 | $newChild->realName = $child->realName; |
||
843 | $newChild->icon = $child->icon; |
||
844 | $newChild->links = $child->links; |
||
845 | $newChild->pos2 = $child->pos2; |
||
846 | $newChild->pos3 = $child->pos3; |
||
847 | $groups[$key]->addChild($newChild); |
||
848 | foreach ($child->children as $elm) { |
||
849 | $newChild->addChild($elm); |
||
850 | } |
||
851 | $node->removeChild($child->name); |
||
852 | } |
||
853 | } |
||
854 | } |
||
855 | foreach ($prefixes as $key => $value) { |
||
856 | $this->groupNode($groups[$key]); |
||
857 | $groups[$key]->classes = "navGroup"; |
||
858 | } |
||
859 | } |
||
860 | } |
||
861 | |||
862 | /** |
||
863 | * Renders a state of the tree, used in light mode when |
||
864 | * either JavaScript and/or Ajax are disabled |
||
865 | * |
||
866 | * @return string HTML code for the navigation tree |
||
867 | */ |
||
868 | public function renderState() |
||
869 | { |
||
870 | $this->buildPath(); |
||
871 | |||
872 | $quickWarp = $this->quickWarp(); |
||
873 | $fastFilter = $this->fastFilterHtml($this->tree); |
||
874 | $controls = ''; |
||
875 | if ($GLOBALS['cfg']['NavigationTreeEnableExpansion']) { |
||
876 | $controls = $this->controls(); |
||
877 | } |
||
878 | $pageSelector = $this->getPageSelector($this->tree); |
||
879 | |||
880 | $this->groupTree(); |
||
881 | $children = $this->tree->children; |
||
882 | usort($children, [ |
||
883 | NavigationTree::class, |
||
884 | 'sortNode', |
||
885 | ]); |
||
886 | $this->setVisibility(); |
||
887 | |||
888 | $nodes = ''; |
||
889 | for ($i = 0, $nbChildren = count($children); $i < $nbChildren; $i++) { |
||
890 | if ($i == 0) { |
||
891 | $nodes .= $this->renderNode($children[0], true, 'first'); |
||
892 | } else { |
||
893 | if ($i + 1 != $nbChildren) { |
||
894 | $nodes .= $this->renderNode($children[$i], true); |
||
895 | } else { |
||
896 | $nodes .= $this->renderNode($children[$i], true, 'last'); |
||
897 | } |
||
898 | } |
||
899 | } |
||
900 | |||
901 | return $this->template->render('navigation/tree/state', [ |
||
902 | 'quick_warp' => $quickWarp, |
||
903 | 'fast_filter' => $fastFilter, |
||
904 | 'controls' => $controls, |
||
905 | 'page_selector' => $pageSelector, |
||
906 | 'nodes' => $nodes, |
||
907 | ]); |
||
908 | } |
||
909 | |||
910 | /** |
||
911 | * Renders a part of the tree, used for Ajax requests in light mode |
||
912 | * |
||
913 | * @return string|false HTML code for the navigation tree |
||
914 | */ |
||
915 | public function renderPath() |
||
916 | { |
||
917 | $node = $this->buildPath(); |
||
918 | if ($node !== false) { |
||
919 | $this->groupTree(); |
||
920 | |||
921 | $listContent = $this->fastFilterHtml($node); |
||
922 | $listContent .= $this->getPageSelector($node); |
||
923 | $children = $node->children; |
||
924 | usort($children, [ |
||
925 | NavigationTree::class, |
||
926 | 'sortNode', |
||
927 | ]); |
||
928 | |||
929 | for ($i = 0, $nbChildren = count($children); $i < $nbChildren; $i++) { |
||
930 | if ($i + 1 != $nbChildren) { |
||
931 | $listContent .= $this->renderNode($children[$i], true); |
||
932 | } else { |
||
933 | $listContent .= $this->renderNode($children[$i], true, 'last'); |
||
934 | } |
||
935 | } |
||
936 | |||
937 | if (! $GLOBALS['cfg']['ShowDatabasesNavigationAsTree']) { |
||
938 | $parents = $node->parents(true); |
||
939 | $parentName = $parents[0]->realName; |
||
940 | } |
||
941 | } |
||
942 | |||
943 | if (! empty($this->searchClause) || ! empty($this->searchClause2)) { |
||
944 | $results = 0; |
||
945 | if (! empty($this->searchClause2)) { |
||
946 | if (is_object($node->realParent())) { |
||
947 | $results = $node->realParent() |
||
948 | ->getPresence( |
||
949 | $node->realName, |
||
950 | $this->searchClause2 |
||
951 | ); |
||
952 | } |
||
953 | } else { |
||
954 | $results = $this->tree->getPresence( |
||
955 | 'databases', |
||
956 | $this->searchClause |
||
957 | ); |
||
958 | } |
||
959 | $results = sprintf( |
||
960 | _ngettext( |
||
961 | '%s result found', |
||
962 | '%s results found', |
||
963 | $results |
||
964 | ), |
||
965 | $results |
||
966 | ); |
||
967 | Response::getInstance() |
||
968 | ->addJSON( |
||
969 | 'results', |
||
970 | $results |
||
971 | ); |
||
972 | } |
||
973 | |||
974 | if ($node !== false) { |
||
975 | return $this->template->render('navigation/tree/path', [ |
||
976 | 'has_search_results' => ! empty($this->searchClause) || ! empty($this->searchClause2), |
||
977 | 'list_content' => $listContent ?? '', |
||
978 | 'is_tree' => $GLOBALS['cfg']['ShowDatabasesNavigationAsTree'], |
||
979 | 'parent_name' => $parentName ?? '', |
||
980 | ]); |
||
981 | } |
||
982 | return false; |
||
983 | } |
||
984 | |||
985 | /** |
||
986 | * Renders the parameters that are required on the client |
||
987 | * side to know which page(s) we will be requesting data from |
||
988 | * |
||
989 | * @param Node $node The node to create the pagination parameters for |
||
990 | * |
||
991 | * @return string |
||
992 | */ |
||
993 | private function getPaginationParamsHtml($node) |
||
994 | { |
||
995 | $retval = ''; |
||
996 | $paths = $node->getPaths(); |
||
997 | if (isset($paths['aPath_clean'][2])) { |
||
998 | $retval .= "<span class='hide pos2_name'>"; |
||
999 | $retval .= $paths['aPath_clean'][2]; |
||
1000 | $retval .= "</span>"; |
||
1001 | $retval .= "<span class='hide pos2_value'>"; |
||
1002 | $retval .= htmlspecialchars((string) $node->pos2); |
||
1003 | $retval .= "</span>"; |
||
1004 | } |
||
1005 | if (isset($paths['aPath_clean'][4])) { |
||
1006 | $retval .= "<span class='hide pos3_name'>"; |
||
1007 | $retval .= $paths['aPath_clean'][4]; |
||
1008 | $retval .= "</span>"; |
||
1009 | $retval .= "<span class='hide pos3_value'>"; |
||
1010 | $retval .= htmlspecialchars((string) $node->pos3); |
||
1011 | $retval .= "</span>"; |
||
1012 | } |
||
1013 | |||
1014 | return $retval; |
||
1015 | } |
||
1016 | |||
1017 | /** |
||
1018 | * Finds whether given tree matches this tree. |
||
1019 | * |
||
1020 | * @param array $tree Tree to check |
||
1021 | * @param array $paths Paths to check |
||
1022 | * |
||
1023 | * @return boolean |
||
1024 | */ |
||
1025 | private function findTreeMatch(array $tree, array $paths) |
||
1026 | { |
||
1027 | $match = false; |
||
1028 | foreach ($tree as $path) { |
||
1029 | $match = true; |
||
1030 | foreach ($paths as $key => $part) { |
||
1031 | if (! isset($path[$key]) || $part != $path[$key]) { |
||
1032 | $match = false; |
||
1033 | break; |
||
1034 | } |
||
1035 | } |
||
1036 | if ($match) { |
||
1037 | break; |
||
1038 | } |
||
1039 | } |
||
1040 | |||
1041 | return $match; |
||
1042 | } |
||
1043 | |||
1044 | /** |
||
1045 | * Renders a single node or a branch of the tree |
||
1046 | * |
||
1047 | * @param Node $node The node to render |
||
1048 | * @param bool $recursive Bool: Whether to render a single node or a branch |
||
1049 | * @param string $class An additional class for the list item |
||
1050 | * |
||
1051 | * @return string HTML code for the tree node or branch |
||
1052 | */ |
||
1053 | private function renderNode($node, $recursive, $class = '') |
||
1054 | { |
||
1055 | $retval = ''; |
||
1056 | $paths = $node->getPaths(); |
||
1057 | if ($node->hasSiblings() |
||
1058 | || $node->realParent() === false |
||
1059 | ) { |
||
1060 | $response = Response::getInstance(); |
||
1061 | if ($node->type == Node::CONTAINER |
||
1062 | && count($node->children) == 0 |
||
1063 | && ! $response->isAjax() |
||
1064 | ) { |
||
1065 | return ''; |
||
1066 | } |
||
1067 | $retval .= '<li class="' . trim($class . ' ' . $node->classes) . '">'; |
||
1068 | $sterile = [ |
||
1069 | 'events', |
||
1070 | 'triggers', |
||
1071 | 'functions', |
||
1072 | 'procedures', |
||
1073 | 'views', |
||
1074 | 'columns', |
||
1075 | 'indexes', |
||
1076 | ]; |
||
1077 | $parentName = ''; |
||
1078 | $parents = $node->parents(false, true); |
||
1079 | if (count($parents)) { |
||
1080 | $parentName = $parents[0]->realName; |
||
1081 | } |
||
1082 | // if node name itself is in sterile, then allow |
||
1083 | if ($node->isGroup |
||
1084 | || (! in_array($parentName, $sterile) && ! $node->isNew) |
||
1085 | || in_array($node->realName, $sterile) |
||
1086 | ) { |
||
1087 | $retval .= "<div class='block'>"; |
||
1088 | $iClass = ''; |
||
1089 | if ($class == 'first') { |
||
1090 | $iClass = " class='first'"; |
||
1091 | } |
||
1092 | $retval .= "<i$iClass></i>"; |
||
1093 | if (strpos($class, 'last') === false) { |
||
1094 | $retval .= "<b></b>"; |
||
1095 | } |
||
1096 | |||
1097 | $match = $this->findTreeMatch( |
||
1098 | $this->vPath, |
||
1099 | $paths['vPath_clean'] |
||
1100 | ); |
||
1101 | |||
1102 | $retval .= '<a class="' . $node->getCssClasses($match) . '"'; |
||
1103 | $retval .= " href='#'>"; |
||
1104 | $retval .= "<span class='hide aPath'>"; |
||
1105 | $retval .= $paths['aPath']; |
||
1106 | $retval .= "</span>"; |
||
1107 | $retval .= "<span class='hide vPath'>"; |
||
1108 | $retval .= $paths['vPath']; |
||
1109 | $retval .= "</span>"; |
||
1110 | $retval .= "<span class='hide pos'>"; |
||
1111 | $retval .= $this->pos; |
||
1112 | $retval .= "</span>"; |
||
1113 | $retval .= $this->getPaginationParamsHtml($node); |
||
1114 | if ($GLOBALS['cfg']['ShowDatabasesNavigationAsTree'] |
||
1115 | || $parentName != 'root' |
||
1116 | ) { |
||
1117 | $retval .= $node->getIcon($match); |
||
1118 | } |
||
1119 | |||
1120 | $retval .= "</a>"; |
||
1121 | $retval .= "</div>"; |
||
1122 | } else { |
||
1123 | $retval .= "<div class='block'>"; |
||
1124 | $iClass = ''; |
||
1125 | if ($class == 'first') { |
||
1126 | $iClass = " class='first'"; |
||
1127 | } |
||
1128 | $retval .= "<i$iClass></i>"; |
||
1129 | $retval .= $this->getPaginationParamsHtml($node); |
||
1130 | $retval .= "</div>"; |
||
1131 | } |
||
1132 | |||
1133 | $linkClass = ''; |
||
1134 | $haveAjax = [ |
||
1135 | 'functions', |
||
1136 | 'procedures', |
||
1137 | 'events', |
||
1138 | 'triggers', |
||
1139 | 'indexes', |
||
1140 | ]; |
||
1141 | $parent = $node->parents(false, true); |
||
1142 | $isNewView = $parent[0]->realName == 'views' && $node->isNew === true; |
||
1143 | if ($parent[0]->type == Node::CONTAINER |
||
1144 | && (in_array($parent[0]->realName, $haveAjax) || $isNewView) |
||
1145 | ) { |
||
1146 | $linkClass = ' ajax'; |
||
1147 | } |
||
1148 | |||
1149 | if ($node->type == Node::CONTAINER) { |
||
1150 | $retval .= "<i>"; |
||
1151 | } |
||
1152 | |||
1153 | $divClass = ''; |
||
1154 | |||
1155 | if (isset($node->links['icon']) && ! empty($node->links['icon'])) { |
||
1156 | $iconLinks = $node->links['icon']; |
||
1157 | $icons = $node->icon; |
||
1158 | if (! is_array($iconLinks)) { |
||
1159 | $iconLinks = [$iconLinks]; |
||
1160 | $icons = [$icons]; |
||
1161 | } |
||
1162 | |||
1163 | if (count($icons) > 1) { |
||
1164 | $divClass = 'double'; |
||
1165 | } |
||
1166 | } |
||
1167 | |||
1168 | $retval .= "<div class='block " . $divClass . "'>"; |
||
1169 | |||
1170 | if (isset($node->links['icon']) && ! empty($node->links['icon'])) { |
||
1171 | $args = []; |
||
1172 | foreach ($node->parents(true) as $parent) { |
||
1173 | $args[] = urlencode($parent->realName); |
||
1174 | } |
||
1175 | |||
1176 | foreach ($icons as $key => $icon) { |
||
1177 | $link = vsprintf($iconLinks[$key], $args); |
||
1178 | if ($linkClass != '') { |
||
1179 | $retval .= "<a class='$linkClass' href='$link'>"; |
||
1180 | $retval .= "{$icon}</a>"; |
||
1181 | } else { |
||
1182 | $retval .= "<a href='$link'>{$icon}</a>"; |
||
1183 | } |
||
1184 | } |
||
1185 | } else { |
||
1186 | $retval .= "<u>{$node->icon}</u>"; |
||
1187 | } |
||
1188 | $retval .= "</div>"; |
||
1189 | |||
1190 | if (isset($node->links['text'])) { |
||
1191 | $args = []; |
||
1192 | foreach ($node->parents(true) as $parent) { |
||
1193 | $args[] = urlencode($parent->realName); |
||
1194 | } |
||
1195 | $link = vsprintf($node->links['text'], $args); |
||
1196 | $title = isset($node->links['title']) ? $node->links['title'] : ''; |
||
1197 | if ($node->type == Node::CONTAINER) { |
||
1198 | $retval .= " <a class='hover_show_full' href='$link'>"; |
||
1199 | $retval .= htmlspecialchars($node->name); |
||
1200 | $retval .= "</a>"; |
||
1201 | } else { |
||
1202 | $retval .= "<a class='hover_show_full$linkClass' href='$link'"; |
||
1203 | $retval .= " title='$title'>"; |
||
1204 | $retval .= htmlspecialchars($node->displayName ?? $node->realName); |
||
1205 | $retval .= "</a>"; |
||
1206 | } |
||
1207 | } else { |
||
1208 | $retval .= " {$node->name}"; |
||
1209 | } |
||
1210 | $retval .= $node->getHtmlForControlButtons(); |
||
1211 | if ($node->type == Node::CONTAINER) { |
||
1212 | $retval .= "</i>"; |
||
1213 | } |
||
1214 | $retval .= '<div class="clearfloat"></div>'; |
||
1215 | $wrap = true; |
||
1216 | } else { |
||
1217 | $node->visible = true; |
||
1218 | $wrap = false; |
||
1219 | $retval .= $this->getPaginationParamsHtml($node); |
||
1220 | } |
||
1221 | |||
1222 | if ($recursive) { |
||
1223 | $hide = ''; |
||
1224 | if (! $node->visible) { |
||
1225 | $hide = " style='display: none;'"; |
||
1226 | } |
||
1227 | $children = $node->children; |
||
1228 | usort( |
||
1229 | $children, |
||
1230 | [ |
||
1231 | NavigationTree::class, |
||
1232 | 'sortNode', |
||
1233 | ] |
||
1234 | ); |
||
1235 | $buffer = ''; |
||
1236 | $extraClass = ''; |
||
1237 | for ($i = 0, $nbChildren = count($children); $i < $nbChildren; $i++) { |
||
1238 | if ($i + 1 == $nbChildren) { |
||
1239 | $extraClass = ' last'; |
||
1240 | } |
||
1241 | $buffer .= $this->renderNode( |
||
1242 | $children[$i], |
||
1243 | true, |
||
1244 | $children[$i]->classes . $extraClass |
||
1245 | ); |
||
1246 | } |
||
1247 | if (! empty($buffer)) { |
||
1248 | if ($wrap) { |
||
1249 | $retval .= "<div$hide class='list_container'><ul>"; |
||
1250 | } |
||
1251 | $retval .= $this->fastFilterHtml($node); |
||
1252 | $retval .= $this->getPageSelector($node); |
||
1253 | $retval .= $buffer; |
||
1254 | if ($wrap) { |
||
1255 | $retval .= "</ul></div>"; |
||
1256 | } |
||
1257 | } |
||
1258 | } |
||
1259 | if ($node->hasSiblings()) { |
||
1260 | $retval .= "</li>"; |
||
1261 | } |
||
1262 | |||
1263 | return $retval; |
||
1264 | } |
||
1265 | |||
1266 | /** |
||
1267 | * Renders a database select box like the pre-4.0 navigation panel |
||
1268 | * |
||
1269 | * @return string HTML code |
||
1270 | */ |
||
1271 | public function renderDbSelect() |
||
1341 | ]); |
||
1342 | } |
||
1343 | |||
1344 | /** |
||
1345 | * Makes some nodes visible based on the which node is active |
||
1346 | * |
||
1347 | * @return void |
||
1348 | */ |
||
1349 | private function setVisibility() |
||
1350 | { |
||
1351 | foreach ($this->vPath as $path) { |
||
1352 | $node = $this->tree; |
||
1353 | foreach ($path as $value) { |
||
1354 | $child = $node->getChild($value); |
||
1355 | if ($child !== false) { |
||
1356 | $child->visible = true; |
||
1357 | $node = $child; |
||
1358 | } |
||
1359 | } |
||
1360 | } |
||
1361 | } |
||
1362 | |||
1363 | /** |
||
1364 | * Generates the HTML code for displaying the fast filter for tables |
||
1365 | * |
||
1366 | * @param Node $node The node for which to generate the fast filter html |
||
1367 | * |
||
1368 | * @return string LI element used for the fast filter |
||
1369 | */ |
||
1370 | private function fastFilterHtml($node) |
||
1371 | { |
||
1372 | $retval = ''; |
||
1373 | $filterDbMin |
||
1374 | = (int) $GLOBALS['cfg']['NavigationTreeDisplayDbFilterMinimum']; |
||
1375 | $filterItemMin |
||
1376 | = (int) $GLOBALS['cfg']['NavigationTreeDisplayItemFilterMinimum']; |
||
1377 | if ($node === $this->tree |
||
1378 | && $this->tree->getPresence() >= $filterDbMin |
||
1379 | ) { |
||
1380 | $urlParams = [ |
||
1381 | 'pos' => 0, |
||
1382 | ]; |
||
1383 | $retval .= '<li class="fast_filter db_fast_filter">'; |
||
1384 | $retval .= '<form class="ajax fast_filter">'; |
||
1385 | $retval .= Url::getHiddenInputs($urlParams); |
||
1386 | $retval .= '<input class="searchClause" type="text"'; |
||
1387 | $retval .= ' name="searchClause" accesskey="q"'; |
||
1388 | $retval .= " placeholder='" |
||
1389 | . __("Type to filter these, Enter to search all"); |
||
1390 | $retval .= "'>"; |
||
1391 | $retval .= '<span title="' . __('Clear fast filter') . '">X</span>'; |
||
1392 | $retval .= "</form>"; |
||
1393 | $retval .= "</li>"; |
||
1394 | |||
1395 | return $retval; |
||
1396 | } |
||
1397 | |||
1398 | if (($node->type == Node::CONTAINER |
||
1399 | && ($node->realName == 'tables' |
||
1400 | || $node->realName == 'views' |
||
1401 | || $node->realName == 'functions' |
||
1402 | || $node->realName == 'procedures' |
||
1403 | || $node->realName == 'events')) |
||
1404 | && method_exists($node->realParent(), 'getPresence') |
||
1405 | && $node->realParent()->getPresence($node->realName) >= $filterItemMin |
||
1406 | ) { |
||
1407 | $paths = $node->getPaths(); |
||
1408 | $urlParams = [ |
||
1409 | 'pos' => $this->pos, |
||
1410 | 'aPath' => $paths['aPath'], |
||
1411 | 'vPath' => $paths['vPath'], |
||
1412 | 'pos2_name' => $node->realName, |
||
1413 | 'pos2_value' => 0, |
||
1414 | ]; |
||
1415 | $retval .= "<li class='fast_filter'>"; |
||
1416 | $retval .= "<form class='ajax fast_filter'>"; |
||
1417 | $retval .= Url::getHiddenFields($urlParams); |
||
1418 | $retval .= "<input class='searchClause' type='text'"; |
||
1419 | $retval .= " name='searchClause2'"; |
||
1420 | $retval .= " placeholder='" |
||
1421 | . __("Type to filter these, Enter to search all") . "'>"; |
||
1422 | $retval .= "<span title='" . __('Clear fast filter') . "'>X</span>"; |
||
1423 | $retval .= "</form>"; |
||
1424 | $retval .= "</li>"; |
||
1425 | } |
||
1426 | |||
1427 | return $retval; |
||
1428 | } |
||
1429 | |||
1430 | /** |
||
1431 | * Creates the code for displaying the controls |
||
1432 | * at the top of the navigation tree |
||
1433 | * |
||
1434 | * @return string HTML code for the controls |
||
1435 | */ |
||
1436 | private function controls() |
||
1437 | { |
||
1438 | // always iconic |
||
1439 | $showIcon = true; |
||
1440 | $showText = false; |
||
1441 | |||
1442 | $retval = '<!-- CONTROLS START -->'; |
||
1443 | $retval .= '<li id="navigation_controls_outer">'; |
||
1444 | $retval .= '<div id="navigation_controls">'; |
||
1445 | $retval .= Util::getNavigationLink( |
||
1446 | '#', |
||
1447 | $showText, |
||
1448 | __('Collapse all'), |
||
1449 | $showIcon, |
||
1450 | 's_collapseall', |
||
1451 | 'pma_navigation_collapse' |
||
1452 | ); |
||
1453 | $syncImage = 's_unlink'; |
||
1454 | $title = __('Link with main panel'); |
||
1455 | if ($GLOBALS['cfg']['NavigationLinkWithMainPanel']) { |
||
1456 | $syncImage = 's_link'; |
||
1457 | $title = __('Unlink from main panel'); |
||
1458 | } |
||
1459 | $retval .= Util::getNavigationLink( |
||
1460 | '#', |
||
1461 | $showText, |
||
1462 | $title, |
||
1463 | $showIcon, |
||
1464 | $syncImage, |
||
1465 | 'pma_navigation_sync' |
||
1466 | ); |
||
1467 | $retval .= '</div>'; |
||
1468 | $retval .= '</li>'; |
||
1469 | $retval .= '<!-- CONTROLS ENDS -->'; |
||
1470 | |||
1471 | return $retval; |
||
1472 | } |
||
1473 | |||
1474 | /** |
||
1475 | * Generates the HTML code for displaying the list pagination |
||
1476 | * |
||
1477 | * @param Node $node The node for whose children the page |
||
1478 | * selector will be created |
||
1479 | * |
||
1480 | * @return string |
||
1481 | */ |
||
1482 | private function getPageSelector($node) |
||
1483 | { |
||
1484 | $retval = ''; |
||
1485 | if ($node === $this->tree) { |
||
1486 | $retval .= Util::getListNavigator( |
||
1487 | $this->tree->getPresence('databases', $this->searchClause), |
||
1488 | $this->pos, |
||
1489 | ['server' => $GLOBALS['server']], |
||
1490 | 'navigation.php', |
||
1491 | 'frame_navigation', |
||
1492 | $GLOBALS['cfg']['FirstLevelNavigationItems'], |
||
1493 | 'pos', |
||
1494 | ['dbselector'] |
||
1495 | ); |
||
1496 | } else { |
||
1497 | if ($node->type == Node::CONTAINER && ! $node->isGroup) { |
||
1498 | $paths = $node->getPaths(); |
||
1499 | |||
1500 | $level = isset($paths['aPath_clean'][4]) ? 3 : 2; |
||
1501 | $urlParams = [ |
||
1502 | 'aPath' => $paths['aPath'], |
||
1503 | 'vPath' => $paths['vPath'], |
||
1504 | 'pos' => $this->pos, |
||
1505 | 'server' => $GLOBALS['server'], |
||
1506 | 'pos2_name' => $paths['aPath_clean'][2], |
||
1507 | ]; |
||
1508 | if ($level == 3) { |
||
1509 | $pos = $node->pos3; |
||
1510 | $urlParams['pos2_value'] = $node->pos2; |
||
1511 | $urlParams['pos3_name'] = $paths['aPath_clean'][4]; |
||
1512 | } else { |
||
1513 | $pos = $node->pos2; |
||
1514 | } |
||
1515 | $num = $node->realParent() |
||
1516 | ->getPresence( |
||
1517 | $node->realName, |
||
1518 | $this->searchClause2 |
||
1519 | ); |
||
1520 | $retval .= Util::getListNavigator( |
||
1521 | $num, |
||
1522 | $pos, |
||
1523 | $urlParams, |
||
1524 | 'navigation.php', |
||
1525 | 'frame_navigation', |
||
1526 | $GLOBALS['cfg']['MaxNavigationItems'], |
||
1527 | 'pos' . $level . '_value' |
||
1528 | ); |
||
1529 | } |
||
1530 | } |
||
1531 | |||
1532 | return $retval; |
||
1533 | } |
||
1534 | |||
1535 | /** |
||
1536 | * Called by usort() for sorting the nodes in a container |
||
1537 | * |
||
1538 | * @param Node $a The first element used in the comparison |
||
1539 | * @param Node $b The second element used in the comparison |
||
1540 | * |
||
1541 | * @return int See strnatcmp() and strcmp() |
||
1542 | */ |
||
1543 | public static function sortNode($a, $b) |
||
1558 | } |
||
1559 | |||
1560 | /** |
||
1561 | * Display quick warp links, contain Recents and Favorites |
||
1562 | * |
||
1563 | * @return string HTML code |
||
1564 | */ |
||
1565 | private function quickWarp() |
||
1566 | { |
||
1567 | $retval = '<div class="pma_quick_warp">'; |
||
1568 | if ($GLOBALS['cfg']['NumRecentTables'] > 0) { |
||
1569 | $retval .= RecentFavoriteTable::getInstance('recent') |
||
1570 | ->getHtml(); |
||
1571 | } |
||
1572 | if ($GLOBALS['cfg']['NumFavoriteTables'] > 0) { |
||
1580 | } |
||
1581 | } |
||
1582 |
If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.