2 /* vim: set expandtab sw=4 ts=4 sts=4: */
4 * Functionality for the navigation tree
6 * @package PhpMyAdmin-Navigation
8 if (! defined('PHPMYADMIN')) {
13 * Displays a collapsible of database objects in the navigation frame
15 * @package PhpMyAdmin-Navigation
17 class PMA_NavigationTree
20 * @var Node Reference to the root node of the tree
25 * @var array The actual paths to all expanded nodes in the tree
26 * This does not include nodes created after the grouping
27 * of nodes has been performed
29 private $_aPath = array();
32 * @var array The virtual paths to all expanded nodes in the tree
33 * This includes nodes created after the grouping of
34 * nodes has been performed
36 private $_vPath = array();
39 * @var int Position in the list of databases,
45 * @var array The names of the type of items that are being paginated on
46 * the second level of the navigation tree. These may be
47 * tables, views, functions, procedures or events.
49 private $_pos2_name = array();
52 * @var array The positions of nodes in the lists of tables, views,
53 * routines or events used for pagination
55 private $_pos2_value = array();
58 * @var array The names of the type of items that are being paginated
59 * on the second level of the navigation tree.
60 * These may be columns or indexes
62 private $_pos3_name = array();
65 * @var array The positions of nodes in the lists of columns or indexes
68 private $_pos3_value = array();
71 * @var string The search clause to use in SQL queries for
73 * Used by the asynchronous fast filter
75 private $_searchClause = '';
78 * @var string The search clause to use in SQL queries for
80 * Used by the asynchronous fast filter
82 private $_searchClause2 = '';
85 * @var bool Whether a warning was raised for large item groups
86 * which can affect performance.
88 private $_largeGroupWarning = false;
91 * Initialises the class
93 public function __construct()
95 // Save the position at which we are in the database list
96 if (isset($_REQUEST['pos'])) {
97 $this->_pos
= (int) $_REQUEST['pos'];
99 if (! isset($this->_pos
)) {
100 $this->_pos
= $this->_getNavigationDbPos();
102 // Get the active node
103 if (isset($_REQUEST['aPath'])) {
104 $this->_aPath
[0] = $this->_parsePath($_REQUEST['aPath']);
105 $this->_pos2_name
[0] = $_REQUEST['pos2_name'];
106 $this->_pos2_value
[0] = $_REQUEST['pos2_value'];
107 if (isset($_REQUEST['pos3_name'])) {
108 $this->_pos3_name
[0] = $_REQUEST['pos3_name'];
109 $this->_pos3_value
[0] = $_REQUEST['pos3_value'];
111 } else if (isset($_REQUEST['n0_aPath'])) {
113 while (isset($_REQUEST['n' . $count . '_aPath'])) {
114 $this->_aPath
[$count] = $this->_parsePath(
115 $_REQUEST['n' . $count . '_aPath']
117 $index = 'n' . $count . '_pos2_';
118 $this->_pos2_name
[$count] = $_REQUEST[$index . 'name'];
119 $this->_pos2_value
[$count] = $_REQUEST[$index . 'value'];
120 $index = 'n' . $count . '_pos3_';
121 if (isset($_REQUEST[$index])) {
122 $this->_pos3_name
[$count] = $_REQUEST[$index . 'name'];
123 $this->_pos3_value
[$count] = $_REQUEST[$index . 'value'];
128 if (isset($_REQUEST['vPath'])) {
129 $this->_vPath
[0] = $this->_parsePath($_REQUEST['vPath']);
130 } else if (isset($_REQUEST['n0_vPath'])) {
132 while (isset($_REQUEST['n' . $count . '_vPath'])) {
133 $this->_vPath
[$count] = $this->_parsePath(
134 $_REQUEST['n' . $count . '_vPath']
139 if (isset($_REQUEST['searchClause'])) {
140 $this->_searchClause
= $_REQUEST['searchClause'];
142 if (isset($_REQUEST['searchClause2'])) {
143 $this->_searchClause2
= $_REQUEST['searchClause2'];
145 // Initialise the tree by creating a root node
146 $node = PMA_NodeFactory
::getInstance('Node_Database_Container', 'root');
147 $this->_tree
= $node;
148 if ($GLOBALS['cfg']['NavigationTreeEnableGrouping']
149 && $GLOBALS['cfg']['ShowDatabasesNavigationAsTree']
151 $this->_tree
->separator
= $GLOBALS['cfg']['NavigationTreeDbSeparator'];
152 $this->_tree
->separator_depth
= 10000;
157 * Returns the database position for the page selector
161 private function _getNavigationDbPos()
165 if (empty($GLOBALS['db'])) {
170 * @todo describe a scenario where this code is executed
172 if (! $GLOBALS['cfg']['Server']['DisableIS']) {
173 $query = "SELECT (COUNT(DB_first_level) DIV %d) * %d ";
175 $query .= " SELECT distinct SUBSTRING_INDEX(SCHEMA_NAME, ";
176 $query .= " '{$GLOBALS['cfg']['NavigationTreeDbSeparator']}', 1) ";
177 $query .= " DB_first_level ";
178 $query .= " FROM INFORMATION_SCHEMA.SCHEMATA ";
179 $query .= " WHERE `SCHEMA_NAME` < '%s' ";
182 $retval = $GLOBALS['dbi']->fetchValue(
185 (int)$GLOBALS['cfg']['FirstLevelNavigationItems'],
186 (int)$GLOBALS['cfg']['FirstLevelNavigationItems'],
187 PMA_Util
::sqlAddSlashes($GLOBALS['db'])
194 $prefixMap = array();
195 if ($GLOBALS['dbs_to_test'] === false) {
196 $handle = $GLOBALS['dbi']->tryQuery("SHOW DATABASES");
197 if ($handle !== false) {
198 while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
199 if (strcasecmp($arr[0], $GLOBALS['db']) >= 0) {
205 $GLOBALS['cfg']['NavigationTreeDbSeparator'],
208 if ($prefix === false) {
211 $prefixMap[$prefix] = 1;
215 $databases = array();
216 foreach ($GLOBALS['dbs_to_test'] as $db) {
217 $query = "SHOW DATABASES LIKE '" . $db . "'";
218 $handle = $GLOBALS['dbi']->tryQuery($query);
219 if ($handle === false) {
222 while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
223 $databases[] = $arr[0];
227 foreach ($databases as $database) {
228 if (strcasecmp($database, $GLOBALS['db']) >= 0) {
234 $GLOBALS['cfg']['NavigationTreeDbSeparator'],
237 if ($prefix === false) {
240 $prefixMap[$prefix] = 1;
244 $navItems = (int) $GLOBALS['cfg']['FirstLevelNavigationItems'];
245 $retval = floor((count($prefixMap) / $navItems)) * $navItems;
250 * Converts an encoded path to a node in string format to an array
252 * @param string $string The path to parse
256 private function _parsePath($string)
258 $path = explode('.', $string);
259 foreach ($path as $key => $value) {
260 $path[$key] = base64_decode($value);
266 * Generates the tree structure so that it can be rendered later
268 * @return Node|false The active node or false in case of failure
270 private function _buildPath()
272 $retval = $this->_tree
;
274 // Add all databases unconditionally
275 $data = $this->_tree
->getData(
280 $hiddenCounts = $this->_tree
->getNavigationHidingData();
281 foreach ($data as $db) {
282 $node = PMA_NodeFactory
::getInstance('Node_Database', $db);
283 if (isset($hiddenCounts[$db])) {
284 $node->setHiddenCount($hiddenCounts[$db]);
286 $this->_tree
->addChild($node);
289 // Whether build other parts of the tree depends
290 // on whether we have any paths in $this->_aPath
291 foreach ($this->_aPath
as $key => $path) {
292 $retval = $this->_buildPathPart(
294 $this->_pos2_name
[$key],
295 $this->_pos2_value
[$key],
296 isset($this->_pos3_name
[$key]) ?
$this->_pos3_name
[$key] : '',
297 isset($this->_pos3_value
[$key]) ?
$this->_pos3_value
[$key] : ''
304 * Builds a branch of the tree
306 * @param array $path A paths pointing to the branch
307 * of the tree that needs to be built
308 * @param string $type2 The type of item being paginated on
309 * the second level of the tree
310 * @param int $pos2 The position for the pagination of
311 * the branch at the second level of the tree
312 * @param string $type3 The type of item being paginated on
313 * the third level of the tree
314 * @param int $pos3 The position for the pagination of
315 * the branch at the third level of the tree
317 * @return Node|false The active node or false in case of failure
319 private function _buildPathPart($path, $type2, $pos2, $type3, $pos3)
329 if (count($path) <= 1) {
333 array_shift($path); // remove 'root'
334 /* @var $db Node_Database */
335 $db = $this->_tree
->getChild($path[0]);
342 $containers = $this->_addDbContainers($db, $type2, $pos2);
344 array_shift($path); // remove db
346 if ((count($path) <= 0
347 ||
!array_key_exists($path[0], $containers))
348 && count($containers) != 1
353 if (count($containers) == 1) {
354 $container = array_shift($containers);
356 $container = $db->getChild($path[0], true);
357 if ($container === false) {
361 $retval = $container;
363 if (count($container->children
) <= 1) {
364 $dbData = $db->getData(
365 $container->real_name
,
367 $this->_searchClause2
369 foreach ($dbData as $item) {
370 switch ($container->real_name
) {
372 $node = PMA_NodeFactory
::getInstance(
378 $node = PMA_NodeFactory
::getInstance(
384 $node = PMA_NodeFactory
::getInstance(
390 $node = PMA_NodeFactory
::getInstance(
396 $node = PMA_NodeFactory
::getInstance(
405 if ($type2 == $container->real_name
) {
408 $container->addChild($node);
412 if (count($path) > 1 && $path[0] != 'tables') {
417 array_shift($path); // remove container
418 if (count($path) <= 0) {
422 /* @var $table Node_Table */
423 $table = $container->getChild($path[0], true);
424 if ($table === false) {
425 if (!$db->getPresence('tables', $path[0])) {
429 $node = PMA_NodeFactory
::getInstance(
433 if ($type2 == $container->real_name
) {
436 $container->addChild($node);
437 $table = $container->getChild($path[0], true);
440 $containers = $this->_addTableContainers(
446 array_shift($path); // remove table
447 if (count($path) <= 0
448 ||
!array_key_exists($path[0], $containers)
453 $container = $table->getChild($path[0], true);
454 $retval = $container;
455 $tableData = $table->getData(
456 $container->real_name
,
459 foreach ($tableData as $item) {
460 switch ($container->real_name
) {
462 $node = PMA_NodeFactory
::getInstance(
468 $node = PMA_NodeFactory
::getInstance(
474 $node = PMA_NodeFactory
::getInstance(
483 $node->pos2
= $container->parent
->pos2
;
484 if ($type3 == $container->real_name
) {
487 $container->addChild($node);
494 * Adds containers to a node that is a table
496 * References to existing children are returned
497 * if this function is called twice on the same node
499 * @param Node_Table $table The table node, new containers will be
500 * attached to this node
501 * @param int $pos2 The position for the pagination of
502 * the branch at the second level of the tree
503 * @param string $type3 The type of item being paginated on
504 * the third level of the tree
505 * @param int $pos3 The position for the pagination of
506 * the branch at the third level of the tree
508 * @return array An array of new nodes
510 private function _addTableContainers($table, $pos2, $type3, $pos3)
513 if ($table->hasChildren(true) == 0) {
514 if ($table->getPresence('columns')) {
515 $retval['columns'] = PMA_NodeFactory
::getInstance(
516 'Node_Column_Container'
519 if ($table->getPresence('indexes')) {
520 $retval['indexes'] = PMA_NodeFactory
::getInstance(
521 'Node_Index_Container'
524 if ($table->getPresence('triggers')) {
525 $retval['triggers'] = PMA_NodeFactory
::getInstance(
526 'Node_Trigger_Container'
529 // Add all new Nodes to the tree
530 foreach ($retval as $node) {
532 if ($type3 == $node->real_name
) {
535 $table->addChild($node);
538 foreach ($table->children
as $node) {
539 if ($type3 == $node->real_name
) {
542 $retval[$node->real_name
] = $node;
549 * Adds containers to a node that is a database
551 * References to existing children are returned
552 * if this function is called twice on the same node
554 * @param Node_Database $db The database node, new containers will be
555 * attached to this node
556 * @param string $type The type of item being paginated on
557 * the second level of the tree
558 * @param int $pos2 The position for the pagination of
559 * the branch at the second level of the tree
561 * @return array An array of new nodes
563 private function _addDbContainers($db, $type, $pos2)
566 $hidden = $db->getHiddenItems('group');
567 if (!$GLOBALS['cfg']['NavigationTreeShowTables']
568 && !in_array('tables', $hidden)
570 $hidden[] = 'tables';
572 if (!$GLOBALS['cfg']['NavigationTreeShowViews']
573 && !in_array('views', $hidden)
577 if (!$GLOBALS['cfg']['NavigationTreeShowFunctions']
578 && !in_array('functions', $hidden)
580 $hidden[] = 'functions';
582 if (!$GLOBALS['cfg']['NavigationTreeShowProcedures']
583 && !in_array('procedures', $hidden)
585 $hidden[] = 'procedures';
587 if (!$GLOBALS['cfg']['NavigationTreeShowEvents']
588 && !in_array('events', $hidden)
590 $hidden[] = 'events';
594 if ($db->hasChildren(true) == 0) {
595 if (!in_array('tables', $hidden) && $db->getPresence('tables')) {
596 $retval['tables'] = PMA_NodeFactory
::getInstance(
597 'Node_Table_Container'
600 if (!in_array('views', $hidden) && $db->getPresence('views')) {
601 $retval['views'] = PMA_NodeFactory
::getInstance(
602 'Node_View_Container'
605 if (!in_array('functions', $hidden) && $db->getPresence('functions')) {
606 $retval['functions'] = PMA_NodeFactory
::getInstance(
607 'Node_Function_Container'
610 if (!in_array('procedures', $hidden) && $db->getPresence('procedures')) {
611 $retval['procedures'] = PMA_NodeFactory
::getInstance(
612 'Node_Procedure_Container'
615 if (!in_array('events', $hidden) && $db->getPresence('events')) {
616 $retval['events'] = PMA_NodeFactory
::getInstance(
617 'Node_Event_Container'
620 // Add all new Nodes to the tree
621 foreach ($retval as $node) {
622 if ($type == $node->real_name
) {
625 $db->addChild($node);
628 foreach ($db->children
as $node) {
629 if ($type == $node->real_name
) {
632 $retval[$node->real_name
] = $node;
639 * Recursively groups tree nodes given a separator
641 * @param mixed $node The node to group or null
642 * to group the whole tree. If
643 * passed as an argument, $node
644 * must be of type CONTAINER
648 public function groupTree($node = null)
650 if (! isset($node)) {
651 $node = $this->_tree
;
653 $this->groupNode($node);
654 foreach ($node->children
as $child) {
655 $this->groupTree($child);
660 * Recursively groups tree nodes given a separator
662 * @param Node $node The node to group
666 public function groupNode($node)
668 if ($node->type
!= Node
::CONTAINER
669 ||
! $GLOBALS['cfg']['NavigationTreeEnableExpansion']
674 $separators = array();
675 if (is_array($node->separator
)) {
676 $separators = $node->separator
;
677 } else if (strlen($node->separator
)) {
678 $separators[] = $node->separator
;
681 if ($node->separator_depth
> 0) {
682 foreach ($node->children
as $child) {
684 foreach ($separators as $separator) {
685 $sep_pos = /*overload*/mb_strpos($child->name
, $separator);
686 if ($sep_pos != false
687 && $sep_pos != /*overload*/mb_strlen($child->name
)
689 && ($prefix_pos == false ||
$sep_pos < $prefix_pos)
691 $prefix_pos = $sep_pos;
694 if ($prefix_pos !== false) {
695 $prefix = /*overload*/mb_substr($child->name
, 0, $prefix_pos);
696 if (! isset($prefixes[$prefix])) {
697 $prefixes[$prefix] = 1;
699 $prefixes[$prefix]++
;
702 //Bug #4375: Check if prefix is the name of a DB, to create a group.
703 foreach ($node->children
as $otherChild) {
704 if (array_key_exists($otherChild->name
, $prefixes)) {
705 $prefixes[$otherChild->name
]++
;
709 //Check if prefix is the name of a DB, to create a group.
710 foreach ($node->children
as $child) {
711 if (array_key_exists($child->name
, $prefixes)) {
712 $prefixes[$child->name
]++
;
716 // It is not a group if it has only one item
717 foreach ($prefixes as $key => $value) {
719 unset($prefixes[$key]);
722 // rfe #1634 Don't group if there's only one group and no other items
723 if (count($prefixes) == 1) {
724 $keys = array_keys($prefixes);
726 if ($prefixes[$key] == count($node->children
) - 1) {
727 unset($prefixes[$key]);
730 if (count($prefixes)) {
731 /** @var Node[] $groups */
733 foreach ($prefixes as $key => $value) {
735 // warn about large groups
736 if ($value > 500 && ! $this->_largeGroupWarning
) {
739 'There are large item groups in navigation panel which '
740 . 'may affect the performance. Consider disabling item '
741 . 'grouping in the navigation panel.'
745 $this->_largeGroupWarning
= true;
748 $groups[$key] = new Node(
753 $groups[$key]->separator
= $node->separator
;
754 $groups[$key]->separator_depth
= $node->separator_depth
- 1;
755 $groups[$key]->icon
= PMA_Util
::getImage(
758 $groups[$key]->pos2
= $node->pos2
;
759 $groups[$key]->pos3
= $node->pos3
;
760 if ($node instanceof Node_Table_Container
761 ||
$node instanceof Node_View_Container
763 $tblGroup = '&tbl_group=' . urlencode($key);
764 $groups[$key]->links
= array(
765 'text' => $node->links
['text'] . $tblGroup,
766 'icon' => $node->links
['icon'] . $tblGroup
769 $node->addChild($groups[$key]);
770 foreach ($separators as $separator) {
771 $separatorLength = strlen($separator);
772 // FIXME: this could be more efficient
773 foreach ($node->children
as $child) {
774 $keySeparatorLength = /*overload*/mb_strlen($key)
776 $name_substring = /*overload*/mb_substr(
781 if (($name_substring != $key . $separator
782 && $child->name
!= $key)
783 ||
$child->type
!= Node
::OBJECT
787 $class = get_class($child);
788 $new_child = PMA_NodeFactory
::getInstance(
790 /*overload*/mb_substr(
796 if ($new_child instanceof Node_Database
797 && $child->getHiddenCount() > 0
799 $new_child->setHiddenCount($child->getHiddenCount());
802 $new_child->real_name
= $child->real_name
;
803 $new_child->icon
= $child->icon
;
804 $new_child->links
= $child->links
;
805 $new_child->pos2
= $child->pos2
;
806 $new_child->pos3
= $child->pos3
;
807 $groups[$key]->addChild($new_child);
808 foreach ($child->children
as $elm) {
809 $new_child->addChild($elm);
811 $node->removeChild($child->name
);
815 foreach ($prefixes as $key => $value) {
816 $this->groupNode($groups[$key]);
817 $groups[$key]->classes
= "navGroup";
823 * Renders a state of the tree, used in light mode when
824 * either JavaScript and/or Ajax are disabled
826 * @return string HTML code for the navigation tree
828 public function renderState()
831 $retval = $this->_quickWarp();
832 $retval .= '<div class="clearfloat"></div>';
834 $retval .= $this->_fastFilterHtml($this->_tree
);
835 if ($GLOBALS['cfg']['NavigationTreeEnableExpansion']
837 $retval .= $this->_controls();
840 $retval .= $this->_getPageSelector($this->_tree
);
842 $retval .= "<div id='pma_navigation_tree_content'><ul>";
843 $children = $this->_tree
->children
;
844 usort($children, array('PMA_NavigationTree', 'sortNode'));
845 $this->_setVisibility();
846 for ($i=0, $nbChildren = count($children); $i < $nbChildren; $i++
) {
848 $retval .= $this->_renderNode($children[0], true, 'first');
849 } else if ($i +
1 != $nbChildren) {
850 $retval .= $this->_renderNode($children[$i], true);
852 $retval .= $this->_renderNode($children[$i], true, 'last');
855 $retval .= "</ul></div>";
860 * Renders a part of the tree, used for Ajax
861 * requests in light mode
863 * @return string HTML code for the navigation tree
865 public function renderPath()
867 $node = $this->_buildPath();
868 if ($node === false) {
872 $retval = "<div class='list_container' style='display: none;'>";
873 if (! empty($this->_searchClause
) ||
! empty($this->_searchClause2
)) {
874 $retval .= "<ul class='search_results'>";
878 $listContent = $this->_fastFilterHtml($node);
879 $listContent .= $this->_getPageSelector($node);
880 $children = $node->children
;
881 usort($children, array('PMA_NavigationTree', 'sortNode'));
882 for ($i=0, $nbChildren = count($children); $i < $nbChildren; $i++
) {
883 if ($i +
1 != $nbChildren) {
884 $listContent .= $this->_renderNode($children[$i], true);
886 $listContent .= $this->_renderNode($children[$i], true, 'last');
889 $retval .= $listContent;
891 if (! $GLOBALS['cfg']['ShowDatabasesNavigationAsTree']) {
892 $retval .= "<span class='hide loaded_db'>";
893 $parents = $node->parents(true);
894 $retval .= urlencode($parents[0]->real_name
);
895 $retval .= "</span>";
896 if (empty($listContent)) {
897 $retval .= "<div style='margin:0.75em'>";
898 $retval .= __('No tables found in database.');
905 if (! empty($this->_searchClause
) ||
! empty($this->_searchClause2
)) {
907 if (! empty($this->_searchClause2
)) {
908 if (is_object($node->realParent())) {
909 $results = $node->realParent()->getPresence(
911 $this->_searchClause2
915 $results = $this->_tree
->getPresence(
928 PMA_Response
::getInstance()->addJSON(
937 * Renders the parameters that are required on the client
938 * side to know which page(s) we will be requesting data from
940 * @param Node $node The node to create the pagination parameters for
944 private function _getPaginationParamsHtml($node)
947 $paths = $node->getPaths();
948 if (isset($paths['aPath_clean'][2])) {
949 $retval .= "<span class='hide pos2_name'>";
950 $retval .= $paths['aPath_clean'][2];
951 $retval .= "</span>";
952 $retval .= "<span class='hide pos2_value'>";
953 $retval .= $node->pos2
;
954 $retval .= "</span>";
956 if (isset($paths['aPath_clean'][4])) {
957 $retval .= "<span class='hide pos3_name'>";
958 $retval .= $paths['aPath_clean'][4];
959 $retval .= "</span>";
960 $retval .= "<span class='hide pos3_value'>";
961 $retval .= $node->pos3
;
962 $retval .= "</span>";
968 * Finds whether given tree matches this tree.
970 * @param array $tree Tree to check
971 * @param array $paths Paths to check
975 private function _findTreeMatch($tree, $paths)
978 foreach ($tree as $path) {
980 foreach ($paths as $key => $part) {
981 if (! isset($path[$key]) ||
$part != $path[$key]) {
994 * Renders a single node or a branch of the tree
996 * @param Node $node The node to render
997 * @param bool $recursive Bool: Whether to render a single node or a branch
998 * @param string $class An additional class for the list item
1000 * @return string HTML code for the tree node or branch
1002 private function _renderNode($node, $recursive, $class = '')
1005 $paths = $node->getPaths();
1006 if ($node->hasSiblings()
1007 ||
$node->realParent() === false
1009 if ($node->type
== Node
::CONTAINER
1010 && count($node->children
) == 0
1011 && $GLOBALS['is_ajax_request'] != true
1015 $retval .= '<li class="' . trim($class . ' ' . $node->classes
) . '">';
1026 $parents = $node->parents(false, true);
1027 if (count($parents)) {
1028 $parentName = $parents[0]->real_name
;
1030 // if node name itself is in sterile, then allow
1032 ||
(! in_array($parentName, $sterile) && ! $node->isNew
)
1033 ||
(in_array($node->real_name
, $sterile))
1035 $retval .= "<div class='block'>";
1037 if ($class == 'first') {
1038 $iClass = " class='first'";
1040 $retval .= "<i$iClass></i>";
1041 if (strpos($class, 'last') === false) {
1042 $retval .= "<b></b>";
1045 $match = $this->_findTreeMatch(
1047 $paths['vPath_clean']
1050 $retval .= '<a class="' . $node->getCssClasses($match) . '"';
1051 $retval .= " href='#'>";
1052 $retval .= "<span class='hide aPath'>";
1053 $retval .= $paths['aPath'];
1054 $retval .= "</span>";
1055 $retval .= "<span class='hide vPath'>";
1056 $retval .= $paths['vPath'];
1057 $retval .= "</span>";
1058 $retval .= "<span class='hide pos'>";
1059 $retval .= $this->_pos
;
1060 $retval .= "</span>";
1061 $retval .= $this->_getPaginationParamsHtml($node);
1062 if ($GLOBALS['cfg']['ShowDatabasesNavigationAsTree']
1063 ||
$parentName != 'root'
1065 $retval .= $node->getIcon($match);
1069 $retval .= "</div>";
1071 $retval .= "<div class='block'>";
1073 if ($class == 'first') {
1074 $iClass = " class='first'";
1076 $retval .= "<i$iClass></i>";
1077 $retval .= $this->_getPaginationParamsHtml($node);
1078 $retval .= "</div>";
1089 $parent = $node->parents(false, true);
1090 $isNewView = $parent[0]->real_name
== 'views' && $node->isNew
=== true;
1091 if ($parent[0]->type
== Node
::CONTAINER
1092 && (in_array($parent[0]->real_name
, $haveAjax) ||
$isNewView)
1094 $linkClass = ' ajax';
1097 if ($node->type
== Node
::CONTAINER
) {
1103 if (isset($node->links
['icon']) && !empty($node->links
['icon'])) {
1104 $iconLinks = $node->links
['icon'];
1105 $icons = $node->icon
;
1106 if (!is_array($iconLinks)) {
1107 $iconLinks = array($iconLinks);
1108 $icons = array($icons);
1111 if (count($icons) > 1) {
1112 $divClass = 'double';
1116 $retval .= "<div class='block " . $divClass . "'>";
1118 if (isset($node->links
['icon']) && !empty($node->links
['icon'])) {
1120 foreach ($node->parents(true) as $parent) {
1121 $args[] = urlencode($parent->real_name
);
1124 foreach ($icons as $key => $icon) {
1125 $link = vsprintf($iconLinks[$key], $args);
1126 if ($linkClass != '') {
1127 $retval .= "<a class='$linkClass' href='$link'>";
1128 $retval .= "{$icon}</a>";
1130 $retval .= "<a href='$link'>{$icon}</a>";
1134 $retval .= "<u>{$node->icon}</u>";
1136 $retval .= "</div>";
1138 if (isset($node->links
['text'])) {
1140 foreach ($node->parents(true) as $parent) {
1141 $args[] = urlencode($parent->real_name
);
1143 $link = vsprintf($node->links
['text'], $args);
1144 $title = empty($node->links
['title']) ?
'' : $node->links
['title'];
1145 if ($node->type
== Node
::CONTAINER
) {
1146 $retval .= " <a class='hover_show_full' href='$link'>";
1147 $retval .= htmlspecialchars($node->name
);
1150 $retval .= "<a class='hover_show_full$linkClass' href='$link'";
1151 $retval .= " title='$title'>";
1152 $retval .= htmlspecialchars($node->real_name
);
1156 $retval .= " {$node->name}";
1158 $retval .= $node->getHtmlForControlButtons();
1159 if ($node->type
== Node
::CONTAINER
) {
1162 $retval .= '<div class="clearfloat"></div>';
1165 $node->visible
= true;
1167 $retval .= $this->_getPaginationParamsHtml($node);
1172 if (!$node->visible
) {
1173 $hide = " style='display: none;'";
1175 $children = $node->children
;
1176 usort($children, array('PMA_NavigationTree', 'sortNode'));
1179 for ($i=0, $nbChildren = count($children); $i < $nbChildren; $i++
) {
1180 if ($i +
1 == $nbChildren) {
1181 $extra_class = ' last';
1183 $buffer .= $this->_renderNode(
1186 $children[$i]->classes
. $extra_class
1189 if (! empty($buffer)) {
1191 $retval .= "<div$hide class='list_container'><ul>";
1193 $retval .= $this->_fastFilterHtml($node);
1194 $retval .= $this->_getPageSelector($node);
1197 $retval .= "</ul></div>";
1201 if ($node->hasSiblings()) {
1208 * Renders a database select box like the pre-4.0 navigation panel
1210 * @return string HTML code
1212 public function renderDbSelect()
1214 $this->_buildPath();
1215 $retval = $this->_quickWarp();
1216 $this->_tree
->is_group
= false;
1217 $retval .= '<div id="pma_navigation_select_database">';
1218 // Provide for pagination in database select
1219 $retval .= PMA_Util
::getListNavigator(
1220 $this->_tree
->getPresence('databases', ''),
1222 array('server' => $GLOBALS['server']),
1225 $GLOBALS['cfg']['FirstLevelNavigationItems'],
1229 $children = $this->_tree
->children
;
1230 array_shift($children);
1231 $url_params = array(
1232 'token' => $_SESSION[' PMA_token '],
1233 'server' => $GLOBALS['server']
1235 $retval .= '<div id="pma_navigation_db_select">';
1236 $retval .= '<form action="index.php">';
1237 $retval .= PMA_getHiddenFields($url_params);
1238 $retval .= '<select name="db" class="hide" id="navi_db_select">'
1239 . '<option value="" dir="' . $GLOBALS['text_dir'] . '">'
1240 . '(' . __('Databases') . ') ...</option>' . "\n";
1241 $selected = $GLOBALS['db'];
1242 foreach ($children as $node) {
1243 $paths = $node->getPaths();
1244 if (isset($node->links
['text'])) {
1245 $title = empty($node->links
['title']) ?
'' : $node->links
['title'];
1246 $retval .= '<option value="'
1247 . htmlspecialchars($node->real_name
) . '"'
1248 . ' title="' . htmlspecialchars($title) . '"'
1249 . ' apath="' . $paths['aPath'] . '"'
1250 . ' vpath="' . $paths['vPath'] . '"'
1251 . ' pos="' . $this->_pos
. '"';
1252 if ($node->real_name
== $selected
1253 ||
(PMA_DRIZZLE
&& strtolower($node->real_name
) == strtolower($selected))
1255 $retval .= ' selected="selected"';
1257 $retval .= '>' . htmlspecialchars($node->real_name
);
1258 $retval .= '</option>';
1261 $retval .= '</select></form>';
1262 $retval .= '</div></div>';
1263 $retval .= '<div id="pma_navigation_tree_content"><ul>';
1264 $children = $this->_tree
->children
;
1265 usort($children, array('PMA_NavigationTree', 'sortNode'));
1266 $this->_setVisibility();
1267 for ($i=0, $nbChildren = count($children); $i < $nbChildren; $i++
) {
1269 $retval .= $this->_renderNode($children[0], true, 'first');
1270 } else if ($i +
1 != $nbChildren) {
1271 $retval .= $this->_renderNode($children[$i], true);
1273 $retval .= $this->_renderNode($children[$i], true, 'last');
1276 $retval .= '</ul></div>';
1281 * Makes some nodes visible based on the which node is active
1285 private function _setVisibility()
1287 foreach ($this->_vPath
as $path) {
1288 $node = $this->_tree
;
1289 foreach ($path as $value) {
1290 $child = $node->getChild($value);
1291 if ($child !== false) {
1292 $child->visible
= true;
1300 * Generates the HTML code for displaying the fast filter for tables
1302 * @param Node $node The node for which to generate the fast filter html
1304 * @return string LI element used for the fast filter
1306 private function _fastFilterHtml($node)
1310 = (int) $GLOBALS['cfg']['NavigationTreeDisplayDbFilterMinimum'];
1312 = (int) $GLOBALS['cfg']['NavigationTreeDisplayItemFilterMinimum'];
1313 if ($node === $this->_tree
1314 && $this->_tree
->getPresence() >= $filter_db_min
1316 $url_params = array(
1319 $retval .= '<li class="fast_filter db_fast_filter">';
1320 $retval .= '<form class="ajax fast_filter">';
1321 $retval .= PMA_getHiddenFields($url_params);
1322 $retval .= '<input class="searchClause" type="text"';
1323 $retval .= ' name="searchClause" accesskey="q"';
1324 // allow html5 placeholder attribute
1325 $placeholder_key = 'value';
1326 if (PMA_USR_BROWSER_AGENT
!== 'IE'
1327 || PMA_USR_BROWSER_VER
> 9
1329 $placeholder_key = 'placeholder';
1331 $retval .= " $placeholder_key='"
1332 . __('Filter databases by name or regex');
1334 $retval .= '<span title="' . __('Clear fast filter') . '">X</span>';
1335 $retval .= "</form>";
1337 } else if (($node->type
== Node
::CONTAINER
1338 && ( $node->real_name
== 'tables'
1339 ||
$node->real_name
== 'views'
1340 ||
$node->real_name
== 'functions'
1341 ||
$node->real_name
== 'procedures'
1342 ||
$node->real_name
== 'events'))
1343 && method_exists($node->realParent(), 'getPresence')
1344 && $node->realParent()->getPresence($node->real_name
) >= $filter_item_min
1346 $paths = $node->getPaths();
1347 $url_params = array(
1348 'pos' => $this->_pos
,
1349 'aPath' => $paths['aPath'],
1350 'vPath' => $paths['vPath'],
1351 'pos2_name' => $node->real_name
,
1354 $retval .= "<li class='fast_filter'>";
1355 $retval .= "<form class='ajax fast_filter'>";
1356 $retval .= PMA_getHiddenFields($url_params);
1357 $retval .= "<input class='searchClause' type='text'";
1358 $retval .= " name='searchClause2'";
1359 // allow html5 placeholder attribute
1360 $placeholder_key = 'value';
1361 if (PMA_USR_BROWSER_AGENT
!== 'IE'
1362 || PMA_USR_BROWSER_VER
> 9
1364 $placeholder_key = 'placeholder';
1366 $retval .= " $placeholder_key='"
1367 . __('Filter by name or regex') . "' />";
1368 $retval .= "<span title='" . __('Clear fast filter') . "'>X</span>";
1369 $retval .= "</form>";
1376 * Creates the code for displaying the controls
1377 * at the top of the navigation tree
1379 * @return string HTML code for the controls
1381 private function _controls()
1387 $retval = '<!-- CONTROLS START -->';
1388 $retval .= '<li id="navigation_controls_outer">';
1389 $retval .= '<div id="navigation_controls">';
1390 $retval .= PMA_Util
::getNavigationLink(
1395 's_collapseall.png',
1396 'pma_navigation_collapse'
1398 $syncImage = 's_unlink.png';
1399 $title = __('Link with main panel');
1400 if ($GLOBALS['cfg']['NavigationLinkWithMainPanel']) {
1401 $syncImage = 's_link.png';
1402 $title = __('Unlink from main panel');
1404 $retval .= PMA_Util
::getNavigationLink(
1410 'pma_navigation_sync'
1412 $retval .= '</div>';
1414 $retval .= '<!-- CONTROLS ENDS -->';
1419 * Generates the HTML code for displaying the list pagination
1421 * @param Node $node The node for whose children the page
1422 * selector will be created
1426 private function _getPageSelector($node)
1429 if ($node === $this->_tree
) {
1430 $retval .= PMA_Util
::getListNavigator(
1431 $this->_tree
->getPresence('databases', $this->_searchClause
),
1433 array('server' => $GLOBALS['server']),
1436 $GLOBALS['cfg']['FirstLevelNavigationItems'],
1440 } else if ($node->type
== Node
::CONTAINER
&& ! $node->is_group
) {
1441 $paths = $node->getPaths();
1443 $level = isset($paths['aPath_clean'][4]) ?
3 : 2;
1444 $_url_params = array(
1445 'aPath' => $paths['aPath'],
1446 'vPath' => $paths['vPath'],
1447 'pos' => $this->_pos
,
1448 'server' => $GLOBALS['server'],
1449 'pos2_name' => $paths['aPath_clean'][2]
1453 $_url_params['pos2_value'] = $node->pos2
;
1454 $_url_params['pos3_name'] = $paths['aPath_clean'][4];
1458 $num = $node->realParent()->getPresence(
1460 $this->_searchClause2
1462 $retval .= PMA_Util
::getListNavigator(
1468 $GLOBALS['cfg']['MaxNavigationItems'],
1469 'pos' . $level . '_value'
1476 * Called by usort() for sorting the nodes in a container
1478 * @param Node $a The first element used in the comparison
1479 * @param Node $b The second element used in the comparison
1481 * @return int See strnatcmp() and strcmp()
1483 static public function sortNode($a, $b)
1487 } else if ($b->isNew
) {
1490 if ($GLOBALS['cfg']['NaturalOrder']) {
1491 return strnatcasecmp($a->name
, $b->name
);
1493 return strcasecmp($a->name
, $b->name
);
1498 * Display quick warp links, contain Recents and Favorites
1500 * @return string HTML code
1502 private function _quickWarp()
1504 $retval = '<div class="pma_quick_warp">';
1505 if ($GLOBALS['cfg']['NumRecentTables'] > 0) {
1506 $retval .= PMA_RecentFavoriteTable
::getInstance('recent')->getHtml();
1508 if ($GLOBALS['cfg']['NumFavoriteTables'] > 0) {
1509 $retval .= PMA_RecentFavoriteTable
::getInstance('favorite')->getHtml();
1511 $retval .= '<div class="clearfloat"></div>';
1512 $retval .= '</div>';