NavigationTree.class.php 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Functionality for the navigation tree
  5. *
  6. * @package PhpMyAdmin-Navigation
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. /**
  12. * Displays a collapsible of database objects in the navigation frame
  13. *
  14. * @package PhpMyAdmin-Navigation
  15. */
  16. class PMA_NavigationTree
  17. {
  18. /**
  19. * @var Node Reference to the root node of the tree
  20. */
  21. private $_tree;
  22. /**
  23. * @var array The actual paths to all expanded nodes in the tree
  24. * This does not include nodes created after the grouping
  25. * of nodes has been performed
  26. */
  27. private $_aPath = array();
  28. /**
  29. * @var array The virtual paths to all expanded nodes in the tree
  30. * This includes nodes created after the grouping of
  31. * nodes has been performed
  32. */
  33. private $_vPath = array();
  34. /**
  35. * @var int Position in the list of databases,
  36. * used for pagination
  37. */
  38. private $_pos;
  39. /**
  40. * @var int The names of the type of items that are being paginated on
  41. * the second level of the navigation tree. These may be
  42. * tables, views, functions, procedures or events.
  43. */
  44. private $_pos2_name = array();
  45. /**
  46. * @var int The positions of nodes in the lists of tables, views,
  47. * routines or events used for pagination
  48. */
  49. private $_pos2_value = array();
  50. /**
  51. * @var int The names of the type of items that are being paginated
  52. * on the second level of the navigation tree.
  53. * These may be columns or indexes
  54. */
  55. private $_pos3_name = array();
  56. /**
  57. * @var int The positions of nodes in the lists of columns or indexes
  58. * used for pagination
  59. */
  60. private $_pos3_value = array();
  61. /**
  62. * @var string The search clause to use in SQL queries for
  63. * fetching databases
  64. * Used by the asynchronous fast filter
  65. */
  66. private $_searchClause = '';
  67. /**
  68. * @var string The search clause to use in SQL queries for
  69. * fetching nodes
  70. * Used by the asynchronous fast filter
  71. */
  72. private $_searchClause2 = '';
  73. /**
  74. * Initialises the class
  75. *
  76. * @return void
  77. */
  78. public function __construct()
  79. {
  80. // Save the position at which we are in the database list
  81. if (isset($_REQUEST['pos'])) {
  82. $this->_pos = (int) $_REQUEST['pos'];
  83. }
  84. if (! isset($this->_pos)) {
  85. $this->_pos = $this->_getNavigationDbPos();
  86. }
  87. // Get the active node
  88. if (isset($_REQUEST['aPath'])) {
  89. $this->_aPath[0] = $this->_parsePath($_REQUEST['aPath']);
  90. $this->_pos2_name[0] = $_REQUEST['pos2_name'];
  91. $this->_pos2_value[0] = $_REQUEST['pos2_value'];
  92. if (isset($_REQUEST['pos3_name'])) {
  93. $this->_pos3_name[0] = $_REQUEST['pos3_name'];
  94. $this->_pos3_value[0] = $_REQUEST['pos3_value'];
  95. }
  96. } else if (isset($_REQUEST['n0_aPath'])) {
  97. $count = 0;
  98. while (isset($_REQUEST['n' . $count . '_aPath'])) {
  99. $this->_aPath[$count] = $this->_parsePath(
  100. $_REQUEST['n' . $count . '_aPath']
  101. );
  102. $index = 'n' . $count . '_pos2_';
  103. $this->_pos2_name[$count] = $_REQUEST[$index . 'name'];
  104. $this->_pos2_value[$count] = $_REQUEST[$index . 'value'];
  105. $index = 'n' . $count . '_pos3_';
  106. if (isset($_REQUEST[$index])) {
  107. $this->_pos3_name[$count] = $_REQUEST[$index . 'name'];
  108. $this->_pos3_value[$count] = $_REQUEST[$index . 'value'];
  109. }
  110. $count++;
  111. }
  112. }
  113. if (isset($_REQUEST['vPath'])) {
  114. $this->_vPath[0] = $this->_parsePath($_REQUEST['vPath']);
  115. } else if (isset($_REQUEST['n0_vPath'])) {
  116. $count = 0;
  117. while (isset($_REQUEST['n' . $count . '_vPath'])) {
  118. $this->_vPath[$count] = $this->_parsePath(
  119. $_REQUEST['n' . $count . '_vPath']
  120. );
  121. $count++;
  122. }
  123. }
  124. if (isset($_REQUEST['searchClause'])) {
  125. $this->_searchClause = $_REQUEST['searchClause'];
  126. }
  127. if (isset($_REQUEST['searchClause2'])) {
  128. $this->_searchClause2 = $_REQUEST['searchClause2'];
  129. }
  130. // Initialise the tree by creating a root node
  131. $node = PMA_NodeFactory::getInstance('Node', 'root', Node::CONTAINER);
  132. $this->_tree = $node;
  133. if ($GLOBALS['cfg']['NavigationTreeEnableGrouping']) {
  134. $this->_tree->separator = $GLOBALS['cfg']['NavigationTreeDbSeparator'];
  135. $this->_tree->separator_depth = 10000;
  136. }
  137. }
  138. /**
  139. * Returns the database position for the page selector
  140. *
  141. * @return int
  142. */
  143. private function _getNavigationDbPos()
  144. {
  145. $retval = 0;
  146. if (! empty($GLOBALS['db'])) {
  147. $query = "SELECT (COUNT(`SCHEMA_NAME`) DIV %d) * %d ";
  148. $query .= "FROM `INFORMATION_SCHEMA`.`SCHEMATA` ";
  149. $query .= "WHERE `SCHEMA_NAME` < '%s' ";
  150. $query .= "ORDER BY `SCHEMA_NAME` ASC";
  151. $retval = PMA_DBI_fetch_value(
  152. sprintf(
  153. $query,
  154. (int)$GLOBALS['cfg']['MaxNavigationItems'],
  155. (int)$GLOBALS['cfg']['MaxNavigationItems'],
  156. PMA_Util::sqlAddSlashes($GLOBALS['db'])
  157. )
  158. );
  159. }
  160. return $retval;
  161. }
  162. /**
  163. * Converts an encoded path to a node in string format to an array
  164. *
  165. * @param string $string The path to parse
  166. *
  167. * @return array
  168. */
  169. private function _parsePath($string)
  170. {
  171. $path = explode('.', $string);
  172. foreach ($path as $key => $value) {
  173. $path[$key] = base64_decode($value);
  174. }
  175. return $path;
  176. }
  177. /**
  178. * Generates the tree structure so that it can be rendered later
  179. *
  180. * @return Node|false The active node or false in case of failure
  181. */
  182. private function _buildPath()
  183. {
  184. $retval = $this->_tree;
  185. // Add all databases unconditionally
  186. $data = $this->_tree->getData(
  187. 'databases',
  188. $this->_pos,
  189. $this->_searchClause
  190. );
  191. foreach ($data as $db) {
  192. $node = PMA_NodeFactory::getInstance('Node_Database', $db);
  193. $this->_tree->addChild($node);
  194. }
  195. // Whether build other parts of the tree depends
  196. // on whether we have any paths in $this->_aPath
  197. foreach ($this->_aPath as $key => $path) {
  198. $retval = $this->_buildPathPart(
  199. $path,
  200. $this->_pos2_name[$key],
  201. $this->_pos2_value[$key],
  202. isset($this->_pos3_name[$key]) ? $this->_pos3_name[$key] : '',
  203. isset($this->_pos3_value[$key]) ? $this->_pos3_value[$key] : ''
  204. );
  205. }
  206. return $retval;
  207. }
  208. /**
  209. * Builds a branch of the tree
  210. *
  211. * @param array $path A paths pointing to the branch
  212. * of the tree that needs to be built
  213. * @param string $type2 The type of item being paginated on
  214. * the second level of the tree
  215. * @param int $pos2 The position for the pagination of
  216. * the branch at the second level of the tree
  217. * @param string $type3 The type of item being paginated on
  218. * the third level of the tree
  219. * @param int $pos3 The position for the pagination of
  220. * the branch at the third level of the tree
  221. *
  222. * @return Node|false The active node or false in case of failure
  223. */
  224. private function _buildPathPart($path, $type2, $pos2, $type3, $pos3)
  225. {
  226. $retval = true;
  227. if (count($path) > 1) {
  228. array_shift($path); // remove 'root'
  229. $db = $this->_tree->getChild($path[0]);
  230. $retval = $db;
  231. if ($db === false) {
  232. return false;
  233. }
  234. $containers = $this->_addDbContainers($db, $type2, $pos2);
  235. array_shift($path); // remove db
  236. if ((count($path) > 0
  237. && array_key_exists($path[0], $containers))
  238. || count($containers) == 1
  239. ) {
  240. if (count($containers) == 1) {
  241. $container = array_shift($containers);
  242. } else {
  243. $container = $db->getChild($path[0], true);
  244. if ($container === false) {
  245. return false;
  246. }
  247. }
  248. $retval = $container;
  249. if (count($container->children) <= 1) {
  250. $dbData = $db->getData(
  251. $container->real_name,
  252. $pos2,
  253. $this->_searchClause2
  254. );
  255. foreach ($dbData as $item) {
  256. switch ($container->real_name) {
  257. case 'events':
  258. $node = PMA_NodeFactory::getInstance(
  259. 'Node_Event',
  260. $item
  261. );
  262. break;
  263. case 'functions':
  264. $node = PMA_NodeFactory::getInstance(
  265. 'Node_Function',
  266. $item
  267. );
  268. break;
  269. case 'procedures':
  270. $node = PMA_NodeFactory::getInstance(
  271. 'Node_Procedure',
  272. $item
  273. );
  274. break;
  275. case 'tables':
  276. $node = PMA_NodeFactory::getInstance(
  277. 'Node_Table',
  278. $item
  279. );
  280. break;
  281. case 'views':
  282. $node = PMA_NodeFactory::getInstance(
  283. 'Node_View',
  284. $item
  285. );
  286. break;
  287. default:
  288. break;
  289. }
  290. if (isset($node)) {
  291. if ($type2 == $container->real_name) {
  292. $node->pos2 = $pos2;
  293. }
  294. $container->addChild($node);
  295. }
  296. }
  297. }
  298. if (count($path) > 1 && $path[0] != 'tables') {
  299. $retval = false;
  300. } else {
  301. array_shift($path); // remove container
  302. if (count($path) > 0) {
  303. $table = $container->getChild($path[0], true);
  304. if ($table === false) {
  305. return false;
  306. }
  307. $retval = $table;
  308. $containers = $this->_addTableContainers(
  309. $table,
  310. $pos2,
  311. $type3,
  312. $pos3
  313. );
  314. array_shift($path); // remove table
  315. if (count($path) > 0
  316. && array_key_exists($path[0], $containers)
  317. ) {
  318. $container = $table->getChild($path[0], true);
  319. $retval = $container;
  320. $tableData = $table->getData(
  321. $container->real_name,
  322. $pos3
  323. );
  324. foreach ($tableData as $item) {
  325. switch ($container->real_name) {
  326. case 'indexes':
  327. $node = PMA_NodeFactory::getInstance(
  328. 'Node_Index',
  329. $item
  330. );
  331. break;
  332. case 'columns':
  333. $node = PMA_NodeFactory::getInstance(
  334. 'Node_Column',
  335. $item
  336. );
  337. break;
  338. case 'triggers':
  339. $node = PMA_NodeFactory::getInstance(
  340. 'Node_Trigger',
  341. $item
  342. );
  343. break;
  344. default:
  345. break;
  346. }
  347. if (isset($node)) {
  348. $node->pos2 = $container->parent->pos2;
  349. if ($type3 == $container->real_name) {
  350. $node->pos3 = $pos3;
  351. }
  352. $container->addChild($node);
  353. }
  354. }
  355. }
  356. }
  357. }
  358. }
  359. }
  360. return $retval;
  361. }
  362. /**
  363. * Adds containers to a node that is a table
  364. *
  365. * References to existing children are returned
  366. * if this function is called twice on the same node
  367. *
  368. * @param Node $table The table node, new containers will be
  369. * attached to this node
  370. * @param int $pos2 The position for the pagination of
  371. * the branch at the second level of the tree
  372. * @param string $type3 The type of item being paginated on
  373. * the third level of the tree
  374. * @param int $pos3 The position for the pagination of
  375. * the branch at the third level of the tree
  376. *
  377. * @return array An array of new nodes
  378. */
  379. private function _addTableContainers($table, $pos2, $type3, $pos3)
  380. {
  381. $retval = array();
  382. if ($table->hasChildren(true) == 0) {
  383. if ($table->getPresence('columns')) {
  384. $retval['columns'] = PMA_NodeFactory::getInstance(
  385. 'Node_Column_Container'
  386. );
  387. }
  388. if ($table->getPresence('indexes')) {
  389. $retval['indexes'] = PMA_NodeFactory::getInstance(
  390. 'Node_Index_Container'
  391. );
  392. }
  393. if ($table->getPresence('triggers')) {
  394. $retval['triggers'] = PMA_NodeFactory::getInstance(
  395. 'Node_Trigger_Container'
  396. );
  397. }
  398. // Add all new Nodes to the tree
  399. foreach ($retval as $node) {
  400. $node->pos2 = $pos2;
  401. if ($type3 == $node->real_name) {
  402. $node->pos3 = $pos3;
  403. }
  404. $table->addChild($node);
  405. }
  406. } else {
  407. foreach ($table->children as $node) {
  408. if ($type3 == $node->real_name) {
  409. $node->pos3 = $pos3;
  410. }
  411. $retval[$node->real_name] = $node;
  412. }
  413. }
  414. return $retval;
  415. }
  416. /**
  417. * Adds containers to a node that is a database
  418. *
  419. * References to existing children are returned
  420. * if this function is called twice on the same node
  421. *
  422. * @param Node $db The database node, new containers will be
  423. * attached to this node
  424. * @param string $type The type of item being paginated on
  425. * the second level of the tree
  426. * @param int $pos2 The position for the pagination of
  427. * the branch at the second level of the tree
  428. *
  429. * @return array An array of new nodes
  430. */
  431. private function _addDbContainers($db, $type, $pos2)
  432. {
  433. $retval = array();
  434. if ($db->hasChildren(true) == 0) {
  435. if ($db->getPresence('tables')) {
  436. $retval['tables'] = PMA_NodeFactory::getInstance(
  437. 'Node_Table_Container'
  438. );
  439. }
  440. if ($db->getPresence('views')) {
  441. $retval['views'] = PMA_NodeFactory::getInstance(
  442. 'Node_View_Container'
  443. );
  444. }
  445. if ($db->getPresence('functions')) {
  446. $retval['functions'] = PMA_NodeFactory::getInstance(
  447. 'Node_Function_Container'
  448. );
  449. }
  450. if ($db->getPresence('procedures')) {
  451. $retval['procedures'] = PMA_NodeFactory::getInstance(
  452. 'Node_Procedure_Container'
  453. );
  454. }
  455. if ($db->getPresence('events')) {
  456. $retval['events'] = PMA_NodeFactory::getInstance(
  457. 'Node_Event_Container'
  458. );
  459. }
  460. // Add all new Nodes to the tree
  461. foreach ($retval as $node) {
  462. if ($type == $node->real_name) {
  463. $node->pos2 = $pos2;
  464. }
  465. $db->addChild($node);
  466. }
  467. } else {
  468. foreach ($db->children as $node) {
  469. if ($type == $node->real_name) {
  470. $node->pos2 = $pos2;
  471. }
  472. $retval[$node->real_name] = $node;
  473. }
  474. }
  475. return $retval;
  476. }
  477. /**
  478. * Recursively groups tree nodes given a separator
  479. *
  480. * @param mixed $node The node to group or null
  481. * to group the whole tree. If
  482. * passed as an argument, $node
  483. * must be of type CONTAINER
  484. *
  485. * @return void
  486. */
  487. public function groupTree($node = null)
  488. {
  489. if (! isset($node)) {
  490. $node = $this->_tree;
  491. }
  492. $this->groupNode($node);
  493. foreach ($node->children as $child) {
  494. $this->groupTree($child);
  495. }
  496. }
  497. /**
  498. * Recursively groups tree nodes given a sperarator
  499. *
  500. * @param Node $node The node to group
  501. *
  502. * @return void
  503. */
  504. public function groupNode($node)
  505. {
  506. if ($node->type == Node::CONTAINER) {
  507. $separators = array();
  508. if (is_array($node->separator)) {
  509. $separators = $node->separator;
  510. } else if (strlen($node->separator)) {
  511. $separators[] = $node->separator;
  512. }
  513. $prefixes = array();
  514. if ($node->separator_depth > 0) {
  515. foreach ($node->children as $child) {
  516. $prefix_pos = false;
  517. foreach ($separators as $separator) {
  518. $sep_pos = strpos($child->name, $separator);
  519. if ($sep_pos != false
  520. && $sep_pos != strlen($child->name)
  521. && $sep_pos != 0
  522. && ($prefix_pos == false || $sep_pos < $prefix_pos)
  523. ) {
  524. $prefix_pos = $sep_pos;
  525. }
  526. }
  527. if ($prefix_pos !== false) {
  528. $prefix = substr($child->name, 0, $prefix_pos);
  529. if (! isset($prefixes[$prefix])) {
  530. $prefixes[$prefix] = 1;
  531. } else {
  532. $prefixes[$prefix]++;
  533. }
  534. }
  535. }
  536. }
  537. foreach ($prefixes as $key => $value) {
  538. if ($value == 1) {
  539. unset($prefixes[$key]);
  540. }
  541. }
  542. if (count($prefixes)) {
  543. $groups = array();
  544. foreach ($prefixes as $key => $value) {
  545. $groups[$key] = new Node(
  546. $key,
  547. Node::CONTAINER,
  548. true
  549. );
  550. $groups[$key]->separator = $node->separator;
  551. $groups[$key]->separator_depth = $node->separator_depth - 1;
  552. $groups[$key]->icon = '';
  553. if (in_array(
  554. $GLOBALS['cfg']['TableNavigationLinksMode'],
  555. array('icons', 'both')
  556. )
  557. ) {
  558. $groups[$key]->icon = PMA_Util::getImage(
  559. 'b_group.png'
  560. );
  561. }
  562. $groups[$key]->pos2 = $node->pos2;
  563. $groups[$key]->pos3 = $node->pos3;
  564. if ($node instanceof Node_Table_Container) {
  565. $tblGroup = '&amp;tbl_group='
  566. . urlencode($key . $node->separator);
  567. $groups[$key]->links = array(
  568. 'text' => $node->links['text'] . $tblGroup,
  569. 'icon' => $node->links['icon'] . $tblGroup
  570. );
  571. }
  572. $node->addChild($groups[$key]);
  573. foreach ($separators as $separator) {
  574. // FIXME: this could be more efficient
  575. foreach ($node->children as $child) {
  576. $name_substring = substr(
  577. $child->name, 0, strlen($key) + strlen($separator)
  578. );
  579. if (($name_substring == $key . $separator
  580. || $child->name == $key)
  581. && $child->type == Node::OBJECT
  582. ) {
  583. $class = get_class($child);
  584. $new_child = PMA_NodeFactory::getInstance(
  585. $class,
  586. substr(
  587. $child->name,
  588. strlen($key) + strlen($separator)
  589. )
  590. );
  591. $new_child->real_name = $child->real_name;
  592. $new_child->icon = $child->icon;
  593. $new_child->links = $child->links;
  594. $new_child->pos2 = $child->pos2;
  595. $new_child->pos3 = $child->pos3;
  596. $groups[$key]->addChild($new_child);
  597. foreach ($child->children as $elm) {
  598. $new_child->addChild($elm);
  599. }
  600. $node->removeChild($child->name);
  601. }
  602. }
  603. }
  604. }
  605. foreach ($prefixes as $key => $value) {
  606. $this->groupNode($groups[$key]);
  607. $groups[$key]->classes = "navGroup";
  608. }
  609. }
  610. }
  611. }
  612. /**
  613. * Renders a state of the tree, used in light mode when
  614. * either JavaScript and/or Ajax are disabled
  615. *
  616. * @return string HTML code for the navigation tree
  617. */
  618. public function renderState()
  619. {
  620. $this->_buildPath();
  621. $retval = $this->_fastFilterHtml($this->_tree);
  622. $retval .= $this->_getPageSelector($this->_tree);
  623. $this->groupTree();
  624. $retval .= "<div id='pma_navigation_tree_content'><ul>";
  625. $children = $this->_tree->children;
  626. usort($children, array('PMA_NavigationTree', 'sortNode'));
  627. $this->_setVisibility();
  628. for ($i=0; $i<count($children); $i++) {
  629. if ($i == 0) {
  630. $retval .= $this->_renderNode($children[0], true, 'first');
  631. } else if ($i + 1 != count($children)) {
  632. $retval .= $this->_renderNode($children[$i], true);
  633. } else {
  634. $retval .= $this->_renderNode($children[$i], true, 'last');
  635. }
  636. }
  637. $retval .= "</ul></div>";
  638. return $retval;
  639. }
  640. /**
  641. * Renders a part of the tree, used for Ajax
  642. * requests in light mode
  643. *
  644. * @return string HTML code for the navigation tree
  645. */
  646. public function renderPath()
  647. {
  648. $node = $this->_buildPath();
  649. if ($node === false) {
  650. $retval = false;
  651. } else {
  652. $this->groupTree();
  653. $retval = "<div class='list_container' style='display: none;'>";
  654. $retval .= "<ul>";
  655. $retval .= $this->_fastFilterHtml($node);
  656. $retval .= $this->_getPageSelector($node);
  657. $children = $node->children;
  658. usort($children, array('PMA_NavigationTree', 'sortNode'));
  659. for ($i=0; $i<count($children); $i++) {
  660. if ($i + 1 != count($children)) {
  661. $retval .= $this->_renderNode($children[$i], true);
  662. } else {
  663. $retval .= $this->_renderNode($children[$i], true, 'last');
  664. }
  665. }
  666. $retval .= "</ul>";
  667. $retval .= "</div>";
  668. }
  669. if (! empty($this->_searchClause) || ! empty($this->_searchClause2)) {
  670. if (! empty($this->_searchClause2)) {
  671. $results = $node->realParent()->getPresence(
  672. $node->real_name,
  673. $this->_searchClause2
  674. );
  675. } else {
  676. $results = $this->_tree->getPresence(
  677. 'databases',
  678. $this->_searchClause
  679. );
  680. }
  681. $clientResults = 0;
  682. if (! empty($_REQUEST['results'])) {
  683. $clientResults = (int)$_REQUEST['results'];
  684. }
  685. $otherResults = $results - $clientResults;
  686. if ($otherResults < 1) {
  687. $otherResults = '';
  688. } else {
  689. $otherResults = sprintf(
  690. _ngettext(
  691. '%s other result found',
  692. '%s other results found',
  693. $otherResults
  694. ),
  695. $otherResults
  696. );
  697. }
  698. PMA_Response::getInstance()->addJSON(
  699. 'results',
  700. $otherResults
  701. );
  702. }
  703. return $retval;
  704. }
  705. /**
  706. * Renders the parameters that are required on the client
  707. * side to know which page(s) we will be requesting data from
  708. *
  709. * @param Node $node The node to create the pagination parameters for
  710. *
  711. * @return string
  712. */
  713. private function _getPaginationParamsHtml($node)
  714. {
  715. $retval = '';
  716. $paths = $node->getPaths();
  717. if (isset($paths['aPath_clean'][2])) {
  718. $retval .= "<span class='hide pos2_name'>";
  719. $retval .= $paths['aPath_clean'][2];
  720. $retval .= "</span>";
  721. $retval .= "<span class='hide pos2_value'>";
  722. $retval .= $node->pos2;
  723. $retval .= "</span>";
  724. }
  725. if (isset($paths['aPath_clean'][4])) {
  726. $retval .= "<span class='hide pos3_name'>";
  727. $retval .= $paths['aPath_clean'][4];
  728. $retval .= "</span>";
  729. $retval .= "<span class='hide pos3_value'>";
  730. $retval .= $node->pos3;
  731. $retval .= "</span>";
  732. }
  733. return $retval;
  734. }
  735. /**
  736. * Renders a single node or a branch of the tree
  737. *
  738. * @param Node $node The node to render
  739. * @param int|bool $recursive Bool: Whether to render a single node or a branch
  740. * Int: How many levels deep to render
  741. * @param string $class An additional class for the list item
  742. *
  743. * @return string HTML code for the tree node or branch
  744. */
  745. private function _renderNode($node, $recursive = -1, $class = '')
  746. {
  747. $retval = '';
  748. $paths = $node->getPaths();
  749. if ($node->hasSiblings()
  750. || isset($_REQUEST['results'])
  751. || $node->realParent() === false
  752. ) {
  753. if ( $node->type == Node::CONTAINER
  754. && count($node->children) == 0
  755. && $GLOBALS['is_ajax_request'] != true
  756. ) {
  757. return '';
  758. }
  759. $liClass = '';
  760. if ($class || $node->classes) {
  761. $liClass = " class='" . trim($class . ' ' . $node->classes) . "'";
  762. }
  763. $retval .= "<li$liClass>";
  764. $sterile = array(
  765. 'events',
  766. 'triggers',
  767. 'functions',
  768. 'procedures',
  769. 'views',
  770. 'columns',
  771. 'indexes'
  772. );
  773. $parentName = '';
  774. $parents = $node->parents(false, true);
  775. if (count($parents)) {
  776. $parentName = $parents[0]->real_name;
  777. }
  778. if ($node->is_group
  779. || (! in_array($parentName, $sterile) && ! $node->isNew)
  780. ) {
  781. $loaded = '';
  782. if ($node->is_group) {
  783. $loaded = ' loaded';
  784. }
  785. $container = '';
  786. if ($node->type == Node::CONTAINER) {
  787. $container = ' container';
  788. }
  789. $retval .= "<div class='block'>";
  790. $iClass = '';
  791. if ($class == 'first') {
  792. $iClass = " class='first'";
  793. }
  794. $retval .= "<i$iClass></i>";
  795. if (strpos($class, 'last') === false) {
  796. $retval .= "<b></b>";
  797. }
  798. $icon = PMA_Util::getImage('b_plus.png', __('Expand/Collapse'));
  799. $match = 1;
  800. foreach ($this->_aPath as $path) {
  801. $match = 1;
  802. foreach ($paths['aPath_clean'] as $key => $part) {
  803. if (! isset($path[$key]) || $part != $path[$key]) {
  804. $match = 0;
  805. break;
  806. }
  807. }
  808. if ($match) {
  809. $loaded = ' loaded';
  810. if (! $node->is_group) {
  811. $icon = PMA_Util::getImage(
  812. 'b_minus.png'
  813. );
  814. }
  815. break;
  816. }
  817. }
  818. foreach ($this->_vPath as $path) {
  819. $match = 1;
  820. foreach ($paths['vPath_clean'] as $key => $part) {
  821. if ((! isset($path[$key]) || $part != $path[$key])) {
  822. $match = 0;
  823. break;
  824. }
  825. }
  826. if ($match) {
  827. $loaded = ' loaded';
  828. $icon = PMA_Util::getImage('b_minus.png');
  829. break;
  830. }
  831. }
  832. $retval .= "<a class='expander$loaded$container'";
  833. $retval .= " href='#'>";
  834. $retval .= "<span class='hide aPath'>";
  835. $retval .= $paths['aPath'];
  836. $retval .= "</span>";
  837. $retval .= "<span class='hide vPath'>";
  838. $retval .= $paths['vPath'];
  839. $retval .= "</span>";
  840. $retval .= "<span class='hide pos'>";
  841. $retval .= $this->_pos;
  842. $retval .= "</span>";
  843. $retval .= $this->_getPaginationParamsHtml($node);
  844. $retval .= $icon;
  845. $retval .= "</a>";
  846. $retval .= "</div>";
  847. } else {
  848. $retval .= "<div class='block'>";
  849. $iClass = '';
  850. if ($class == 'first') {
  851. $iClass = " class='first'";
  852. }
  853. $retval .= "<i$iClass></i>";
  854. $retval .= $this->_getPaginationParamsHtml($node);
  855. $retval .= "</div>";
  856. }
  857. $linkClass = '';
  858. $haveAjax = array(
  859. 'functions',
  860. 'procedures',
  861. 'events',
  862. 'triggers',
  863. 'indexes'
  864. );
  865. $parent = $node->parents(false, true);
  866. if ($parent[0]->type == Node::CONTAINER
  867. && (in_array($parent[0]->real_name, $haveAjax)
  868. || ($parent[0]->real_name == 'views'
  869. && $node->isNew == true
  870. )
  871. )
  872. ) {
  873. $linkClass = ' class="ajax"';
  874. }
  875. if ($node->type == Node::CONTAINER) {
  876. $retval .= "<i>";
  877. }
  878. if (in_array(
  879. $GLOBALS['cfg']['TableNavigationLinksMode'],
  880. array('icons', 'both')
  881. )
  882. ) {
  883. $retval .= "<div class='block'>";
  884. if (isset($node->links['icon'])) {
  885. $args = array();
  886. foreach ($node->parents(true) as $parent) {
  887. $args[] = urlencode($parent->real_name);
  888. }
  889. $link = vsprintf($node->links['icon'], $args);
  890. $retval .= "<a$linkClass href='$link'>{$node->icon}</a>";
  891. } else {
  892. $retval .= "<u>{$node->icon}</u>";
  893. }
  894. $retval .= "</div>";
  895. }
  896. if (isset($node->links['text'])) {
  897. $args = array();
  898. foreach ($node->parents(true) as $parent) {
  899. $args[] = urlencode($parent->real_name);
  900. }
  901. $link = vsprintf($node->links['text'], $args);
  902. if ($node->type == Node::CONTAINER) {
  903. $retval .= "<a href='$link'>";
  904. $retval .= htmlspecialchars($node->name);
  905. $retval .= "</a>";
  906. } else {
  907. $retval .= "<a$linkClass href='$link'>";
  908. $retval .= htmlspecialchars($node->real_name);
  909. $retval .= "</a>";
  910. }
  911. } else {
  912. $retval .= "{$node->name}";
  913. }
  914. if ($node->type == Node::CONTAINER) {
  915. $retval .= "</i>";
  916. }
  917. $wrap = true;
  918. } else {
  919. $node->visible = true;
  920. $wrap = false;
  921. $retval .= $this->_getPaginationParamsHtml($node);
  922. }
  923. if ($recursive) {
  924. $hide = '';
  925. if ($node->visible == false) {
  926. $hide = " style='display: none;'";
  927. }
  928. $children = $node->children;
  929. usort($children, array('PMA_NavigationTree', 'sortNode'));
  930. $buffer = '';
  931. for ($i=0; $i<count($children); $i++) {
  932. if ($i + 1 != count($children)) {
  933. $buffer .= $this->_renderNode(
  934. $children[$i],
  935. true,
  936. $children[$i]->classes
  937. );
  938. } else {
  939. $buffer .= $this->_renderNode(
  940. $children[$i],
  941. true,
  942. $children[$i]->classes . ' last'
  943. );
  944. }
  945. }
  946. if (! empty($buffer)) {
  947. if ($wrap) {
  948. $retval .= "<div$hide class='list_container'><ul>";
  949. }
  950. $retval .= $this->_fastFilterHtml($node);
  951. $retval .= $this->_getPageSelector($node);
  952. $retval .= $buffer;
  953. if ($wrap) {
  954. $retval .= "</ul></div>";
  955. }
  956. }
  957. }
  958. if ($node->hasSiblings() || isset($_REQUEST['results'])) {
  959. $retval .= "</li>";
  960. }
  961. return $retval;
  962. }
  963. /**
  964. * Makes some nodes visible based on the which node is active
  965. *
  966. * @return nothing
  967. */
  968. private function _setVisibility()
  969. {
  970. foreach ($this->_vPath as $path) {
  971. $node = $this->_tree;
  972. foreach ($path as $value) {
  973. $child = $node->getChild($value);
  974. if ($child !== false) {
  975. $child->visible = true;
  976. $node = $child;
  977. }
  978. }
  979. }
  980. }
  981. /**
  982. * Generates the HTML code for displaying the fast filter for tables
  983. *
  984. * @param Node $node The node for which to generate the fast filter html
  985. *
  986. * @return string LI element used for the fast filter
  987. */
  988. private function _fastFilterHtml($node)
  989. {
  990. $retval = '';
  991. if ($node === $this->_tree
  992. && $this->_tree->getPresence() >= (int)$GLOBALS['cfg']['NavigationTreeDisplayDbFilterMinimum']
  993. ) {
  994. $url_params = array(
  995. 'pos' => 0
  996. );
  997. $retval .= "<ul>";
  998. $retval .= "<li class='fast_filter db_fast_filter'>";
  999. $retval .= "<form class='ajax fast_filter'>";
  1000. $retval .= PMA_getHiddenFields($url_params);
  1001. $retval .= "<input class='searchClause' name='searchClause'";
  1002. $retval .= " value='" . __('filter databases by name') . "' />";
  1003. $retval .= "<span title='" . __('Clear Fast Filter') . "'>X</span>";
  1004. $retval .= "</form>";
  1005. $retval .= "</li>";
  1006. $retval .= "</ul>";
  1007. } else if (($node->type == Node::CONTAINER
  1008. && ( $node->real_name == 'tables'
  1009. || $node->real_name == 'views'
  1010. || $node->real_name == 'functions'
  1011. || $node->real_name == 'procedures'
  1012. || $node->real_name == 'events')
  1013. )
  1014. && method_exists($node->realParent(),'getPresence')
  1015. && $node->realParent()->getPresence($node->real_name) >= (int)$GLOBALS['cfg']['NavigationTreeDisplayItemFilterMinimum']
  1016. ) {
  1017. $paths = $node->getPaths();
  1018. $url_params = array(
  1019. 'pos' => $this->_pos,
  1020. 'aPath' => $paths['aPath'],
  1021. 'vPath' => $paths['vPath'],
  1022. 'pos2_name' => $node->real_name,
  1023. 'pos2_value' => 0
  1024. );
  1025. $retval .= "<li class='fast_filter'>";
  1026. $retval .= "<form class='ajax fast_filter'>";
  1027. $retval .= PMA_getHiddenFields($url_params);
  1028. $retval .= "<input class='searchClause' name='searchClause2'";
  1029. $retval .= " value='" . __('filter items by name') . "' />";
  1030. $retval .= "<span title='" . __('Clear Fast Filter') . "'>X</span>";
  1031. $retval .= "</form>";
  1032. $retval .= "</li>";
  1033. }
  1034. return $retval;
  1035. }
  1036. /**
  1037. * Generates the HTML code for displaying the list pagination
  1038. *
  1039. * @param Node $node The node for whose children the page
  1040. * selector will be created
  1041. *
  1042. * @return string
  1043. */
  1044. private function _getPageSelector($node)
  1045. {
  1046. $retval = '';
  1047. if ($node === $this->_tree) {
  1048. $retval .= PMA_Util::getListNavigator(
  1049. $this->_tree->getPresence('databases', $this->_searchClause),
  1050. $this->_pos,
  1051. array('server' => $GLOBALS['server']),
  1052. 'navigation.php',
  1053. 'frame_navigation',
  1054. $GLOBALS['cfg']['MaxNavigationItems'],
  1055. 'pos',
  1056. array('dbselector')
  1057. );
  1058. } else if ($node->type == Node::CONTAINER && ! $node->is_group) {
  1059. $paths = $node->getPaths();
  1060. $level = isset($paths['aPath_clean'][4]) ? 3 : 2;
  1061. $_url_params = array(
  1062. 'aPath' => $paths['aPath'],
  1063. 'vPath' => $paths['vPath'],
  1064. 'pos' => $this->_pos,
  1065. 'server' => $GLOBALS['server'],
  1066. 'pos2_name' => $paths['aPath_clean'][2]
  1067. );
  1068. if ($level == 3) {
  1069. $pos = $node->pos3;
  1070. $_url_params['pos2_value'] = $node->pos2;
  1071. $_url_params['pos3_name'] = $paths['aPath_clean'][4];
  1072. } else {
  1073. $pos = $node->pos2;
  1074. }
  1075. $num = $node->realParent()->getPresence(
  1076. $node->real_name,
  1077. $this->_searchClause2
  1078. );
  1079. $retval .= PMA_Util::getListNavigator(
  1080. $num,
  1081. $pos,
  1082. $_url_params,
  1083. 'navigation.php',
  1084. 'frame_navigation',
  1085. $GLOBALS['cfg']['MaxNavigationItems'],
  1086. 'pos' . $level . '_value'
  1087. );
  1088. }
  1089. return $retval;
  1090. }
  1091. /**
  1092. * Called by usort() for sorting the nodes in a container
  1093. *
  1094. * @param Node $a The first element used in the comparison
  1095. * @param Node $b The second element used in the comparison
  1096. *
  1097. * @return int See strnatcmp() and strcmp()
  1098. */
  1099. static public function sortNode($a, $b)
  1100. {
  1101. if ($a->isNew) {
  1102. return -1;
  1103. } else if ($b->isNew) {
  1104. return 1;
  1105. }
  1106. if ($GLOBALS['cfg']['NaturalOrder']) {
  1107. return strnatcasecmp($a->name, $b->name);
  1108. } else {
  1109. return strcasecmp($a->name, $b->name);
  1110. }
  1111. }
  1112. }
  1113. ?>