Node.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Functionality for the navigation tree in the left frame
  5. *
  6. * @package PhpMyAdmin-Navigation
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin\Navigation\Nodes;
  10. use PhpMyAdmin\DatabaseInterface;
  11. use PhpMyAdmin\Relation;
  12. use PhpMyAdmin\Util;
  13. /**
  14. * The Node is the building block for the collapsible navigation tree
  15. *
  16. * @package PhpMyAdmin-Navigation
  17. */
  18. class Node
  19. {
  20. /**
  21. * @var int Defines a possible node type
  22. */
  23. public const CONTAINER = 0;
  24. /**
  25. * @var int Defines a possible node type
  26. */
  27. public const OBJECT = 1;
  28. /**
  29. * @var string A non-unique identifier for the node
  30. * This may be trimmed when grouping nodes
  31. */
  32. public $name = "";
  33. /**
  34. * @var string A non-unique identifier for the node
  35. * This will never change after being assigned
  36. */
  37. public $realName = "";
  38. /**
  39. * @var int May be one of CONTAINER or OBJECT
  40. */
  41. public $type = Node::OBJECT;
  42. /**
  43. * @var bool Whether this object has been created while grouping nodes
  44. * Only relevant if the node is of type CONTAINER
  45. */
  46. public $isGroup;
  47. /**
  48. * @var bool Whether to add a "display: none;" CSS
  49. * rule to the node when rendering it
  50. */
  51. public $visible = false;
  52. /**
  53. * @var Node A reference to the parent object of
  54. * this node, NULL for the root node.
  55. */
  56. public $parent;
  57. /**
  58. * @var Node[] An array of Node objects that are
  59. * direct children of this node
  60. */
  61. public $children = [];
  62. /**
  63. * @var Mixed A string used to group nodes, or an array of strings
  64. * Only relevant if the node is of type CONTAINER
  65. */
  66. public $separator = '';
  67. /**
  68. * @var int How many time to recursively apply the grouping function
  69. * Only relevant if the node is of type CONTAINER
  70. */
  71. public $separatorDepth = 1;
  72. /**
  73. * @var string|array An IMG tag, used when rendering the node, an array for NodeTabl
  74. */
  75. public $icon;
  76. /**
  77. * @var array An array of A tags, used when rendering the node
  78. * The indexes in the array may be 'icon' and 'text'
  79. */
  80. public $links;
  81. /**
  82. * @var string HTML title
  83. */
  84. public $title;
  85. /**
  86. * @var string Extra CSS classes for the node
  87. */
  88. public $classes = '';
  89. /**
  90. * @var bool Whether this node is a link for creating new objects
  91. */
  92. public $isNew = false;
  93. /**
  94. * @var int The position for the pagination of
  95. * the branch at the second level of the tree
  96. */
  97. public $pos2 = 0;
  98. /**
  99. * @var int The position for the pagination of
  100. * the branch at the third level of the tree
  101. */
  102. public $pos3 = 0;
  103. /**
  104. * @var Relation
  105. */
  106. protected $relation;
  107. /**
  108. * @var string $displayName display name for the navigation tree
  109. */
  110. public $displayName;
  111. /**
  112. * Initialises the class by setting the mandatory variables
  113. *
  114. * @param string $name An identifier for the new node
  115. * @param int $type Type of node, may be one of CONTAINER or OBJECT
  116. * @param bool $isGroup Whether this object has been created
  117. * while grouping nodes
  118. */
  119. public function __construct($name, $type = Node::OBJECT, $isGroup = false)
  120. {
  121. if (strlen((string) $name)) {
  122. $this->name = $name;
  123. $this->realName = $name;
  124. }
  125. if ($type === Node::CONTAINER) {
  126. $this->type = Node::CONTAINER;
  127. }
  128. $this->isGroup = (bool) $isGroup;
  129. $this->relation = new Relation($GLOBALS['dbi']);
  130. }
  131. /**
  132. * Adds a child node to this node
  133. *
  134. * @param Node $child A child node
  135. *
  136. * @return void
  137. */
  138. public function addChild($child)
  139. {
  140. $this->children[] = $child;
  141. $child->parent = $this;
  142. }
  143. /**
  144. * Returns a child node given it's name
  145. *
  146. * @param string $name The name of requested child
  147. * @param bool $realName Whether to use the "realName"
  148. * instead of "name" in comparisons
  149. *
  150. * @return false|Node The requested child node or false,
  151. * if the requested node cannot be found
  152. */
  153. public function getChild($name, $realName = false)
  154. {
  155. if ($realName) {
  156. foreach ($this->children as $child) {
  157. if ($child->realName == $name) {
  158. return $child;
  159. }
  160. }
  161. } else {
  162. foreach ($this->children as $child) {
  163. if ($child->name == $name) {
  164. return $child;
  165. }
  166. }
  167. }
  168. return false;
  169. }
  170. /**
  171. * Removes a child node from this node
  172. *
  173. * @param string $name The name of child to be removed
  174. *
  175. * @return void
  176. */
  177. public function removeChild($name)
  178. {
  179. foreach ($this->children as $key => $child) {
  180. if ($child->name == $name) {
  181. unset($this->children[$key]);
  182. break;
  183. }
  184. }
  185. }
  186. /**
  187. * Retrieves the parents for a node
  188. *
  189. * @param bool $self Whether to include the Node itself in the results
  190. * @param bool $containers Whether to include nodes of type CONTAINER
  191. * @param bool $groups Whether to include nodes which have $group == true
  192. *
  193. * @return array An array of parent Nodes
  194. */
  195. public function parents($self = false, $containers = false, $groups = false)
  196. {
  197. $parents = [];
  198. if ($self
  199. && ($this->type != Node::CONTAINER || $containers)
  200. && (! $this->isGroup || $groups)
  201. ) {
  202. $parents[] = $this;
  203. }
  204. $parent = $this->parent;
  205. while ($parent !== null) {
  206. if (($parent->type != Node::CONTAINER || $containers)
  207. && (! $parent->isGroup || $groups)
  208. ) {
  209. $parents[] = $parent;
  210. }
  211. $parent = $parent->parent;
  212. }
  213. return $parents;
  214. }
  215. /**
  216. * Returns the actual parent of a node. If used twice on an index or columns
  217. * node, it will return the table and database nodes. The names of the returned
  218. * nodes can be used in SQL queries, etc...
  219. *
  220. * @return Node|false
  221. */
  222. public function realParent()
  223. {
  224. $retval = $this->parents();
  225. if (count($retval) <= 0) {
  226. return false;
  227. }
  228. return $retval[0];
  229. }
  230. /**
  231. * This function checks if the node has children nodes associated with it
  232. *
  233. * @param bool $countEmptyContainers Whether to count empty child
  234. * containers as valid children
  235. *
  236. * @return bool Whether the node has child nodes
  237. */
  238. public function hasChildren($countEmptyContainers = true)
  239. {
  240. $retval = false;
  241. if ($countEmptyContainers) {
  242. if (count($this->children)) {
  243. $retval = true;
  244. }
  245. } else {
  246. foreach ($this->children as $child) {
  247. if ($child->type == Node::OBJECT || $child->hasChildren(false)) {
  248. $retval = true;
  249. break;
  250. }
  251. }
  252. }
  253. return $retval;
  254. }
  255. /**
  256. * Returns true if the node has some siblings (other nodes on the same tree
  257. * level, in the same branch), false otherwise.
  258. * The only exception is for nodes on
  259. * the third level of the tree (columns and indexes), for which the function
  260. * always returns true. This is because we want to render the containers
  261. * for these nodes
  262. *
  263. * @return bool
  264. */
  265. public function hasSiblings()
  266. {
  267. $retval = false;
  268. $paths = $this->getPaths();
  269. if (count($paths['aPath_clean']) > 3) {
  270. return true;
  271. }
  272. foreach ($this->parent->children as $child) {
  273. if ($child !== $this
  274. && ($child->type == Node::OBJECT || $child->hasChildren(false))
  275. ) {
  276. $retval = true;
  277. break;
  278. }
  279. }
  280. return $retval;
  281. }
  282. /**
  283. * Returns the number of child nodes that a node has associated with it
  284. *
  285. * @return int The number of children nodes
  286. */
  287. public function numChildren()
  288. {
  289. $retval = 0;
  290. foreach ($this->children as $child) {
  291. if ($child->type == Node::OBJECT) {
  292. $retval++;
  293. } else {
  294. $retval += $child->numChildren();
  295. }
  296. }
  297. return $retval;
  298. }
  299. /**
  300. * Returns the actual path and the virtual paths for a node
  301. * both as clean arrays and base64 encoded strings
  302. *
  303. * @return array
  304. */
  305. public function getPaths()
  306. {
  307. $aPath = [];
  308. $aPathClean = [];
  309. foreach ($this->parents(true, true, false) as $parent) {
  310. $aPath[] = base64_encode($parent->realName);
  311. $aPathClean[] = $parent->realName;
  312. }
  313. $aPath = implode('.', array_reverse($aPath));
  314. $aPathClean = array_reverse($aPathClean);
  315. $vPath = [];
  316. $vPathClean = [];
  317. foreach ($this->parents(true, true, true) as $parent) {
  318. $vPath[] = base64_encode((string) $parent->name);
  319. $vPathClean[] = $parent->name;
  320. }
  321. $vPath = implode('.', array_reverse($vPath));
  322. $vPathClean = array_reverse($vPathClean);
  323. return [
  324. 'aPath' => $aPath,
  325. 'aPath_clean' => $aPathClean,
  326. 'vPath' => $vPath,
  327. 'vPath_clean' => $vPathClean,
  328. ];
  329. }
  330. /**
  331. * Returns the names of children of type $type present inside this container
  332. * This method is overridden by the PhpMyAdmin\Navigation\Nodes\NodeDatabase and PhpMyAdmin\Navigation\Nodes\NodeTable classes
  333. *
  334. * @param string $type The type of item we are looking for
  335. * ('tables', 'views', etc)
  336. * @param int $pos The offset of the list within the results
  337. * @param string $searchClause A string used to filter the results of the query
  338. *
  339. * @return array
  340. */
  341. public function getData($type, $pos, $searchClause = '')
  342. {
  343. $maxItems = $GLOBALS['cfg']['FirstLevelNavigationItems'];
  344. if (! $GLOBALS['cfg']['NavigationTreeEnableGrouping']
  345. || ! $GLOBALS['cfg']['ShowDatabasesNavigationAsTree']
  346. ) {
  347. if (isset($GLOBALS['cfg']['Server']['DisableIS'])
  348. && ! $GLOBALS['cfg']['Server']['DisableIS']
  349. ) {
  350. $query = "SELECT `SCHEMA_NAME` ";
  351. $query .= "FROM `INFORMATION_SCHEMA`.`SCHEMATA` ";
  352. $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
  353. $query .= "ORDER BY `SCHEMA_NAME` ";
  354. $query .= "LIMIT $pos, $maxItems";
  355. $retval = $GLOBALS['dbi']->fetchResult($query);
  356. return $retval;
  357. }
  358. if ($GLOBALS['dbs_to_test'] === false) {
  359. $retval = [];
  360. $query = "SHOW DATABASES ";
  361. $query .= $this->getWhereClause('Database', $searchClause);
  362. $handle = $GLOBALS['dbi']->tryQuery($query);
  363. if ($handle === false) {
  364. return $retval;
  365. }
  366. $count = 0;
  367. if (! $GLOBALS['dbi']->dataSeek($handle, $pos)) {
  368. return $retval;
  369. }
  370. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  371. if ($count < $maxItems) {
  372. $retval[] = $arr[0];
  373. $count++;
  374. } else {
  375. break;
  376. }
  377. }
  378. return $retval;
  379. }
  380. $retval = [];
  381. $count = 0;
  382. foreach ($this->getDatabasesToSearch($searchClause) as $db) {
  383. $query = "SHOW DATABASES LIKE '" . $db . "'";
  384. $handle = $GLOBALS['dbi']->tryQuery($query);
  385. if ($handle === false) {
  386. continue;
  387. }
  388. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  389. if ($this->isHideDb($arr[0])) {
  390. continue;
  391. }
  392. if (in_array($arr[0], $retval)) {
  393. continue;
  394. }
  395. if ($pos <= 0 && $count < $maxItems) {
  396. $retval[] = $arr[0];
  397. $count++;
  398. }
  399. $pos--;
  400. }
  401. }
  402. sort($retval);
  403. return $retval;
  404. }
  405. $dbSeparator = $GLOBALS['cfg']['NavigationTreeDbSeparator'];
  406. if (isset($GLOBALS['cfg']['Server']['DisableIS'])
  407. && ! $GLOBALS['cfg']['Server']['DisableIS']
  408. ) {
  409. $query = "SELECT `SCHEMA_NAME` ";
  410. $query .= "FROM `INFORMATION_SCHEMA`.`SCHEMATA`, ";
  411. $query .= "(";
  412. $query .= "SELECT DB_first_level ";
  413. $query .= "FROM ( ";
  414. $query .= "SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, ";
  415. $query .= "'" . $GLOBALS['dbi']->escapeString($dbSeparator) . "', 1) ";
  416. $query .= "DB_first_level ";
  417. $query .= "FROM INFORMATION_SCHEMA.SCHEMATA ";
  418. $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
  419. $query .= ") t ";
  420. $query .= "ORDER BY DB_first_level ASC ";
  421. $query .= "LIMIT $pos, $maxItems";
  422. $query .= ") t2 ";
  423. $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
  424. $query .= "AND 1 = LOCATE(CONCAT(DB_first_level, ";
  425. $query .= "'" . $GLOBALS['dbi']->escapeString($dbSeparator) . "'), ";
  426. $query .= "CONCAT(SCHEMA_NAME, ";
  427. $query .= "'" . $GLOBALS['dbi']->escapeString($dbSeparator) . "')) ";
  428. $query .= "ORDER BY SCHEMA_NAME ASC";
  429. $retval = $GLOBALS['dbi']->fetchResult($query);
  430. return $retval;
  431. }
  432. if ($GLOBALS['dbs_to_test'] === false) {
  433. $query = "SHOW DATABASES ";
  434. $query .= $this->getWhereClause('Database', $searchClause);
  435. $handle = $GLOBALS['dbi']->tryQuery($query);
  436. $prefixes = [];
  437. if ($handle !== false) {
  438. $prefixMap = [];
  439. $total = $pos + $maxItems;
  440. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  441. $prefix = strstr($arr[0], $dbSeparator, true);
  442. if ($prefix === false) {
  443. $prefix = $arr[0];
  444. }
  445. $prefixMap[$prefix] = 1;
  446. if (count($prefixMap) == $total) {
  447. break;
  448. }
  449. }
  450. $prefixes = array_slice(array_keys($prefixMap), (int) $pos);
  451. }
  452. $query = "SHOW DATABASES ";
  453. $query .= $this->getWhereClause('Database', $searchClause);
  454. $query .= "AND (";
  455. $subClauses = [];
  456. foreach ($prefixes as $prefix) {
  457. $subClauses[] = " LOCATE('"
  458. . $GLOBALS['dbi']->escapeString((string) $prefix) . $dbSeparator
  459. . "', "
  460. . "CONCAT(`Database`, '" . $dbSeparator . "')) = 1 ";
  461. }
  462. $query .= implode("OR", $subClauses) . ")";
  463. $retval = $GLOBALS['dbi']->fetchResult($query);
  464. return $retval;
  465. }
  466. $retval = [];
  467. $prefixMap = [];
  468. $total = $pos + $maxItems;
  469. foreach ($this->getDatabasesToSearch($searchClause) as $db) {
  470. $query = "SHOW DATABASES LIKE '" . $db . "'";
  471. $handle = $GLOBALS['dbi']->tryQuery($query);
  472. if ($handle === false) {
  473. continue;
  474. }
  475. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  476. if ($this->isHideDb($arr[0])) {
  477. continue;
  478. }
  479. $prefix = strstr($arr[0], $dbSeparator, true);
  480. if ($prefix === false) {
  481. $prefix = $arr[0];
  482. }
  483. $prefixMap[$prefix] = 1;
  484. if (count($prefixMap) == $total) {
  485. break 2;
  486. }
  487. }
  488. }
  489. $prefixes = array_slice(array_keys($prefixMap), $pos);
  490. foreach ($this->getDatabasesToSearch($searchClause) as $db) {
  491. $query = "SHOW DATABASES LIKE '" . $db . "'";
  492. $handle = $GLOBALS['dbi']->tryQuery($query);
  493. if ($handle === false) {
  494. continue;
  495. }
  496. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  497. if ($this->isHideDb($arr[0])) {
  498. continue;
  499. }
  500. if (in_array($arr[0], $retval)) {
  501. continue;
  502. }
  503. foreach ($prefixes as $prefix) {
  504. $startsWith = strpos(
  505. $arr[0] . $dbSeparator,
  506. $prefix . $dbSeparator
  507. ) === 0;
  508. if ($startsWith) {
  509. $retval[] = $arr[0];
  510. break;
  511. }
  512. }
  513. }
  514. }
  515. sort($retval);
  516. return $retval;
  517. }
  518. /**
  519. * Returns the number of children of type $type present inside this container
  520. * This method is overridden by the PhpMyAdmin\Navigation\Nodes\NodeDatabase and PhpMyAdmin\Navigation\Nodes\NodeTable classes
  521. *
  522. * @param string $type The type of item we are looking for
  523. * ('tables', 'views', etc)
  524. * @param string $searchClause A string used to filter the results of the query
  525. *
  526. * @return int
  527. */
  528. public function getPresence($type = '', $searchClause = '')
  529. {
  530. if (! $GLOBALS['cfg']['NavigationTreeEnableGrouping']
  531. || ! $GLOBALS['cfg']['ShowDatabasesNavigationAsTree']
  532. ) {
  533. if (isset($GLOBALS['cfg']['Server']['DisableIS'])
  534. && ! $GLOBALS['cfg']['Server']['DisableIS']
  535. ) {
  536. $query = "SELECT COUNT(*) ";
  537. $query .= "FROM INFORMATION_SCHEMA.SCHEMATA ";
  538. $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
  539. $retval = (int) $GLOBALS['dbi']->fetchValue($query);
  540. return $retval;
  541. }
  542. if ($GLOBALS['dbs_to_test'] === false) {
  543. $query = "SHOW DATABASES ";
  544. $query .= $this->getWhereClause('Database', $searchClause);
  545. $retval = $GLOBALS['dbi']->numRows(
  546. $GLOBALS['dbi']->tryQuery($query)
  547. );
  548. return $retval;
  549. }
  550. $retval = 0;
  551. foreach ($this->getDatabasesToSearch($searchClause) as $db) {
  552. $query = "SHOW DATABASES LIKE '" . $db . "'";
  553. $retval += $GLOBALS['dbi']->numRows(
  554. $GLOBALS['dbi']->tryQuery($query)
  555. );
  556. }
  557. return $retval;
  558. }
  559. $dbSeparator = $GLOBALS['cfg']['NavigationTreeDbSeparator'];
  560. if (! $GLOBALS['cfg']['Server']['DisableIS']) {
  561. $query = "SELECT COUNT(*) ";
  562. $query .= "FROM ( ";
  563. $query .= "SELECT DISTINCT SUBSTRING_INDEX(SCHEMA_NAME, ";
  564. $query .= "'$dbSeparator', 1) ";
  565. $query .= "DB_first_level ";
  566. $query .= "FROM INFORMATION_SCHEMA.SCHEMATA ";
  567. $query .= $this->getWhereClause('SCHEMA_NAME', $searchClause);
  568. $query .= ") t ";
  569. $retval = (int) $GLOBALS['dbi']->fetchValue($query);
  570. return $retval;
  571. }
  572. if ($GLOBALS['dbs_to_test'] !== false) {
  573. $prefixMap = [];
  574. foreach ($this->getDatabasesToSearch($searchClause) as $db) {
  575. $query = "SHOW DATABASES LIKE '" . $db . "'";
  576. $handle = $GLOBALS['dbi']->tryQuery($query);
  577. if ($handle === false) {
  578. continue;
  579. }
  580. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  581. if ($this->isHideDb($arr[0])) {
  582. continue;
  583. }
  584. $prefix = strstr($arr[0], $dbSeparator, true);
  585. if ($prefix === false) {
  586. $prefix = $arr[0];
  587. }
  588. $prefixMap[$prefix] = 1;
  589. }
  590. }
  591. $retval = count($prefixMap);
  592. return $retval;
  593. }
  594. $prefixMap = [];
  595. $query = "SHOW DATABASES ";
  596. $query .= $this->getWhereClause('Database', $searchClause);
  597. $handle = $GLOBALS['dbi']->tryQuery($query);
  598. if ($handle !== false) {
  599. while ($arr = $GLOBALS['dbi']->fetchArray($handle)) {
  600. $prefix = strstr($arr[0], $dbSeparator, true);
  601. if ($prefix === false) {
  602. $prefix = $arr[0];
  603. }
  604. $prefixMap[$prefix] = 1;
  605. }
  606. }
  607. $retval = count($prefixMap);
  608. return $retval;
  609. }
  610. /**
  611. * Detemines whether a given database should be hidden according to 'hide_db'
  612. *
  613. * @param string $db database name
  614. *
  615. * @return boolean whether to hide
  616. */
  617. private function isHideDb($db)
  618. {
  619. return ! empty($GLOBALS['cfg']['Server']['hide_db'])
  620. && preg_match('/' . $GLOBALS['cfg']['Server']['hide_db'] . '/', $db);
  621. }
  622. /**
  623. * Get the list of databases for 'SHOW DATABASES LIKE' queries.
  624. * If a search clause is set it gets the highest priority while only_db gets
  625. * the next priority. In case both are empty list of databases determined by
  626. * GRANTs are used
  627. *
  628. * @param string $searchClause search clause
  629. *
  630. * @return array array of databases
  631. */
  632. private function getDatabasesToSearch($searchClause)
  633. {
  634. $databases = [];
  635. if (! empty($searchClause)) {
  636. $databases = [
  637. "%" . $GLOBALS['dbi']->escapeString($searchClause) . "%",
  638. ];
  639. } elseif (! empty($GLOBALS['cfg']['Server']['only_db'])) {
  640. $databases = $GLOBALS['cfg']['Server']['only_db'];
  641. } elseif (! empty($GLOBALS['dbs_to_test'])) {
  642. $databases = $GLOBALS['dbs_to_test'];
  643. }
  644. sort($databases);
  645. return $databases;
  646. }
  647. /**
  648. * Returns the WHERE clause depending on the $searchClause parameter
  649. * and the hide_db directive
  650. *
  651. * @param string $columnName Column name of the column having database names
  652. * @param string $searchClause A string used to filter the results of the query
  653. *
  654. * @return string
  655. */
  656. private function getWhereClause($columnName, $searchClause = '')
  657. {
  658. $whereClause = "WHERE TRUE ";
  659. if (! empty($searchClause)) {
  660. $whereClause .= "AND " . Util::backquote($columnName)
  661. . " LIKE '%";
  662. $whereClause .= $GLOBALS['dbi']->escapeString($searchClause);
  663. $whereClause .= "%' ";
  664. }
  665. if (! empty($GLOBALS['cfg']['Server']['hide_db'])) {
  666. $whereClause .= "AND " . Util::backquote($columnName)
  667. . " NOT REGEXP '"
  668. . $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['hide_db'])
  669. . "' ";
  670. }
  671. if (! empty($GLOBALS['cfg']['Server']['only_db'])) {
  672. if (is_string($GLOBALS['cfg']['Server']['only_db'])) {
  673. $GLOBALS['cfg']['Server']['only_db'] = [
  674. $GLOBALS['cfg']['Server']['only_db'],
  675. ];
  676. }
  677. $whereClause .= "AND (";
  678. $subClauses = [];
  679. foreach ($GLOBALS['cfg']['Server']['only_db'] as $eachOnlyDb) {
  680. $subClauses[] = " " . Util::backquote($columnName)
  681. . " LIKE '"
  682. . $GLOBALS['dbi']->escapeString($eachOnlyDb) . "' ";
  683. }
  684. $whereClause .= implode("OR", $subClauses) . ") ";
  685. }
  686. return $whereClause;
  687. }
  688. /**
  689. * Returns HTML for control buttons displayed infront of a node
  690. *
  691. * @return String HTML for control buttons
  692. */
  693. public function getHtmlForControlButtons()
  694. {
  695. return '';
  696. }
  697. /**
  698. * Returns CSS classes for a node
  699. *
  700. * @param boolean $match Whether the node matched loaded tree
  701. *
  702. * @return String with html classes.
  703. */
  704. public function getCssClasses($match)
  705. {
  706. if (! $GLOBALS['cfg']['NavigationTreeEnableExpansion']
  707. ) {
  708. return '';
  709. }
  710. $result = ['expander'];
  711. if ($this->isGroup || $match) {
  712. $result[] = 'loaded';
  713. }
  714. if ($this->type == Node::CONTAINER) {
  715. $result[] = 'container';
  716. }
  717. return implode(' ', $result);
  718. }
  719. /**
  720. * Returns icon for the node
  721. *
  722. * @param boolean $match Whether the node matched loaded tree
  723. *
  724. * @return String with image name
  725. */
  726. public function getIcon($match)
  727. {
  728. if (! $GLOBALS['cfg']['NavigationTreeEnableExpansion']
  729. ) {
  730. return '';
  731. } elseif ($match) {
  732. $this->visible = true;
  733. return Util::getImage('b_minus');
  734. }
  735. return Util::getImage('b_plus', __('Expand/Collapse'));
  736. }
  737. /**
  738. * Gets the count of hidden elements for each database
  739. *
  740. * @return array|null array containing the count of hidden elements for each database
  741. */
  742. public function getNavigationHidingData()
  743. {
  744. $cfgRelation = $this->relation->getRelationsParam();
  745. if ($cfgRelation['navwork']) {
  746. $navTable = Util::backquote($cfgRelation['db'])
  747. . "." . Util::backquote(
  748. $cfgRelation['navigationhiding']
  749. );
  750. $sqlQuery = "SELECT `db_name`, COUNT(*) AS `count` FROM " . $navTable
  751. . " WHERE `username`='"
  752. . $GLOBALS['dbi']->escapeString(
  753. $GLOBALS['cfg']['Server']['user']
  754. ) . "'"
  755. . " GROUP BY `db_name`";
  756. $counts = $GLOBALS['dbi']->fetchResult(
  757. $sqlQuery,
  758. 'db_name',
  759. 'count',
  760. DatabaseInterface::CONNECT_CONTROL
  761. );
  762. return $counts;
  763. }
  764. return null;
  765. }
  766. }