Remove `@access` annotations
[phpmyadmin.git] / libraries / classes / Menu.php
blob745af6cb7afeca060c0abb0e92f51143854c9056
1 <?php
2 /**
3 * Generates and renders the top menu
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin;
10 use PhpMyAdmin\ConfigStorage\Relation;
11 use PhpMyAdmin\Query\Utilities;
12 use PhpMyAdmin\Utils\SessionCache;
14 use function __;
15 use function array_intersect_key;
16 use function count;
17 use function in_array;
18 use function mb_strpos;
19 use function mb_strstr;
20 use function mb_substr;
21 use function preg_replace;
23 /**
24 * Class for generating the top menu
26 class Menu
28 /**
29 * Database name
31 * @var string
33 private $db;
35 /** @var DatabaseInterface */
36 private $dbi;
38 /**
39 * Table name
41 * @var string
43 private $table;
45 /** @var Relation */
46 private $relation;
48 /** @var Template */
49 private $template;
51 /**
52 * Creates a new instance of Menu
54 * @param string $db Database name
55 * @param string $table Table name
57 public function __construct(DatabaseInterface $dbi, string $db, string $table)
59 $this->db = $db;
60 $this->dbi = $dbi;
61 $this->table = $table;
62 $this->relation = new Relation($dbi);
63 $this->template = new Template();
66 /**
67 * Returns the menu and the breadcrumbs as a string
69 public function getDisplay(): string
71 $retval = $this->getBreadcrumbs();
72 $retval .= $this->getMenu();
74 return $retval;
77 /**
78 * Returns the menu as HTML
80 * @return string HTML formatted menubar
82 private function getMenu(): string
84 $urlParams = [];
86 // The URL will not work if the table is defined without a database
87 if ($this->table !== '' && $this->db !== '') {
88 $tabs = $this->getTableTabs();
89 $urlParams['db'] = $this->db;
90 $urlParams['table'] = $this->table;
91 $level = 'table';
92 } elseif ($this->db !== '') {
93 $tabs = $this->getDbTabs();
94 $urlParams['db'] = $this->db;
95 $level = 'db';
96 } else {
97 $tabs = $this->getServerTabs();
98 $level = 'server';
101 $allowedTabs = $this->getAllowedTabs($level);
102 // Filter out any tabs that are not allowed
103 $tabs = array_intersect_key($tabs, $allowedTabs);
105 return $this->template->render('top_menu', [
106 'tabs' => $tabs,
107 'url_params' => $urlParams,
112 * Returns a list of allowed tabs for the current user for the given level
114 * @param string $level 'server', 'db' or 'table' level
116 * @return array list of allowed tabs
118 private function getAllowedTabs($level)
120 $cacheKey = 'menu-levels-' . $level;
121 if (SessionCache::has($cacheKey)) {
122 return SessionCache::get($cacheKey);
125 $allowedTabs = Util::getMenuTabList($level) ?? [];
126 $relationParameters = $this->relation->getRelationParameters();
127 if ($relationParameters->hasConfigurableMenusFeature()) {
128 $groupTable = Util::backquote($relationParameters->db)
129 . '.'
130 . Util::backquote($relationParameters->usergroups);
131 $userTable = Util::backquote($relationParameters->db)
132 . '.' . Util::backquote($relationParameters->users);
134 $sqlQuery = 'SELECT `tab` FROM ' . $groupTable
135 . " WHERE `allowed` = 'N'"
136 . " AND `tab` LIKE '" . $level . "%'"
137 . ' AND `usergroup` = (SELECT usergroup FROM '
138 . $userTable . " WHERE `username` = '"
139 . $this->dbi->escapeString($GLOBALS['cfg']['Server']['user']) . "')";
141 $result = $this->relation->queryAsControlUser($sqlQuery, false);
142 if ($result) {
143 while ($row = $this->dbi->fetchAssoc($result)) {
144 $tab = (string) $row['tab'];
145 $tabName = mb_substr(
146 $tab,
147 mb_strpos($tab, '_') + 1
149 unset($allowedTabs[$tabName]);
154 SessionCache::set($cacheKey, $allowedTabs);
156 return $allowedTabs;
160 * Returns the breadcrumbs as HTML
162 * @return string HTML formatted breadcrumbs
164 private function getBreadcrumbs(): string
166 global $cfg;
168 $server = [];
169 $database = [];
170 $table = [];
172 if (empty($cfg['Server']['host'])) {
173 $cfg['Server']['host'] = '';
176 $server['name'] = ! empty($cfg['Server']['verbose'])
177 ? $cfg['Server']['verbose'] : $cfg['Server']['host'];
178 $server['name'] .= empty($cfg['Server']['port'])
179 ? '' : ':' . $cfg['Server']['port'];
180 $server['url'] = Util::getUrlForOption($cfg['DefaultTabServer'], 'server');
182 if ($this->db !== '') {
183 $database['name'] = $this->db;
184 $database['url'] = Util::getUrlForOption($cfg['DefaultTabDatabase'], 'database');
185 if ($this->table !== '') {
186 $table['name'] = $this->table;
187 $table['url'] = Util::getUrlForOption($cfg['DefaultTabTable'], 'table');
188 $tableObj = $this->dbi->getTable($this->db, $this->table);
189 $table['is_view'] = $tableObj->isView();
190 $table['comment'] = '';
191 if (! $table['is_view']) {
192 $table['comment'] = $tableObj->getComment();
195 if (mb_strstr($table['comment'], '; InnoDB free')) {
196 $table['comment'] = preg_replace('@; InnoDB free:.*?$@', '', $table['comment']);
198 } else {
199 // no table selected, display database comment if present
200 $relationParameters = $this->relation->getRelationParameters();
202 // Get additional information about tables for tooltip is done
203 // in Util::getDbInfo() only once
204 if ($relationParameters->hasColumnCommentsFeature()) {
205 $database['comment'] = $this->relation->getDbComment($this->db);
210 return $this->template->render('menu/breadcrumbs', [
211 'server' => $server,
212 'database' => $database,
213 'table' => $table,
218 * Returns the table tabs as an array
220 * @return array Data for generating table tabs
222 private function getTableTabs(): array
224 global $route;
226 $isSystemSchema = Utilities::isSystemSchema($this->db);
227 $tableIsView = $this->dbi->getTable($this->db, $this->table)
228 ->isView();
229 $updatableView = false;
230 if ($tableIsView) {
231 $updatableView = $this->dbi->getTable($this->db, $this->table)
232 ->isUpdatableView();
235 $isSuperUser = $this->dbi->isSuperUser();
236 $isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
238 $tabs = [];
240 $tabs['browse']['icon'] = 'b_browse';
241 $tabs['browse']['text'] = __('Browse');
242 $tabs['browse']['route'] = '/sql';
243 $tabs['browse']['args']['pos'] = 0;
244 $tabs['browse']['active'] = $route === '/sql';
246 $tabs['structure']['icon'] = 'b_props';
247 $tabs['structure']['route'] = '/table/structure';
248 $tabs['structure']['text'] = __('Structure');
249 $tabs['structure']['active'] = in_array($route, [
250 '/table/relation',
251 '/table/structure',
254 $tabs['sql']['icon'] = 'b_sql';
255 $tabs['sql']['route'] = '/table/sql';
256 $tabs['sql']['text'] = __('SQL');
257 $tabs['sql']['active'] = $route === '/table/sql';
259 $tabs['search']['icon'] = 'b_search';
260 $tabs['search']['text'] = __('Search');
261 $tabs['search']['route'] = '/table/search';
262 $tabs['search']['active'] = in_array($route, [
263 '/table/find-replace',
264 '/table/search',
265 '/table/zoom-search',
268 if (! $isSystemSchema && (! $tableIsView || $updatableView)) {
269 $tabs['insert']['icon'] = 'b_insrow';
270 $tabs['insert']['route'] = '/table/change';
271 $tabs['insert']['text'] = __('Insert');
272 $tabs['insert']['active'] = $route === '/table/change';
275 $tabs['export']['icon'] = 'b_tblexport';
276 $tabs['export']['route'] = '/table/export';
277 $tabs['export']['args']['single_table'] = 'true';
278 $tabs['export']['text'] = __('Export');
279 $tabs['export']['active'] = $route === '/table/export';
282 * Don't display "Import" for views and information_schema
284 if (! $tableIsView && ! $isSystemSchema) {
285 $tabs['import']['icon'] = 'b_tblimport';
286 $tabs['import']['route'] = '/table/import';
287 $tabs['import']['text'] = __('Import');
288 $tabs['import']['active'] = $route === '/table/import';
291 if (($isSuperUser || $isCreateOrGrantUser) && ! $isSystemSchema) {
292 $tabs['privileges']['route'] = '/server/privileges';
293 $tabs['privileges']['args']['checkprivsdb'] = $this->db;
294 $tabs['privileges']['args']['checkprivstable'] = $this->table;
295 // stay on table view
296 $tabs['privileges']['args']['viewing_mode'] = 'table';
297 $tabs['privileges']['text'] = __('Privileges');
298 $tabs['privileges']['icon'] = 's_rights';
299 $tabs['privileges']['active'] = $route === '/server/privileges';
303 * Don't display "Operations" for views and information_schema
305 if (! $tableIsView && ! $isSystemSchema) {
306 $tabs['operation']['icon'] = 'b_tblops';
307 $tabs['operation']['route'] = '/table/operations';
308 $tabs['operation']['text'] = __('Operations');
309 $tabs['operation']['active'] = $route === '/table/operations';
313 * Views support a limited number of operations
315 if ($tableIsView && ! $isSystemSchema) {
316 $tabs['operation']['icon'] = 'b_tblops';
317 $tabs['operation']['route'] = '/view/operations';
318 $tabs['operation']['text'] = __('Operations');
319 $tabs['operation']['active'] = $route === '/view/operations';
322 if (Tracker::isActive() && ! $isSystemSchema) {
323 $tabs['tracking']['icon'] = 'eye';
324 $tabs['tracking']['text'] = __('Tracking');
325 $tabs['tracking']['route'] = '/table/tracking';
326 $tabs['tracking']['active'] = $route === '/table/tracking';
329 if (! $isSystemSchema && Util::currentUserHasPrivilege('TRIGGER', $this->db, $this->table) && ! $tableIsView) {
330 $tabs['triggers']['route'] = '/table/triggers';
331 $tabs['triggers']['text'] = __('Triggers');
332 $tabs['triggers']['icon'] = 'b_triggers';
333 $tabs['triggers']['active'] = $route === '/table/triggers';
336 return $tabs;
340 * Returns the db tabs as an array
342 * @return array Data for generating db tabs
344 private function getDbTabs(): array
346 global $route;
348 $isSystemSchema = Utilities::isSystemSchema($this->db);
349 $numTables = count($this->dbi->getTables($this->db));
350 $isSuperUser = $this->dbi->isSuperUser();
351 $isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
353 $relationParameters = $this->relation->getRelationParameters();
355 $tabs = [];
357 $tabs['structure']['route'] = '/database/structure';
358 $tabs['structure']['text'] = __('Structure');
359 $tabs['structure']['icon'] = 'b_props';
360 $tabs['structure']['active'] = $route === '/database/structure';
362 $tabs['sql']['route'] = '/database/sql';
363 $tabs['sql']['text'] = __('SQL');
364 $tabs['sql']['icon'] = 'b_sql';
365 $tabs['sql']['active'] = $route === '/database/sql';
367 $tabs['search']['text'] = __('Search');
368 $tabs['search']['icon'] = 'b_search';
369 $tabs['search']['route'] = '/database/search';
370 $tabs['search']['active'] = $route === '/database/search';
371 if ($numTables == 0) {
372 $tabs['search']['warning'] = __('Database seems to be empty!');
375 $tabs['query']['text'] = __('Query');
376 $tabs['query']['icon'] = 's_db';
377 $tabs['query']['route'] = '/database/multi-table-query';
378 $tabs['query']['active'] = $route === '/database/multi-table-query' || $route === '/database/qbe';
379 if ($numTables == 0) {
380 $tabs['query']['warning'] = __('Database seems to be empty!');
383 $tabs['export']['text'] = __('Export');
384 $tabs['export']['icon'] = 'b_export';
385 $tabs['export']['route'] = '/database/export';
386 $tabs['export']['active'] = $route === '/database/export';
387 if ($numTables == 0) {
388 $tabs['export']['warning'] = __('Database seems to be empty!');
391 if (! $isSystemSchema) {
392 $tabs['import']['route'] = '/database/import';
393 $tabs['import']['text'] = __('Import');
394 $tabs['import']['icon'] = 'b_import';
395 $tabs['import']['active'] = $route === '/database/import';
397 $tabs['operation']['route'] = '/database/operations';
398 $tabs['operation']['text'] = __('Operations');
399 $tabs['operation']['icon'] = 'b_tblops';
400 $tabs['operation']['active'] = $route === '/database/operations';
402 if ($isSuperUser || $isCreateOrGrantUser) {
403 $tabs['privileges']['route'] = '/server/privileges';
404 $tabs['privileges']['args']['checkprivsdb'] = $this->db;
405 // stay on database view
406 $tabs['privileges']['args']['viewing_mode'] = 'db';
407 $tabs['privileges']['text'] = __('Privileges');
408 $tabs['privileges']['icon'] = 's_rights';
409 $tabs['privileges']['active'] = $route === '/server/privileges';
412 $tabs['routines']['route'] = '/database/routines';
413 $tabs['routines']['text'] = __('Routines');
414 $tabs['routines']['icon'] = 'b_routines';
415 $tabs['routines']['active'] = $route === '/database/routines';
417 if (Util::currentUserHasPrivilege('EVENT', $this->db)) {
418 $tabs['events']['route'] = '/database/events';
419 $tabs['events']['text'] = __('Events');
420 $tabs['events']['icon'] = 'b_events';
421 $tabs['events']['active'] = $route === '/database/events';
424 if (Util::currentUserHasPrivilege('TRIGGER', $this->db)) {
425 $tabs['triggers']['route'] = '/database/triggers';
426 $tabs['triggers']['text'] = __('Triggers');
427 $tabs['triggers']['icon'] = 'b_triggers';
428 $tabs['triggers']['active'] = $route === '/database/triggers';
432 if (Tracker::isActive() && ! $isSystemSchema) {
433 $tabs['tracking']['text'] = __('Tracking');
434 $tabs['tracking']['icon'] = 'eye';
435 $tabs['tracking']['route'] = '/database/tracking';
436 $tabs['tracking']['active'] = $route === '/database/tracking';
439 if (! $isSystemSchema) {
440 $tabs['designer']['text'] = __('Designer');
441 $tabs['designer']['icon'] = 'b_relations';
442 $tabs['designer']['route'] = '/database/designer';
443 $tabs['designer']['active'] = $route === '/database/designer';
446 if (! $isSystemSchema && $relationParameters->hasCentralColumnsFeature()) {
447 $tabs['central_columns']['text'] = __('Central columns');
448 $tabs['central_columns']['icon'] = 'centralColumns';
449 $tabs['central_columns']['route'] = '/database/central-columns';
450 $tabs['central_columns']['active'] = $route === '/database/central-columns';
453 return $tabs;
457 * Returns the server tabs as an array
459 * @return array Data for generating server tabs
461 private function getServerTabs(): array
463 global $route;
465 $isSuperUser = $this->dbi->isSuperUser();
466 $isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
467 if (SessionCache::has('binary_logs')) {
468 $binaryLogs = SessionCache::get('binary_logs');
469 } else {
470 $binaryLogs = $this->dbi->fetchResult(
471 'SHOW MASTER LOGS',
472 'Log_name',
473 null,
474 DatabaseInterface::CONNECT_USER,
475 DatabaseInterface::QUERY_STORE
477 SessionCache::set('binary_logs', $binaryLogs);
480 $tabs = [];
482 $tabs['databases']['icon'] = 's_db';
483 $tabs['databases']['route'] = '/server/databases';
484 $tabs['databases']['text'] = __('Databases');
485 $tabs['databases']['active'] = $route === '/server/databases';
487 $tabs['sql']['icon'] = 'b_sql';
488 $tabs['sql']['route'] = '/server/sql';
489 $tabs['sql']['text'] = __('SQL');
490 $tabs['sql']['active'] = $route === '/server/sql';
492 $tabs['status']['icon'] = 's_status';
493 $tabs['status']['route'] = '/server/status';
494 $tabs['status']['text'] = __('Status');
495 $tabs['status']['active'] = in_array($route, [
496 '/server/status',
497 '/server/status/advisor',
498 '/server/status/monitor',
499 '/server/status/processes',
500 '/server/status/queries',
501 '/server/status/variables',
504 if ($isSuperUser || $isCreateOrGrantUser) {
505 $tabs['rights']['icon'] = 's_rights';
506 $tabs['rights']['route'] = '/server/privileges';
507 $tabs['rights']['text'] = __('User accounts');
508 $tabs['rights']['active'] = in_array($route, [
509 '/server/privileges',
510 '/server/user-groups',
512 $tabs['rights']['args']['viewing_mode'] = 'server';
515 $tabs['export']['icon'] = 'b_export';
516 $tabs['export']['route'] = '/server/export';
517 $tabs['export']['text'] = __('Export');
518 $tabs['export']['active'] = $route === '/server/export';
520 $tabs['import']['icon'] = 'b_import';
521 $tabs['import']['route'] = '/server/import';
522 $tabs['import']['text'] = __('Import');
523 $tabs['import']['active'] = $route === '/server/import';
525 $tabs['settings']['icon'] = 'b_tblops';
526 $tabs['settings']['route'] = '/preferences/manage';
527 $tabs['settings']['text'] = __('Settings');
528 $tabs['settings']['active'] = in_array($route, [
529 '/preferences/export',
530 '/preferences/features',
531 '/preferences/import',
532 '/preferences/main-panel',
533 '/preferences/manage',
534 '/preferences/navigation',
535 '/preferences/sql',
536 '/preferences/two-factor',
539 if (! empty($binaryLogs)) {
540 $tabs['binlog']['icon'] = 's_tbl';
541 $tabs['binlog']['route'] = '/server/binlog';
542 $tabs['binlog']['text'] = __('Binary log');
543 $tabs['binlog']['active'] = $route === '/server/binlog';
546 if ($isSuperUser) {
547 $tabs['replication']['icon'] = 's_replication';
548 $tabs['replication']['route'] = '/server/replication';
549 $tabs['replication']['text'] = __('Replication');
550 $tabs['replication']['active'] = $route === '/server/replication';
553 $tabs['vars']['icon'] = 's_vars';
554 $tabs['vars']['route'] = '/server/variables';
555 $tabs['vars']['text'] = __('Variables');
556 $tabs['vars']['active'] = $route === '/server/variables';
558 $tabs['charset']['icon'] = 's_asci';
559 $tabs['charset']['route'] = '/server/collations';
560 $tabs['charset']['text'] = __('Charsets');
561 $tabs['charset']['active'] = $route === '/server/collations';
563 $tabs['engine']['icon'] = 'b_engine';
564 $tabs['engine']['route'] = '/server/engines';
565 $tabs['engine']['text'] = __('Engines');
566 $tabs['engine']['active'] = $route === '/server/engines';
568 $tabs['plugins']['icon'] = 'b_plugin';
569 $tabs['plugins']['route'] = '/server/plugins';
570 $tabs['plugins']['text'] = __('Plugins');
571 $tabs['plugins']['active'] = $route === '/server/plugins';
573 return $tabs;
577 * Set current table
579 * @param string $table Current table
581 * @return Menu
583 public function setTable(string $table)
585 $this->table = $table;
587 return $this;