Translated using Weblate (Portuguese)
[phpmyadmin.git] / src / Favorites / RecentFavoriteTables.php
blob7cf76c875e55933fd698359f0fcf06ea700226e5
1 <?php
2 /**
3 * Recent and Favorite table list handling
4 */
6 declare(strict_types=1);
8 namespace PhpMyAdmin\Favorites;
10 use PhpMyAdmin\Config;
11 use PhpMyAdmin\ConfigStorage\Relation;
12 use PhpMyAdmin\Current;
13 use PhpMyAdmin\DatabaseInterface;
14 use PhpMyAdmin\Dbal\ConnectionType;
15 use PhpMyAdmin\DbTableExists;
16 use PhpMyAdmin\Message;
17 use PhpMyAdmin\Template;
18 use PhpMyAdmin\Url;
19 use PhpMyAdmin\Util;
21 use function __;
22 use function array_key_exists;
23 use function array_pop;
24 use function array_unique;
25 use function array_unshift;
26 use function array_values;
27 use function count;
28 use function in_array;
29 use function is_string;
30 use function json_decode;
31 use function json_encode;
32 use function max;
33 use function md5;
34 use function ucfirst;
36 use const SORT_REGULAR;
38 /**
39 * Handles the recently used and favorite tables.
41 * @TODO Change the release version in table pma_recent
42 * (#recent in documentation)
44 class RecentFavoriteTables
46 /** @var RecentFavoriteTable[] */
47 private array $tables = [];
49 /**
50 * RecentFavoriteTable instances.
52 * @var array<string,RecentFavoriteTables>
54 private static array $instances = [];
56 /** @psalm-param int<0, max> $serverId */
57 private function __construct(
58 public Template $template,
59 private readonly TableType $tableType,
60 private readonly int $serverId,
61 private readonly DatabaseInterface $dbi,
62 private readonly Relation $relation,
63 private readonly DbTableExists $dbTableExists,
64 ) {
65 // Code search hint: recentTables
66 // Code search hint: favoriteTables
67 if (! isset($_SESSION['tmpval'][$this->tableType->value . 'Tables'][$this->serverId])) {
68 $_SESSION['tmpval'][$this->tableType->value . 'Tables'][$this->serverId] = $this->getPmaTable() !== null
69 ? $this->getFromDb()
70 : [];
73 foreach ($_SESSION['tmpval'][$this->tableType->value . 'Tables'][$this->serverId] as $table) {
74 $this->tables[] = RecentFavoriteTable::fromArray($table);
78 public function __destruct()
80 $_SESSION['tmpval'][$this->tableType->value . 'Tables'][$this->serverId] = [];
81 foreach ($this->tables as $table) {
82 $_SESSION['tmpval'][$this->tableType->value . 'Tables'][$this->serverId][] = $table->toArray();
86 /**
87 * Returns class instance.
89 public static function getInstance(TableType $type): RecentFavoriteTables
91 if (! array_key_exists($type->value, self::$instances)) {
92 $template = new Template();
93 $dbi = DatabaseInterface::getInstance();
94 self::$instances[$type->value] = new RecentFavoriteTables(
95 $template,
96 $type,
97 Current::$server,
98 $dbi,
99 new Relation($dbi),
100 new DbTableExists($dbi),
104 return self::$instances[$type->value];
108 * Returns the recent/favorite tables array
110 * @return RecentFavoriteTable[]
112 public function getTables(): array
114 return $this->tables;
118 * Returns recently used tables or favorite from phpMyAdmin database.
120 * @return array{db:string, table:string}[]
122 private function getFromDb(): array
124 // Read from phpMyAdmin database, if recent tables is not in session
125 $sqlQuery = 'SELECT `tables` FROM ' . $this->getPmaTable()
126 . ' WHERE `username` = '
127 . $this->dbi->quoteString(Config::getInstance()->selectedServer['user'], ConnectionType::ControlUser);
129 $result = $this->dbi->tryQueryAsControlUser($sqlQuery);
130 if ($result !== false) {
131 $value = $result->fetchValue();
132 if (is_string($value)) {
133 return json_decode($value, true);
137 return [];
141 * Save recent/favorite tables into phpMyAdmin database.
143 * @return true|Message
145 private function saveToDb(): bool|Message
147 $username = Config::getInstance()->selectedServer['user'];
148 $sqlQuery = ' REPLACE INTO ' . $this->getPmaTable() . ' (`username`, `tables`)'
149 . ' VALUES (' . $this->dbi->quoteString($username) . ', '
150 . $this->dbi->quoteString(json_encode($this->tables)) . ')';
152 $success = $this->dbi->tryQuery($sqlQuery, ConnectionType::ControlUser);
154 if ($success === false) {
155 $message = Message::error(match ($this->tableType) {
156 TableType::Recent => __('Could not save recent table!'),
157 TableType::Favorite => __('Could not save favorite table!'),
160 $message->addMessage(
161 Message::rawError($this->dbi->getError(ConnectionType::ControlUser)),
162 '<br><br>',
165 return $message;
168 return true;
172 * Trim recent/favorite table according to the
173 * NumRecentTables/NumFavoriteTables configuration.
175 private function trim(): void
177 $max = max(
178 Config::getInstance()->settings['Num' . ucfirst($this->tableType->value) . 'Tables'],
182 while (count($this->tables) > $max) {
183 array_pop($this->tables);
188 * Return HTML ul.
190 public function getHtmlList(): string
192 if ($this->tableType === TableType::Recent) {
193 $tables = [];
194 foreach ($this->tables as $table) {
195 $tables[] = $table->toArray();
198 return $this->template->render('recent_favorite_table_recent', ['tables' => $tables]);
201 $tables = [];
202 foreach ($this->tables as $table) {
203 $removeParameters = [
204 'db' => $table->db->getName(),
205 'favorite_table' => $table->table->getName(),
206 'ajax_request' => true,
207 'remove_favorite' => true,
209 $tableParameters = [
210 'db' => $table->db->getName(),
211 'table' => $table->table->getName(),
212 'md5' => md5($table->db . '.' . $table->table),
215 $tables[] = ['remove_parameters' => $removeParameters, 'table_parameters' => $tableParameters];
218 return $this->template->render('recent_favorite_table_favorite', ['tables' => $tables]);
222 * Add recently used or favorite tables.
224 * @return true|Message True if success, Message if not
226 public function add(RecentFavoriteTable $newTable): bool|Message
228 if (! $this->dbTableExists->hasTable($newTable->db, $newTable->table)) {
229 return true;
232 // add only if this is new table
233 if (! isset($this->tables[0]) || $this->tables[0] != $newTable) {
234 array_unshift($this->tables, $newTable);
235 $this->tables = array_values(array_unique($this->tables, SORT_REGULAR));
236 $this->trim();
237 if ($this->getPmaTable() !== null) {
238 return $this->saveToDb();
242 return true;
246 * Removes recent/favorite tables that don't exist.
248 * @return bool|Message True if invalid and removed, False if not invalid,
249 * Message if error while removing
251 public function removeIfInvalid(RecentFavoriteTable $tableToRemove): bool|Message
253 foreach ($this->tables as $table) {
254 if (
255 $table->db->getName() !== $tableToRemove->db->getName()
256 || $table->table->getName() !== $tableToRemove->table->getName()
258 continue;
261 if (! $this->dbTableExists->hasTable($table->db, $table->table)) {
262 return $this->remove($tableToRemove);
266 return false;
270 * Remove favorite tables.
272 * @return true|Message True if success, Message if not
274 public function remove(RecentFavoriteTable $tableToRemove): bool|Message
276 foreach ($this->tables as $key => $table) {
277 if (
278 $table->db->getName() !== $tableToRemove->db->getName()
279 || $table->table->getName() !== $tableToRemove->table->getName()
281 continue;
284 unset($this->tables[$key]);
287 if ($this->getPmaTable() !== null) {
288 return $this->saveToDb();
291 return true;
295 * Function to check if a table is already in favorite list.
297 public function contains(RecentFavoriteTable $currentTable): bool
299 // When looking for the value we are looking for a similar object with
300 // the same public properties, not the same instance. The in_array must be loose comparison.
301 return in_array($currentTable, $this->tables, false);
305 * Generate Html for sync Favorite tables anchor. (from localStorage to pmadb)
307 public function getHtmlSyncFavoriteTables(): string
309 $retval = '';
310 if (Current::$server === 0) {
311 return '';
314 $relationParameters = $this->relation->getRelationParameters();
315 // Not to show this once list is synchronized.
316 if (
317 $relationParameters->favoriteTablesFeature !== null
318 && ! isset($_SESSION['tmpval']['favorites_synced'][Current::$server])
320 $url = Url::getFromRoute('/database/structure/favorite-table', [
321 'ajax_request' => true,
322 'favorite_table' => true,
323 'sync_favorite_tables' => true,
325 $retval = '<a class="hide" id="sync_favorite_tables"';
326 $retval .= ' href="' . $url . '"></a>';
329 return $retval;
333 * Return the name of the configuration storage table
335 * @return string|null pma table name
337 private function getPmaTable(): string|null
339 $relationParameters = $this->relation->getRelationParameters();
340 if ($this->tableType === TableType::Recent && $relationParameters->recentlyUsedTablesFeature !== null) {
341 return Util::backquote($relationParameters->recentlyUsedTablesFeature->database)
342 . '.' . Util::backquote($relationParameters->recentlyUsedTablesFeature->recent);
345 if ($this->tableType === TableType::Favorite && $relationParameters->favoriteTablesFeature !== null) {
346 return Util::backquote($relationParameters->favoriteTablesFeature->database)
347 . '.' . Util::backquote($relationParameters->favoriteTablesFeature->favorite);
350 return null;