Translated using Weblate (Portuguese)
[phpmyadmin.git] / src / Menu.php
blob70c191ae93159e91c290732eb76d91d3537cab51
1 <?php
3 declare(strict_types=1);
5 namespace PhpMyAdmin;
7 use PhpMyAdmin\ConfigStorage\Relation;
8 use PhpMyAdmin\ConfigStorage\UserGroupLevel;
9 use PhpMyAdmin\Dbal\ConnectionType;
10 use PhpMyAdmin\Query\Utilities;
11 use PhpMyAdmin\Routing\Routing;
12 use PhpMyAdmin\Tracking\Tracker;
13 use PhpMyAdmin\Utils\SessionCache;
15 use function __;
16 use function array_intersect_key;
17 use function count;
18 use function in_array;
19 use function mb_strpos;
20 use function mb_substr;
21 use function preg_replace;
22 use function str_contains;
24 /**
25 * Generates and renders the top menu
27 class Menu
29 /**
30 * Creates a new instance of Menu
32 * @param string $db Database name
33 * @param string $table Table name
35 public function __construct(
36 private readonly DatabaseInterface $dbi,
37 private readonly Template $template,
38 private readonly Config $config,
39 private readonly Relation $relation,
40 private string $db,
41 private string $table,
42 ) {
45 /**
46 * Returns the menu and the breadcrumbs as a string
48 public function getDisplay(): string
50 $breadcrumbs = $this->getBreadcrumbs();
51 $menu = $this->getMenu();
53 return $this->template->render('menu/main', [
54 'server' => $breadcrumbs['server'],
55 'database' => $breadcrumbs['database'],
56 'table' => $breadcrumbs['table'],
57 'tabs' => $menu['tabs'],
58 'url_params' => $menu['url_params'],
59 ]);
62 /** @return array{tabs: mixed[], url_params: mixed[]} */
63 private function getMenu(): array
65 $urlParams = [];
67 // The URL will not work if the table is defined without a database
68 if ($this->table !== '' && $this->db !== '') {
69 $tabs = $this->getTableTabs();
70 $urlParams['db'] = $this->db;
71 $urlParams['table'] = $this->table;
72 $level = UserGroupLevel::Table;
73 } elseif ($this->db !== '') {
74 $tabs = $this->getDbTabs();
75 $urlParams['db'] = $this->db;
76 $level = UserGroupLevel::Database;
77 } else {
78 $tabs = $this->getServerTabs();
79 $level = UserGroupLevel::Server;
82 $allowedTabs = $this->getAllowedTabs($level);
83 // Filter out any tabs that are not allowed
84 $tabs = array_intersect_key($tabs, $allowedTabs);
86 return ['tabs' => $tabs, 'url_params' => $urlParams];
89 /**
90 * Returns a list of allowed tabs for the current user for the given level
92 * @return mixed[] list of allowed tabs
94 private function getAllowedTabs(UserGroupLevel $level): array
96 $cacheKey = 'menu-levels-' . $level->value;
97 if (SessionCache::has($cacheKey)) {
98 return SessionCache::get($cacheKey);
101 $allowedTabs = Util::getMenuTabList($level);
102 $configurableMenusFeature = $this->relation->getRelationParameters()->configurableMenusFeature;
103 if ($configurableMenusFeature !== null) {
104 $groupTable = Util::backquote($configurableMenusFeature->database)
105 . '.' . Util::backquote($configurableMenusFeature->userGroups);
106 $userTable = Util::backquote($configurableMenusFeature->database)
107 . '.' . Util::backquote($configurableMenusFeature->users);
109 $sqlQuery = 'SELECT `tab` FROM ' . $groupTable
110 . " WHERE `allowed` = 'N'"
111 . " AND `tab` LIKE '" . $level->value . "%'"
112 . ' AND `usergroup` = (SELECT usergroup FROM '
113 . $userTable . ' WHERE `username` = '
114 . $this->dbi->quoteString($this->config->selectedServer['user'], ConnectionType::ControlUser) . ')';
116 $result = $this->dbi->tryQueryAsControlUser($sqlQuery);
117 if ($result) {
118 while ($row = $result->fetchAssoc()) {
119 $tab = (string) $row['tab'];
120 $tabName = mb_substr(
121 $tab,
122 mb_strpos($tab, '_') + 1,
124 unset($allowedTabs[$tabName]);
129 SessionCache::set($cacheKey, $allowedTabs);
131 return $allowedTabs;
134 /** @return array{server: mixed[], database: mixed[], table: mixed[]} */
135 private function getBreadcrumbs(): array
137 $server = [];
138 $database = [];
139 $table = [];
141 if (empty($this->config->selectedServer['host'])) {
142 $this->config->selectedServer['host'] = '';
145 $server['name'] = ! empty($this->config->selectedServer['verbose'])
146 ? $this->config->selectedServer['verbose'] : $this->config->selectedServer['host'];
147 $server['name'] .= empty($this->config->selectedServer['port'])
148 ? '' : ':' . $this->config->selectedServer['port'];
149 $server['url'] = Util::getUrlForOption($this->config->settings['DefaultTabServer'], 'server');
151 if ($this->db !== '') {
152 $database['name'] = $this->db;
153 $database['url'] = Util::getUrlForOption($this->config->settings['DefaultTabDatabase'], 'database');
154 if ($this->table !== '') {
155 $table['name'] = $this->table;
156 $table['url'] = Util::getUrlForOption($this->config->settings['DefaultTabTable'], 'table');
157 $tableObj = $this->dbi->getTable($this->db, $this->table);
158 $table['is_view'] = $tableObj->isView();
159 $table['comment'] = '';
160 if (! $table['is_view']) {
161 $table['comment'] = $tableObj->getComment();
164 if (str_contains($table['comment'], '; InnoDB free')) {
165 $table['comment'] = (string) preg_replace('@; InnoDB free:.*?$@', '', $table['comment']);
167 } else {
168 // no table selected, display database comment if present
169 $relationParameters = $this->relation->getRelationParameters();
171 // Get additional information about tables for tooltip is done
172 // in Util::getDbInfo() only once
173 if ($relationParameters->columnCommentsFeature !== null) {
174 $database['comment'] = $this->relation->getDbComment($this->db);
179 return ['server' => $server, 'database' => $database, 'table' => $table];
183 * Returns the table tabs as an array
185 * @return mixed[] Data for generating table tabs
187 private function getTableTabs(): array
189 $route = Routing::$route;
191 $isSystemSchema = Utilities::isSystemSchema($this->db);
192 $tableIsView = $this->dbi->getTable($this->db, $this->table)
193 ->isView();
194 $updatableView = false;
195 if ($tableIsView) {
196 $updatableView = $this->dbi->getTable($this->db, $this->table)
197 ->isUpdatableView();
200 $isSuperUser = $this->dbi->isSuperUser();
201 $isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
203 $tabs = [];
205 $tabs['browse']['icon'] = 'b_browse';
206 $tabs['browse']['text'] = __('Browse');
207 $tabs['browse']['route'] = '/sql';
208 $tabs['browse']['args']['pos'] = 0;
209 $tabs['browse']['active'] = $route === '/sql';
211 $tabs['structure']['icon'] = 'b_props';
212 $tabs['structure']['route'] = '/table/structure';
213 $tabs['structure']['text'] = __('Structure');
214 $tabs['structure']['active'] = in_array($route, ['/table/relation', '/table/structure'], true);
216 $tabs['sql']['icon'] = 'b_sql';
217 $tabs['sql']['route'] = '/table/sql';
218 $tabs['sql']['text'] = __('SQL');
219 $tabs['sql']['active'] = $route === '/table/sql';
221 $tabs['search']['icon'] = 'b_search';
222 $tabs['search']['text'] = __('Search');
223 $tabs['search']['route'] = '/table/search';
224 $tabs['search']['active'] = in_array($route, [
225 '/table/find-replace',
226 '/table/search',
227 '/table/zoom-search',
228 ], true);
230 if (! $isSystemSchema && (! $tableIsView || $updatableView)) {
231 $tabs['insert']['icon'] = 'b_insrow';
232 $tabs['insert']['route'] = '/table/change';
233 $tabs['insert']['text'] = __('Insert');
234 $tabs['insert']['active'] = $route === '/table/change';
237 $tabs['export']['icon'] = 'b_tblexport';
238 $tabs['export']['route'] = '/table/export';
239 $tabs['export']['args']['single_table'] = 'true';
240 $tabs['export']['text'] = __('Export');
241 $tabs['export']['active'] = $route === '/table/export';
244 * Don't display "Import" for views and information_schema
246 if (! $tableIsView && ! $isSystemSchema) {
247 $tabs['import']['icon'] = 'b_tblimport';
248 $tabs['import']['route'] = '/table/import';
249 $tabs['import']['text'] = __('Import');
250 $tabs['import']['active'] = $route === '/table/import';
253 if (($isSuperUser || $isCreateOrGrantUser) && ! $isSystemSchema) {
254 $tabs['privileges']['route'] = '/table/privileges';
255 // stay on table view
256 $tabs['privileges']['text'] = __('Privileges');
257 $tabs['privileges']['icon'] = 's_rights';
258 $tabs['privileges']['active'] = $route === '/table/privileges';
262 * Don't display "Operations" for views and information_schema
264 if (! $tableIsView && ! $isSystemSchema) {
265 $tabs['operation']['icon'] = 'b_tblops';
266 $tabs['operation']['route'] = '/table/operations';
267 $tabs['operation']['text'] = __('Operations');
268 $tabs['operation']['active'] = $route === '/table/operations';
272 * Views support a limited number of operations
274 if ($tableIsView && ! $isSystemSchema) {
275 $tabs['operation']['icon'] = 'b_tblops';
276 $tabs['operation']['route'] = '/view/operations';
277 $tabs['operation']['text'] = __('Operations');
278 $tabs['operation']['active'] = $route === '/view/operations';
281 if (Tracker::isActive() && ! $isSystemSchema) {
282 $tabs['tracking']['icon'] = 'eye';
283 $tabs['tracking']['text'] = __('Tracking');
284 $tabs['tracking']['route'] = '/table/tracking';
285 $tabs['tracking']['active'] = $route === '/table/tracking';
288 if (! $isSystemSchema && Util::currentUserHasPrivilege('TRIGGER', $this->db, $this->table) && ! $tableIsView) {
289 $tabs['triggers']['route'] = '/triggers';
290 $tabs['triggers']['text'] = __('Triggers');
291 $tabs['triggers']['icon'] = 'b_triggers';
292 $tabs['triggers']['active'] = $route === '/triggers';
295 return $tabs;
299 * Returns the db tabs as an array
301 * @return mixed[] Data for generating db tabs
303 private function getDbTabs(): array
305 $route = Routing::$route;
307 $isSystemSchema = Utilities::isSystemSchema($this->db);
308 $numTables = count($this->dbi->getTables($this->db));
309 $isSuperUser = $this->dbi->isSuperUser();
310 $isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
312 $relationParameters = $this->relation->getRelationParameters();
314 $tabs = [];
316 $tabs['structure']['route'] = '/database/structure';
317 $tabs['structure']['text'] = __('Structure');
318 $tabs['structure']['icon'] = 'b_props';
319 $tabs['structure']['active'] = $route === '/database/structure';
321 $tabs['sql']['route'] = '/database/sql';
322 $tabs['sql']['text'] = __('SQL');
323 $tabs['sql']['icon'] = 'b_sql';
324 $tabs['sql']['active'] = $route === '/database/sql';
326 $tabs['search']['text'] = __('Search');
327 $tabs['search']['icon'] = 'b_search';
328 $tabs['search']['route'] = '/database/search';
329 $tabs['search']['active'] = $route === '/database/search';
330 if ($numTables == 0) {
331 $tabs['search']['warning'] = __('Database seems to be empty!');
334 $tabs['query']['text'] = __('Query');
335 $tabs['query']['icon'] = 's_db';
336 $tabs['query']['route'] = '/database/multi-table-query';
337 $tabs['query']['active'] = $route === '/database/multi-table-query';
338 if ($numTables == 0) {
339 $tabs['query']['warning'] = __('Database seems to be empty!');
342 $tabs['export']['text'] = __('Export');
343 $tabs['export']['icon'] = 'b_export';
344 $tabs['export']['route'] = '/database/export';
345 $tabs['export']['active'] = $route === '/database/export';
346 if ($numTables == 0) {
347 $tabs['export']['warning'] = __('Database seems to be empty!');
350 if (! $isSystemSchema) {
351 $tabs['import']['route'] = '/database/import';
352 $tabs['import']['text'] = __('Import');
353 $tabs['import']['icon'] = 'b_import';
354 $tabs['import']['active'] = $route === '/database/import';
356 $tabs['operation']['route'] = '/database/operations';
357 $tabs['operation']['text'] = __('Operations');
358 $tabs['operation']['icon'] = 'b_tblops';
359 $tabs['operation']['active'] = $route === '/database/operations';
361 if ($isSuperUser || $isCreateOrGrantUser) {
362 $tabs['privileges']['route'] = '/database/privileges';
363 // stay on database view
364 $tabs['privileges']['text'] = __('Privileges');
365 $tabs['privileges']['icon'] = 's_rights';
366 $tabs['privileges']['active'] = $route === '/database/privileges';
369 $tabs['routines']['route'] = '/database/routines';
370 $tabs['routines']['text'] = __('Routines');
371 $tabs['routines']['icon'] = 'b_routines';
372 $tabs['routines']['active'] = $route === '/database/routines';
374 if (Util::currentUserHasPrivilege('EVENT', $this->db)) {
375 $tabs['events']['route'] = '/database/events';
376 $tabs['events']['text'] = __('Events');
377 $tabs['events']['icon'] = 'b_events';
378 $tabs['events']['active'] = $route === '/database/events';
381 if (Util::currentUserHasPrivilege('TRIGGER', $this->db)) {
382 $tabs['triggers']['route'] = '/triggers';
383 $tabs['triggers']['text'] = __('Triggers');
384 $tabs['triggers']['icon'] = 'b_triggers';
385 $tabs['triggers']['active'] = $route === '/triggers';
389 if (Tracker::isActive() && ! $isSystemSchema) {
390 $tabs['tracking']['text'] = __('Tracking');
391 $tabs['tracking']['icon'] = 'eye';
392 $tabs['tracking']['route'] = '/database/tracking';
393 $tabs['tracking']['active'] = $route === '/database/tracking';
396 if (! $isSystemSchema) {
397 $tabs['designer']['text'] = __('Designer');
398 $tabs['designer']['icon'] = 'b_relations';
399 $tabs['designer']['route'] = '/database/designer';
400 $tabs['designer']['active'] = $route === '/database/designer';
403 if (! $isSystemSchema && $relationParameters->centralColumnsFeature !== null) {
404 $tabs['central_columns']['text'] = __('Central columns');
405 $tabs['central_columns']['icon'] = 'centralColumns';
406 $tabs['central_columns']['route'] = '/database/central-columns';
407 $tabs['central_columns']['active'] = $route === '/database/central-columns';
410 return $tabs;
414 * Returns the server tabs as an array
416 * @return mixed[] Data for generating server tabs
418 private function getServerTabs(): array
420 $route = Routing::$route;
422 $isSuperUser = $this->dbi->isSuperUser();
423 $isCreateOrGrantUser = $this->dbi->isGrantUser() || $this->dbi->isCreateUser();
424 if (SessionCache::has('binary_logs')) {
425 $binaryLogs = SessionCache::get('binary_logs');
426 } else {
427 $binaryLogs = $this->dbi->fetchResult('SHOW MASTER LOGS', 'Log_name');
428 SessionCache::set('binary_logs', $binaryLogs);
431 $tabs = [];
433 $tabs['databases']['icon'] = 's_db';
434 $tabs['databases']['route'] = '/server/databases';
435 $tabs['databases']['text'] = __('Databases');
436 $tabs['databases']['active'] = $route === '/server/databases';
438 $tabs['sql']['icon'] = 'b_sql';
439 $tabs['sql']['route'] = '/server/sql';
440 $tabs['sql']['text'] = __('SQL');
441 $tabs['sql']['active'] = $route === '/server/sql';
443 $tabs['status']['icon'] = 's_status';
444 $tabs['status']['route'] = '/server/status';
445 $tabs['status']['text'] = __('Status');
446 $tabs['status']['active'] = in_array($route, [
447 '/server/status',
448 '/server/status/advisor',
449 '/server/status/monitor',
450 '/server/status/processes',
451 '/server/status/queries',
452 '/server/status/variables',
453 ], true);
455 if ($isSuperUser || $isCreateOrGrantUser) {
456 $tabs['rights']['icon'] = 's_rights';
457 $tabs['rights']['route'] = '/server/privileges';
458 $tabs['rights']['text'] = __('User accounts');
459 $tabs['rights']['active'] = in_array($route, ['/server/privileges', '/server/user-groups'], true);
462 $tabs['export']['icon'] = 'b_export';
463 $tabs['export']['route'] = '/server/export';
464 $tabs['export']['text'] = __('Export');
465 $tabs['export']['active'] = $route === '/server/export';
467 $tabs['import']['icon'] = 'b_import';
468 $tabs['import']['route'] = '/server/import';
469 $tabs['import']['text'] = __('Import');
470 $tabs['import']['active'] = $route === '/server/import';
472 $tabs['settings']['icon'] = 'b_tblops';
473 $tabs['settings']['route'] = '/preferences/manage';
474 $tabs['settings']['text'] = __('Settings');
475 $tabs['settings']['active'] = in_array($route, [
476 '/preferences/export',
477 '/preferences/features',
478 '/preferences/import',
479 '/preferences/main-panel',
480 '/preferences/manage',
481 '/preferences/navigation',
482 '/preferences/sql',
483 '/preferences/two-factor',
484 ], true);
486 if (! empty($binaryLogs)) {
487 $tabs['binlog']['icon'] = 's_tbl';
488 $tabs['binlog']['route'] = '/server/binlog';
489 $tabs['binlog']['text'] = __('Binary log');
490 $tabs['binlog']['active'] = $route === '/server/binlog';
493 if ($isSuperUser) {
494 $tabs['replication']['icon'] = 's_replication';
495 $tabs['replication']['route'] = '/server/replication';
496 $tabs['replication']['text'] = __('Replication');
497 $tabs['replication']['active'] = $route === '/server/replication';
500 $tabs['vars']['icon'] = 's_vars';
501 $tabs['vars']['route'] = '/server/variables';
502 $tabs['vars']['text'] = __('Variables');
503 $tabs['vars']['active'] = $route === '/server/variables';
505 $tabs['charset']['icon'] = 's_asci';
506 $tabs['charset']['route'] = '/server/collations';
507 $tabs['charset']['text'] = __('Charsets');
508 $tabs['charset']['active'] = $route === '/server/collations';
510 $tabs['engine']['icon'] = 'b_engine';
511 $tabs['engine']['route'] = '/server/engines';
512 $tabs['engine']['text'] = __('Engines');
513 $tabs['engine']['active'] = $route === '/server/engines';
515 $tabs['plugins']['icon'] = 'b_plugin';
516 $tabs['plugins']['route'] = '/server/plugins';
517 $tabs['plugins']['text'] = __('Plugins');
518 $tabs['plugins']['active'] = $route === '/server/plugins';
520 return $tabs;
524 * Set current table
526 * @param string $table Current table
528 public function setTable(string $table): Menu
530 $this->table = $table;
532 return $this;