NavigationTree.class.php 42 KB

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