NavigationTree.php 53 KB

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