Menu.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. <?php
  2. /**
  3. * Generates and renders the top menu
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin;
  7. use PhpMyAdmin\Query\Utilities;
  8. use PhpMyAdmin\Utils\SessionCache;
  9. use function array_key_exists;
  10. use function count;
  11. use function in_array;
  12. use function mb_strpos;
  13. use function mb_strstr;
  14. use function mb_substr;
  15. use function md5;
  16. use function preg_replace;
  17. use function strlen;
  18. use function substr;
  19. /**
  20. * Class for generating the top menu
  21. */
  22. class Menu
  23. {
  24. /**
  25. * Database name
  26. *
  27. * @access private
  28. * @var string
  29. */
  30. private $db;
  31. /**
  32. * Table name
  33. *
  34. * @access private
  35. * @var string
  36. */
  37. private $table;
  38. /** @var Relation */
  39. private $relation;
  40. /** @var Template */
  41. private $template;
  42. /**
  43. * Creates a new instance of Menu
  44. *
  45. * @param string $db Database name
  46. * @param string $table Table name
  47. */
  48. public function __construct($db, $table)
  49. {
  50. global $dbi;
  51. $this->db = $db;
  52. $this->table = $table;
  53. $this->relation = new Relation($dbi);
  54. $this->template = new Template();
  55. }
  56. /**
  57. * Returns the menu and the breadcrumbs as a string
  58. *
  59. * @return string
  60. */
  61. public function getDisplay()
  62. {
  63. $retval = $this->getBreadcrumbs();
  64. $retval .= $this->getMenu();
  65. return $retval;
  66. }
  67. /**
  68. * Returns hash for the menu and the breadcrumbs
  69. *
  70. * @return string
  71. */
  72. public function getHash()
  73. {
  74. return substr(
  75. md5($this->getMenu() . $this->getBreadcrumbs()),
  76. 0,
  77. 8
  78. );
  79. }
  80. /**
  81. * Returns the menu as HTML
  82. *
  83. * @return string HTML formatted menubar
  84. */
  85. private function getMenu(): string
  86. {
  87. $url_params = [];
  88. $hasDbArg = strlen($this->db) > 0;
  89. // The URL will not work if the table is defined without a database
  90. if (strlen((string) $this->table) > 0 && $hasDbArg) {
  91. $tabs = $this->getTableTabs();
  92. $url_params['db'] = $this->db;
  93. $url_params['table'] = $this->table;
  94. $level = 'table';
  95. } elseif ($hasDbArg) {
  96. $tabs = $this->getDbTabs();
  97. $url_params['db'] = $this->db;
  98. $level = 'db';
  99. } else {
  100. $tabs = $this->getServerTabs();
  101. $level = 'server';
  102. }
  103. $allowedTabs = $this->getAllowedTabs($level);
  104. foreach ($tabs as $key => $value) {
  105. if (array_key_exists($key, $allowedTabs)) {
  106. continue;
  107. }
  108. unset($tabs[$key]);
  109. }
  110. return $this->template->render('top_menu', [
  111. 'tabs' => $tabs,
  112. 'url_params' => $url_params,
  113. ]);
  114. }
  115. /**
  116. * Returns a list of allowed tabs for the current user for the given level
  117. *
  118. * @param string $level 'server', 'db' or 'table' level
  119. *
  120. * @return array list of allowed tabs
  121. */
  122. private function getAllowedTabs($level)
  123. {
  124. /** @var DatabaseInterface $dbi */
  125. global $dbi;
  126. $cache_key = 'menu-levels-' . $level;
  127. if (SessionCache::has($cache_key)) {
  128. return SessionCache::get($cache_key);
  129. }
  130. $allowedTabs = Util::getMenuTabList($level);
  131. $cfgRelation = $this->relation->getRelationsParam();
  132. if ($cfgRelation['menuswork']) {
  133. $groupTable = Util::backquote($cfgRelation['db'])
  134. . '.'
  135. . Util::backquote($cfgRelation['usergroups']);
  136. $userTable = Util::backquote($cfgRelation['db'])
  137. . '.' . Util::backquote($cfgRelation['users']);
  138. $sql_query = 'SELECT `tab` FROM ' . $groupTable
  139. . " WHERE `allowed` = 'N'"
  140. . " AND `tab` LIKE '" . $level . "%'"
  141. . ' AND `usergroup` = (SELECT usergroup FROM '
  142. . $userTable . " WHERE `username` = '"
  143. . $dbi->escapeString($GLOBALS['cfg']['Server']['user']) . "')";
  144. $result = $this->relation->queryAsControlUser($sql_query, false);
  145. if ($result) {
  146. while ($row = $dbi->fetchAssoc($result)) {
  147. $tabName = mb_substr(
  148. $row['tab'],
  149. mb_strpos($row['tab'], '_') + 1
  150. );
  151. unset($allowedTabs[$tabName]);
  152. }
  153. }
  154. }
  155. SessionCache::set($cache_key, $allowedTabs);
  156. return $allowedTabs;
  157. }
  158. /**
  159. * Returns the breadcrumbs as HTML
  160. *
  161. * @return string HTML formatted breadcrumbs
  162. */
  163. private function getBreadcrumbs(): string
  164. {
  165. global $cfg, $dbi;
  166. $server = [];
  167. $database = [];
  168. $table = [];
  169. if (empty($cfg['Server']['host'])) {
  170. $cfg['Server']['host'] = '';
  171. }
  172. $server['name'] = ! empty($cfg['Server']['verbose'])
  173. ? $cfg['Server']['verbose'] : $cfg['Server']['host'];
  174. $server['name'] .= empty($cfg['Server']['port'])
  175. ? '' : ':' . $cfg['Server']['port'];
  176. $server['url'] = Util::getUrlForOption(
  177. $cfg['DefaultTabServer'],
  178. 'server'
  179. ) ?? '/';
  180. if (strlen($this->db) > 0) {
  181. $database['name'] = $this->db;
  182. $database['url'] = Util::getUrlForOption(
  183. $cfg['DefaultTabDatabase'],
  184. 'database'
  185. ) ?? '/';
  186. if (strlen((string) $this->table) > 0) {
  187. $table['name'] = $this->table;
  188. $table['url'] = Util::getUrlForOption(
  189. $cfg['DefaultTabTable'],
  190. 'table'
  191. ) ?? '/';
  192. /** @var Table $tableObj */
  193. $tableObj = $dbi->getTable($this->db, $this->table);
  194. $table['is_view'] = $tableObj->isView();
  195. $table['comment'] = '';
  196. if (! $table['is_view']) {
  197. $table['comment'] = $tableObj->getComment();
  198. }
  199. if (mb_strstr($table['comment'], '; InnoDB free')) {
  200. $table['comment'] = preg_replace(
  201. '@; InnoDB free:.*?$@',
  202. '',
  203. $table['comment']
  204. );
  205. }
  206. } else {
  207. // no table selected, display database comment if present
  208. $cfgRelation = $this->relation->getRelationsParam();
  209. // Get additional information about tables for tooltip is done
  210. // in Util::getDbInfo() only once
  211. if ($cfgRelation['commwork']) {
  212. $database['comment'] = $this->relation->getDbComment($this->db);
  213. }
  214. }
  215. }
  216. return $this->template->render('menu/breadcrumbs', [
  217. 'server' => $server,
  218. 'database' => $database,
  219. 'table' => $table,
  220. ]);
  221. }
  222. /**
  223. * Returns the table tabs as an array
  224. *
  225. * @return array Data for generating table tabs
  226. */
  227. private function getTableTabs()
  228. {
  229. /** @var DatabaseInterface $dbi */
  230. global $route, $dbi;
  231. $isSystemSchema = Utilities::isSystemSchema($this->db);
  232. $tbl_is_view = $dbi->getTable($this->db, $this->table)
  233. ->isView();
  234. $updatable_view = false;
  235. if ($tbl_is_view) {
  236. $updatable_view = $dbi->getTable($this->db, $this->table)
  237. ->isUpdatableView();
  238. }
  239. $is_superuser = $dbi->isSuperUser();
  240. $isCreateOrGrantUser = $dbi->isGrantUser() || $dbi->isCreateUser();
  241. $tabs = [];
  242. $tabs['browse']['icon'] = 'b_browse';
  243. $tabs['browse']['text'] = __('Browse');
  244. $tabs['browse']['route'] = '/sql';
  245. $tabs['browse']['args']['pos'] = 0;
  246. $tabs['browse']['active'] = $route === '/sql';
  247. $tabs['structure']['icon'] = 'b_props';
  248. $tabs['structure']['route'] = '/table/structure';
  249. $tabs['structure']['text'] = __('Structure');
  250. $tabs['structure']['active'] = in_array($route, [
  251. '/table/relation',
  252. '/table/structure',
  253. ]);
  254. $tabs['sql']['icon'] = 'b_sql';
  255. $tabs['sql']['route'] = '/table/sql';
  256. $tabs['sql']['text'] = __('SQL');
  257. $tabs['sql']['active'] = $route === '/table/sql';
  258. $tabs['search']['icon'] = 'b_search';
  259. $tabs['search']['text'] = __('Search');
  260. $tabs['search']['route'] = '/table/search';
  261. $tabs['search']['active'] = in_array($route, [
  262. '/table/find-replace',
  263. '/table/search',
  264. '/table/zoom-search',
  265. ]);
  266. if (! $isSystemSchema && (! $tbl_is_view || $updatable_view)) {
  267. $tabs['insert']['icon'] = 'b_insrow';
  268. $tabs['insert']['route'] = '/table/change';
  269. $tabs['insert']['text'] = __('Insert');
  270. $tabs['insert']['active'] = $route === '/table/change';
  271. }
  272. $tabs['export']['icon'] = 'b_tblexport';
  273. $tabs['export']['route'] = '/table/export';
  274. $tabs['export']['args']['single_table'] = 'true';
  275. $tabs['export']['text'] = __('Export');
  276. $tabs['export']['active'] = $route === '/table/export';
  277. /**
  278. * Don't display "Import" for views and information_schema
  279. */
  280. if (! $tbl_is_view && ! $isSystemSchema) {
  281. $tabs['import']['icon'] = 'b_tblimport';
  282. $tabs['import']['route'] = '/table/import';
  283. $tabs['import']['text'] = __('Import');
  284. $tabs['import']['active'] = $route === '/table/import';
  285. }
  286. if (($is_superuser || $isCreateOrGrantUser)
  287. && ! $isSystemSchema
  288. ) {
  289. $tabs['privileges']['route'] = '/server/privileges';
  290. $tabs['privileges']['args']['checkprivsdb'] = $this->db;
  291. $tabs['privileges']['args']['checkprivstable'] = $this->table;
  292. // stay on table view
  293. $tabs['privileges']['args']['viewing_mode'] = 'table';
  294. $tabs['privileges']['text'] = __('Privileges');
  295. $tabs['privileges']['icon'] = 's_rights';
  296. $tabs['privileges']['active'] = $route === '/server/privileges';
  297. }
  298. /**
  299. * Don't display "Operations" for views and information_schema
  300. */
  301. if (! $tbl_is_view && ! $isSystemSchema) {
  302. $tabs['operation']['icon'] = 'b_tblops';
  303. $tabs['operation']['route'] = '/table/operations';
  304. $tabs['operation']['text'] = __('Operations');
  305. $tabs['operation']['active'] = $route === '/table/operations';
  306. }
  307. /**
  308. * Views support a limited number of operations
  309. */
  310. if ($tbl_is_view && ! $isSystemSchema) {
  311. $tabs['operation']['icon'] = 'b_tblops';
  312. $tabs['operation']['route'] = '/view/operations';
  313. $tabs['operation']['text'] = __('Operations');
  314. $tabs['operation']['active'] = $route === '/view/operations';
  315. }
  316. if (Tracker::isActive() && ! $isSystemSchema) {
  317. $tabs['tracking']['icon'] = 'eye';
  318. $tabs['tracking']['text'] = __('Tracking');
  319. $tabs['tracking']['route'] = '/table/tracking';
  320. $tabs['tracking']['active'] = $route === '/table/tracking';
  321. }
  322. if (! $isSystemSchema
  323. && Util::currentUserHasPrivilege(
  324. 'TRIGGER',
  325. $this->db,
  326. $this->table
  327. )
  328. && ! $tbl_is_view
  329. ) {
  330. $tabs['triggers']['route'] = '/table/triggers';
  331. $tabs['triggers']['text'] = __('Triggers');
  332. $tabs['triggers']['icon'] = 'b_triggers';
  333. $tabs['triggers']['active'] = $route === '/table/triggers';
  334. }
  335. return $tabs;
  336. }
  337. /**
  338. * Returns the db tabs as an array
  339. *
  340. * @return array Data for generating db tabs
  341. */
  342. private function getDbTabs()
  343. {
  344. /** @var DatabaseInterface $dbi */
  345. global $route, $dbi;
  346. $isSystemSchema = Utilities::isSystemSchema($this->db);
  347. $num_tables = count($dbi->getTables($this->db));
  348. $is_superuser = $dbi->isSuperUser();
  349. $isCreateOrGrantUser = $dbi->isGrantUser() || $dbi->isCreateUser();
  350. /**
  351. * Gets the relation settings
  352. */
  353. $cfgRelation = $this->relation->getRelationsParam();
  354. $tabs = [];
  355. $tabs['structure']['route'] = '/database/structure';
  356. $tabs['structure']['text'] = __('Structure');
  357. $tabs['structure']['icon'] = 'b_props';
  358. $tabs['structure']['active'] = $route === '/database/structure';
  359. $tabs['sql']['route'] = '/database/sql';
  360. $tabs['sql']['text'] = __('SQL');
  361. $tabs['sql']['icon'] = 'b_sql';
  362. $tabs['sql']['active'] = $route === '/database/sql';
  363. $tabs['search']['text'] = __('Search');
  364. $tabs['search']['icon'] = 'b_search';
  365. $tabs['search']['route'] = '/database/search';
  366. $tabs['search']['active'] = $route === '/database/search';
  367. if ($num_tables == 0) {
  368. $tabs['search']['warning'] = __('Database seems to be empty!');
  369. }
  370. $tabs['query']['text'] = __('Query');
  371. $tabs['query']['icon'] = 's_db';
  372. $tabs['query']['route'] = '/database/multi-table-query';
  373. $tabs['query']['active'] = $route === '/database/multi-table-query' || $route === '/database/qbe';
  374. if ($num_tables == 0) {
  375. $tabs['query']['warning'] = __('Database seems to be empty!');
  376. }
  377. $tabs['export']['text'] = __('Export');
  378. $tabs['export']['icon'] = 'b_export';
  379. $tabs['export']['route'] = '/database/export';
  380. $tabs['export']['active'] = $route === '/database/export';
  381. if ($num_tables == 0) {
  382. $tabs['export']['warning'] = __('Database seems to be empty!');
  383. }
  384. if (! $isSystemSchema) {
  385. $tabs['import']['route'] = '/database/import';
  386. $tabs['import']['text'] = __('Import');
  387. $tabs['import']['icon'] = 'b_import';
  388. $tabs['import']['active'] = $route === '/database/import';
  389. $tabs['operation']['route'] = '/database/operations';
  390. $tabs['operation']['text'] = __('Operations');
  391. $tabs['operation']['icon'] = 'b_tblops';
  392. $tabs['operation']['active'] = $route === '/database/operations';
  393. if ($is_superuser || $isCreateOrGrantUser) {
  394. $tabs['privileges']['route'] = '/server/privileges';
  395. $tabs['privileges']['args']['checkprivsdb'] = $this->db;
  396. // stay on database view
  397. $tabs['privileges']['args']['viewing_mode'] = 'db';
  398. $tabs['privileges']['text'] = __('Privileges');
  399. $tabs['privileges']['icon'] = 's_rights';
  400. $tabs['privileges']['active'] = $route === '/server/privileges';
  401. }
  402. $tabs['routines']['route'] = '/database/routines';
  403. $tabs['routines']['text'] = __('Routines');
  404. $tabs['routines']['icon'] = 'b_routines';
  405. $tabs['routines']['active'] = $route === '/database/routines';
  406. if (Util::currentUserHasPrivilege('EVENT', $this->db)) {
  407. $tabs['events']['route'] = '/database/events';
  408. $tabs['events']['text'] = __('Events');
  409. $tabs['events']['icon'] = 'b_events';
  410. $tabs['events']['active'] = $route === '/database/events';
  411. }
  412. if (Util::currentUserHasPrivilege('TRIGGER', $this->db)) {
  413. $tabs['triggers']['route'] = '/database/triggers';
  414. $tabs['triggers']['text'] = __('Triggers');
  415. $tabs['triggers']['icon'] = 'b_triggers';
  416. $tabs['triggers']['active'] = $route === '/database/triggers';
  417. }
  418. }
  419. if (Tracker::isActive() && ! $isSystemSchema) {
  420. $tabs['tracking']['text'] = __('Tracking');
  421. $tabs['tracking']['icon'] = 'eye';
  422. $tabs['tracking']['route'] = '/database/tracking';
  423. $tabs['tracking']['active'] = $route === '/database/tracking';
  424. }
  425. if (! $isSystemSchema) {
  426. $tabs['designer']['text'] = __('Designer');
  427. $tabs['designer']['icon'] = 'b_relations';
  428. $tabs['designer']['route'] = '/database/designer';
  429. $tabs['designer']['active'] = $route === '/database/designer';
  430. }
  431. if (! $isSystemSchema
  432. && $cfgRelation['centralcolumnswork']
  433. ) {
  434. $tabs['central_columns']['text'] = __('Central columns');
  435. $tabs['central_columns']['icon'] = 'centralColumns';
  436. $tabs['central_columns']['route'] = '/database/central-columns';
  437. $tabs['central_columns']['active'] = $route === '/database/central-columns';
  438. }
  439. return $tabs;
  440. }
  441. /**
  442. * Returns the server tabs as an array
  443. *
  444. * @return array Data for generating server tabs
  445. */
  446. private function getServerTabs()
  447. {
  448. /** @var DatabaseInterface $dbi */
  449. global $route, $dbi;
  450. $is_superuser = $dbi->isSuperUser();
  451. $isCreateOrGrantUser = $dbi->isGrantUser() || $dbi->isCreateUser();
  452. if (SessionCache::has('binary_logs')) {
  453. $binary_logs = SessionCache::get('binary_logs');
  454. } else {
  455. $binary_logs = $dbi->fetchResult(
  456. 'SHOW MASTER LOGS',
  457. 'Log_name',
  458. null,
  459. DatabaseInterface::CONNECT_USER,
  460. DatabaseInterface::QUERY_STORE
  461. );
  462. SessionCache::set('binary_logs', $binary_logs);
  463. }
  464. $tabs = [];
  465. $tabs['databases']['icon'] = 's_db';
  466. $tabs['databases']['route'] = '/server/databases';
  467. $tabs['databases']['text'] = __('Databases');
  468. $tabs['databases']['active'] = $route === '/server/databases';
  469. $tabs['sql']['icon'] = 'b_sql';
  470. $tabs['sql']['route'] = '/server/sql';
  471. $tabs['sql']['text'] = __('SQL');
  472. $tabs['sql']['active'] = $route === '/server/sql';
  473. $tabs['status']['icon'] = 's_status';
  474. $tabs['status']['route'] = '/server/status';
  475. $tabs['status']['text'] = __('Status');
  476. $tabs['status']['active'] = in_array($route, [
  477. '/server/status',
  478. '/server/status/advisor',
  479. '/server/status/monitor',
  480. '/server/status/processes',
  481. '/server/status/queries',
  482. '/server/status/variables',
  483. ]);
  484. if ($is_superuser || $isCreateOrGrantUser) {
  485. $tabs['rights']['icon'] = 's_rights';
  486. $tabs['rights']['route'] = '/server/privileges';
  487. $tabs['rights']['text'] = __('User accounts');
  488. $tabs['rights']['active'] = in_array($route, [
  489. '/server/privileges',
  490. '/server/user-groups',
  491. ]);
  492. $tabs['rights']['args']['viewing_mode'] = 'server';
  493. }
  494. $tabs['export']['icon'] = 'b_export';
  495. $tabs['export']['route'] = '/server/export';
  496. $tabs['export']['text'] = __('Export');
  497. $tabs['export']['active'] = $route === '/server/export';
  498. $tabs['import']['icon'] = 'b_import';
  499. $tabs['import']['route'] = '/server/import';
  500. $tabs['import']['text'] = __('Import');
  501. $tabs['import']['active'] = $route === '/server/import';
  502. $tabs['settings']['icon'] = 'b_tblops';
  503. $tabs['settings']['route'] = '/preferences/manage';
  504. $tabs['settings']['text'] = __('Settings');
  505. $tabs['settings']['active'] = in_array($route, [
  506. '/preferences/export',
  507. '/preferences/features',
  508. '/preferences/import',
  509. '/preferences/main-panel',
  510. '/preferences/manage',
  511. '/preferences/navigation',
  512. '/preferences/sql',
  513. '/preferences/two-factor',
  514. ]);
  515. if (! empty($binary_logs)) {
  516. $tabs['binlog']['icon'] = 's_tbl';
  517. $tabs['binlog']['route'] = '/server/binlog';
  518. $tabs['binlog']['text'] = __('Binary log');
  519. $tabs['binlog']['active'] = $route === '/server/binlog';
  520. }
  521. if ($is_superuser) {
  522. $tabs['replication']['icon'] = 's_replication';
  523. $tabs['replication']['route'] = '/server/replication';
  524. $tabs['replication']['text'] = __('Replication');
  525. $tabs['replication']['active'] = $route === '/server/replication';
  526. }
  527. $tabs['vars']['icon'] = 's_vars';
  528. $tabs['vars']['route'] = '/server/variables';
  529. $tabs['vars']['text'] = __('Variables');
  530. $tabs['vars']['active'] = $route === '/server/variables';
  531. $tabs['charset']['icon'] = 's_asci';
  532. $tabs['charset']['route'] = '/server/collations';
  533. $tabs['charset']['text'] = __('Charsets');
  534. $tabs['charset']['active'] = $route === '/server/collations';
  535. $tabs['engine']['icon'] = 'b_engine';
  536. $tabs['engine']['route'] = '/server/engines';
  537. $tabs['engine']['text'] = __('Engines');
  538. $tabs['engine']['active'] = $route === '/server/engines';
  539. $tabs['plugins']['icon'] = 'b_plugin';
  540. $tabs['plugins']['route'] = '/server/plugins';
  541. $tabs['plugins']['text'] = __('Plugins');
  542. $tabs['plugins']['active'] = $route === '/server/plugins';
  543. return $tabs;
  544. }
  545. /**
  546. * Set current table
  547. *
  548. * @param string $table Current table
  549. *
  550. * @return Menu
  551. */
  552. public function setTable($table)
  553. {
  554. $this->table = $table;
  555. return $this;
  556. }
  557. }