Translated using Weblate (Portuguese (Brazil))
[phpmyadmin.git] / src / RecentFavoriteTable.php
blob28b0d3c897a70e97fcb897949dffd5997fb128ef
1 <?php
2 /**
3 * Recent and Favorite table list handling
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin;
10 use PhpMyAdmin\ConfigStorage\Relation;
11 use PhpMyAdmin\Dbal\Connection;
13 use function __;
14 use function array_key_exists;
15 use function array_merge;
16 use function array_pop;
17 use function array_unique;
18 use function array_unshift;
19 use function count;
20 use function is_string;
21 use function json_decode;
22 use function json_encode;
23 use function max;
24 use function md5;
25 use function ucfirst;
27 use const SORT_REGULAR;
29 /**
30 * Handles the recently used and favorite tables.
32 * @TODO Change the release version in table pma_recent
33 * (#recent in documentation)
35 class RecentFavoriteTable
37 /**
38 * Reference to session variable containing recently used or favorite tables.
40 * @var mixed[]
42 private array $tables = [];
44 /**
45 * RecentFavoriteTable instances.
47 * @var array<string,RecentFavoriteTable>
49 private static array $instances = [];
51 private Relation $relation;
53 /**
54 * Creates a new instance of RecentFavoriteTable
56 * @param string $tableType Defines type of action, Favorite or Recent table.
57 * @phpstan-param 'favorite'|'recent' $tableType
59 private function __construct(public Template $template, private string $tableType)
61 $this->relation = new Relation(DatabaseInterface::getInstance());
62 $serverId = $GLOBALS['server'];
63 // Code search hint: recentTables
64 // Code search hint: favoriteTables
65 if (! isset($_SESSION['tmpval'][$this->tableType . 'Tables'][$serverId])) {
66 $_SESSION['tmpval'][$this->tableType . 'Tables'][$serverId] = $this->getPmaTable()
67 ? $this->getFromDb()
68 : [];
71 $this->tables =& $_SESSION['tmpval'][$this->tableType . 'Tables'][$serverId];
74 /**
75 * Returns class instance.
77 * @param string $type the table type
78 * @psalm-param 'favorite'|'recent' $type
80 public static function getInstance(string $type): RecentFavoriteTable
82 if (! array_key_exists($type, self::$instances)) {
83 $template = new Template();
84 self::$instances[$type] = new RecentFavoriteTable($template, $type);
87 return self::$instances[$type];
90 /**
91 * Returns the recent/favorite tables array
93 * @return mixed[]
95 public function getTables(): array
97 return $this->tables;
101 * Returns recently used tables or favorite from phpMyAdmin database.
103 * @return mixed[]
105 public function getFromDb(): array
107 // Read from phpMyAdmin database, if recent tables is not in session
108 $dbi = DatabaseInterface::getInstance();
109 $sqlQuery = ' SELECT `tables` FROM ' . $this->getPmaTable()
110 . ' WHERE `username` = '
111 . $dbi->quoteString(Config::getInstance()->selectedServer['user'], Connection::TYPE_CONTROL);
113 $result = $dbi->tryQueryAsControlUser($sqlQuery);
114 if ($result) {
115 $value = $result->fetchValue();
116 if (is_string($value)) {
117 return json_decode($value, true);
121 return [];
125 * Save recent/favorite tables into phpMyAdmin database.
127 * @return true|Message
129 public function saveToDb(): bool|Message
131 $username = Config::getInstance()->selectedServer['user'];
132 $dbi = DatabaseInterface::getInstance();
133 $sqlQuery = ' REPLACE INTO ' . $this->getPmaTable() . ' (`username`, `tables`)'
134 . ' VALUES (' . $dbi->quoteString($username) . ', '
135 . $dbi->quoteString(json_encode($this->tables)) . ')';
137 $success = $dbi->tryQuery($sqlQuery, Connection::TYPE_CONTROL);
139 if (! $success) {
140 $errorMsg = match ($this->tableType) {
141 'recent' => __('Could not save recent table!'),
142 'favorite' => __('Could not save favorite table!'),
145 $message = Message::error($errorMsg);
146 $message->addMessage(
147 Message::rawError($dbi->getError(Connection::TYPE_CONTROL)),
148 '<br><br>',
151 return $message;
154 return true;
158 * Trim recent.favorite table according to the
159 * NumRecentTables/NumFavoriteTables configuration.
161 public function trim(): bool
163 $max = max(
164 Config::getInstance()->settings['Num' . ucfirst($this->tableType) . 'Tables'],
167 $trimmingOccurred = count($this->tables) > $max;
168 while (count($this->tables) > $max) {
169 array_pop($this->tables);
172 return $trimmingOccurred;
176 * Return HTML ul.
178 public function getHtmlList(): string
180 if ($this->tables !== []) {
181 if ($this->tableType === 'recent') {
182 $tables = [];
183 foreach ($this->tables as $table) {
184 $tables[] = ['db' => $table['db'], 'table' => $table['table']];
187 return $this->template->render('recent_favorite_table_recent', ['tables' => $tables]);
190 $tables = [];
191 foreach ($this->tables as $table) {
192 $removeParameters = [
193 'db' => $table['db'],
194 'ajax_request' => true,
195 'favorite_table' => $table['table'],
196 'remove_favorite' => true,
198 $tableParameters = [
199 'db' => $table['db'],
200 'table' => $table['table'],
201 'md5' => md5($table['db'] . '.' . $table['table']),
204 $tables[] = ['remove_parameters' => $removeParameters, 'table_parameters' => $tableParameters];
207 return $this->template->render('recent_favorite_table_favorite', ['tables' => $tables]);
210 return $this->template->render('recent_favorite_table_no_tables', [
211 'is_recent' => $this->tableType === 'recent',
215 public function getHtml(): string
217 $html = '<div class="drop_list">';
218 if ($this->tableType === 'recent') {
219 $html .= '<button title="' . __('Recent tables')
220 . '" class="drop_button btn btn-sm btn-outline-secondary">'
221 . __('Recent') . '</button><ul id="pma_recent_list">';
222 } else {
223 $html .= '<button title="' . __('Favorite tables')
224 . '" class="drop_button btn btn-sm btn-outline-secondary">'
225 . __('Favorites') . '</button><ul id="pma_favorite_list">';
228 $html .= $this->getHtmlList();
229 $html .= '</ul></div>';
231 return $html;
235 * Add recently used or favorite tables.
237 * @param string $db database name where the table is located
238 * @param string $table table name
240 * @return true|Message True if success, Message if not
242 public function add(string $db, string $table): bool|Message
244 // If table does not exist, do not add._getPmaTable()
245 if (DatabaseInterface::getInstance()->getColumns($db, $table) === []) {
246 return true;
249 $tableArr = [];
250 $tableArr['db'] = $db;
251 $tableArr['table'] = $table;
253 // add only if this is new table
254 if (! isset($this->tables[0]) || $this->tables[0] != $tableArr) {
255 array_unshift($this->tables, $tableArr);
256 $this->tables = array_merge(array_unique($this->tables, SORT_REGULAR));
257 $this->trim();
258 if ($this->getPmaTable()) {
259 return $this->saveToDb();
263 return true;
267 * Removes recent/favorite tables that don't exist.
269 * @param string $db database
270 * @param string $table table
272 * @return bool|Message True if invalid and removed, False if not invalid,
273 * Message if error while removing
275 public function removeIfInvalid(string $db, string $table): bool|Message
277 foreach ($this->tables as $tbl) {
278 if ($tbl['db'] != $db || $tbl['table'] != $table) {
279 continue;
282 // TODO Figure out a better way to find the existence of a table
283 if (DatabaseInterface::getInstance()->getColumns($tbl['db'], $tbl['table']) === []) {
284 return $this->remove($tbl['db'], $tbl['table']);
288 return false;
292 * Remove favorite tables.
294 * @param string $db database name where the table is located
295 * @param string $table table name
297 * @return true|Message True if success, Message if not
299 public function remove(string $db, string $table): bool|Message
301 foreach ($this->tables as $key => $value) {
302 if ($value['db'] != $db || $value['table'] != $table) {
303 continue;
306 unset($this->tables[$key]);
309 if ($this->getPmaTable()) {
310 return $this->saveToDb();
313 return true;
317 * Generate Html for sync Favorite tables anchor. (from localStorage to pmadb)
319 public function getHtmlSyncFavoriteTables(): string
321 $retval = '';
322 $serverId = $GLOBALS['server'];
323 if ($serverId == 0) {
324 return '';
327 $relationParameters = $this->relation->getRelationParameters();
328 // Not to show this once list is synchronized.
329 if (
330 $relationParameters->favoriteTablesFeature !== null
331 && ! isset($_SESSION['tmpval']['favorites_synced'][$serverId])
333 $url = Url::getFromRoute('/database/structure/favorite-table', [
334 'ajax_request' => true,
335 'favorite_table' => true,
336 'sync_favorite_tables' => true,
338 $retval = '<a class="hide" id="sync_favorite_tables"';
339 $retval .= ' href="' . $url . '"></a>';
342 return $retval;
346 * Generate Html to update recent tables.
348 public static function getHtmlUpdateRecentTables(): string
350 return '<a class="hide" id="update_recent_tables" href="'
351 . Url::getFromRoute('/recent-table', ['ajax_request' => true, 'recent_table' => true])
352 . '"></a>';
356 * Return the name of the configuration storage table
358 * @return string|null pma table name
360 private function getPmaTable(): string|null
362 $relationParameters = $this->relation->getRelationParameters();
363 if ($this->tableType === 'recent' && $relationParameters->recentlyUsedTablesFeature !== null) {
364 return Util::backquote($relationParameters->recentlyUsedTablesFeature->database)
365 . '.' . Util::backquote($relationParameters->recentlyUsedTablesFeature->recent);
368 if ($this->tableType === 'favorite' && $relationParameters->favoriteTablesFeature !== null) {
369 return Util::backquote($relationParameters->favoriteTablesFeature->database)
370 . '.' . Util::backquote($relationParameters->favoriteTablesFeature->favorite);
373 return null;