RecentFavoriteTable.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Recent and Favorite table list handling
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\DatabaseInterface;
  10. use PhpMyAdmin\Message;
  11. use PhpMyAdmin\Relation;
  12. use PhpMyAdmin\Url;
  13. use PhpMyAdmin\Util;
  14. /**
  15. * Handles the recently used and favorite tables.
  16. *
  17. * @TODO Change the release version in table pma_recent
  18. * (#recent in documentation)
  19. *
  20. * @package PhpMyAdmin
  21. */
  22. class RecentFavoriteTable
  23. {
  24. /**
  25. * Reference to session variable containing recently used or favorite tables.
  26. *
  27. * @access private
  28. * @var array
  29. */
  30. private $_tables;
  31. /**
  32. * Defines type of action, Favorite or Recent table.
  33. *
  34. * @access private
  35. * @var string
  36. */
  37. private $_tableType;
  38. /**
  39. * RecentFavoriteTable instances.
  40. *
  41. * @access private
  42. * @var array
  43. */
  44. private static $_instances = array();
  45. /**
  46. * @var Relation $relation
  47. */
  48. private $relation;
  49. /**
  50. * Creates a new instance of RecentFavoriteTable
  51. *
  52. * @param string $type the table type
  53. *
  54. * @access private
  55. */
  56. private function __construct($type)
  57. {
  58. $this->relation = new Relation();
  59. $this->_tableType = $type;
  60. $server_id = $GLOBALS['server'];
  61. if (! isset($_SESSION['tmpval'][$this->_tableType . '_tables'][$server_id])
  62. ) {
  63. $_SESSION['tmpval'][$this->_tableType . '_tables'][$server_id]
  64. = $this->_getPmaTable() ? $this->getFromDb() : array();
  65. }
  66. $this->_tables
  67. =& $_SESSION['tmpval'][$this->_tableType . '_tables'][$server_id];
  68. }
  69. /**
  70. * Returns class instance.
  71. *
  72. * @param string $type the table type
  73. *
  74. * @return RecentFavoriteTable
  75. */
  76. public static function getInstance($type)
  77. {
  78. if (! array_key_exists($type, self::$_instances)) {
  79. self::$_instances[$type] = new RecentFavoriteTable($type);
  80. }
  81. return self::$_instances[$type];
  82. }
  83. /**
  84. * Returns the recent/favorite tables array
  85. *
  86. * @return array
  87. */
  88. public function getTables()
  89. {
  90. return $this->_tables;
  91. }
  92. /**
  93. * Returns recently used tables or favorite from phpMyAdmin database.
  94. *
  95. * @return array
  96. */
  97. public function getFromDb()
  98. {
  99. // Read from phpMyAdmin database, if recent tables is not in session
  100. $sql_query
  101. = " SELECT `tables` FROM " . $this->_getPmaTable() .
  102. " WHERE `username` = '" . $GLOBALS['dbi']->escapeString($GLOBALS['cfg']['Server']['user']) . "'";
  103. $return = array();
  104. $result = $this->relation->queryAsControlUser($sql_query, false);
  105. if ($result) {
  106. $row = $GLOBALS['dbi']->fetchArray($result);
  107. if (isset($row[0])) {
  108. $return = json_decode($row[0], true);
  109. }
  110. }
  111. return $return;
  112. }
  113. /**
  114. * Save recent/favorite tables into phpMyAdmin database.
  115. *
  116. * @return true|Message
  117. */
  118. public function saveToDb()
  119. {
  120. $username = $GLOBALS['cfg']['Server']['user'];
  121. $sql_query
  122. = " REPLACE INTO " . $this->_getPmaTable() . " (`username`, `tables`)" .
  123. " VALUES ('" . $GLOBALS['dbi']->escapeString($username) . "', '"
  124. . $GLOBALS['dbi']->escapeString(
  125. json_encode($this->_tables)
  126. ) . "')";
  127. $success = $GLOBALS['dbi']->tryQuery($sql_query, DatabaseInterface::CONNECT_CONTROL);
  128. if (! $success) {
  129. $error_msg = '';
  130. switch ($this->_tableType) {
  131. case 'recent':
  132. $error_msg = __('Could not save recent table!');
  133. break;
  134. case 'favorite':
  135. $error_msg = __('Could not save favorite table!');
  136. break;
  137. }
  138. $message = Message::error($error_msg);
  139. $message->addMessage(
  140. Message::rawError(
  141. $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL)
  142. ),
  143. '<br /><br />'
  144. );
  145. return $message;
  146. }
  147. return true;
  148. }
  149. /**
  150. * Trim recent.favorite table according to the
  151. * NumRecentTables/NumFavoriteTables configuration.
  152. *
  153. * @return boolean True if trimming occurred
  154. */
  155. public function trim()
  156. {
  157. $max = max(
  158. $GLOBALS['cfg']['Num' . ucfirst($this->_tableType) . 'Tables'], 0
  159. );
  160. $trimming_occurred = count($this->_tables) > $max;
  161. while (count($this->_tables) > $max) {
  162. array_pop($this->_tables);
  163. }
  164. return $trimming_occurred;
  165. }
  166. /**
  167. * Return HTML ul.
  168. *
  169. * @return string
  170. */
  171. public function getHtmlList()
  172. {
  173. $html = '';
  174. if (count($this->_tables)) {
  175. if ($this->_tableType == 'recent') {
  176. foreach ($this->_tables as $table) {
  177. $html .= '<li class="warp_link">';
  178. $recent_params = array(
  179. 'db' => $table['db'],
  180. 'table' => $table['table']
  181. );
  182. $recent_url = 'tbl_recent_favorite.php'
  183. . Url::getCommon($recent_params);
  184. $html .= '<a href="' . $recent_url . '">`'
  185. . htmlspecialchars($table['db']) . '`.`'
  186. . htmlspecialchars($table['table']) . '`</a>';
  187. $html .= '</li>';
  188. }
  189. } else {
  190. foreach ($this->_tables as $table) {
  191. $html .= '<li class="warp_link">';
  192. $html .= '<a class="ajax favorite_table_anchor" ';
  193. $fav_params = array(
  194. 'db' => $table['db'],
  195. 'ajax_request' => true,
  196. 'favorite_table' => $table['table'],
  197. 'remove_favorite' => true
  198. );
  199. $fav_rm_url = 'db_structure.php'
  200. . Url::getCommon($fav_params);
  201. $html .= 'href="' . $fav_rm_url
  202. . '" title="' . __("Remove from Favorites")
  203. . '" data-favtargetn="'
  204. . md5($table['db'] . "." . $table['table'])
  205. . '" >'
  206. . Util::getIcon('b_favorite')
  207. . '</a>';
  208. $fav_params = array(
  209. 'db' => $table['db'],
  210. 'table' => $table['table']
  211. );
  212. $table_url = 'tbl_recent_favorite.php'
  213. . Url::getCommon($fav_params);
  214. $html .= '<a href="' . $table_url . '">`'
  215. . htmlspecialchars($table['db']) . '`.`'
  216. . htmlspecialchars($table['table']) . '`</a>';
  217. $html .= '</li>';
  218. }
  219. }
  220. } else {
  221. $html .= '<li class="warp_link">'
  222. . ($this->_tableType == 'recent'
  223. ?__('There are no recent tables.')
  224. :__('There are no favorite tables.'))
  225. . '</li>';
  226. }
  227. return $html;
  228. }
  229. /**
  230. * Return HTML.
  231. *
  232. * @return string
  233. */
  234. public function getHtml()
  235. {
  236. $html = '<div class="drop_list">';
  237. if ($this->_tableType == 'recent') {
  238. $html .= '<span title="' . __('Recent tables')
  239. . '" class="drop_button">'
  240. . __('Recent') . '</span><ul id="pma_recent_list">';
  241. } else {
  242. $html .= '<span title="' . __('Favorite tables')
  243. . '" class="drop_button">'
  244. . __('Favorites') . '</span><ul id="pma_favorite_list">';
  245. }
  246. $html .= $this->getHtmlList();
  247. $html .= '</ul></div>';
  248. return $html;
  249. }
  250. /**
  251. * Add recently used or favorite tables.
  252. *
  253. * @param string $db database name where the table is located
  254. * @param string $table table name
  255. *
  256. * @return true|Message True if success, Message if not
  257. */
  258. public function add($db, $table)
  259. {
  260. // If table does not exist, do not add._getPmaTable()
  261. if (! $GLOBALS['dbi']->getColumns($db, $table)) {
  262. return true;
  263. }
  264. $table_arr = array();
  265. $table_arr['db'] = $db;
  266. $table_arr['table'] = $table;
  267. // add only if this is new table
  268. if (! isset($this->_tables[0]) || $this->_tables[0] != $table_arr) {
  269. array_unshift($this->_tables, $table_arr);
  270. $this->_tables = array_merge(array_unique($this->_tables, SORT_REGULAR));
  271. $this->trim();
  272. if ($this->_getPmaTable()) {
  273. return $this->saveToDb();
  274. }
  275. }
  276. return true;
  277. }
  278. /**
  279. * Removes recent/favorite tables that don't exist.
  280. *
  281. * @param string $db database
  282. * @param string $table table
  283. *
  284. * @return boolean|Message True if invalid and removed, False if not invalid,
  285. * Message if error while removing
  286. */
  287. public function removeIfInvalid($db, $table)
  288. {
  289. foreach ($this->_tables as $tbl) {
  290. if ($tbl['db'] == $db && $tbl['table'] == $table) {
  291. // TODO Figure out a better way to find the existence of a table
  292. if (! $GLOBALS['dbi']->getColumns($tbl['db'], $tbl['table'])) {
  293. return $this->remove($tbl['db'], $tbl['table']);
  294. }
  295. }
  296. }
  297. return false;
  298. }
  299. /**
  300. * Remove favorite tables.
  301. *
  302. * @param string $db database name where the table is located
  303. * @param string $table table name
  304. *
  305. * @return true|Message True if success, Message if not
  306. */
  307. public function remove($db, $table)
  308. {
  309. $table_arr = array();
  310. $table_arr['db'] = $db;
  311. $table_arr['table'] = $table;
  312. foreach ($this->_tables as $key => $value) {
  313. if ($value['db'] == $db && $value['table'] == $table) {
  314. unset($this->_tables[$key]);
  315. }
  316. }
  317. if ($this->_getPmaTable()) {
  318. return $this->saveToDb();
  319. }
  320. return true;
  321. }
  322. /**
  323. * Generate Html for sync Favorite tables anchor. (from localStorage to pmadb)
  324. *
  325. * @return string
  326. */
  327. public function getHtmlSyncFavoriteTables()
  328. {
  329. $retval = '';
  330. $server_id = $GLOBALS['server'];
  331. if ($server_id == 0) {
  332. return '';
  333. }
  334. $cfgRelation = $this->relation->getRelationsParam();
  335. // Not to show this once list is synchronized.
  336. if ($cfgRelation['favoritework'] && ! isset($_SESSION['tmpval']['favorites_synced'][$server_id])) {
  337. $params = array('ajax_request' => true, 'favorite_table' => true,
  338. 'sync_favorite_tables' => true);
  339. $url = 'db_structure.php' . Url::getCommon($params);
  340. $retval = '<a class="hide" id="sync_favorite_tables"';
  341. $retval .= ' href="' . $url . '"></a>';
  342. }
  343. return $retval;
  344. }
  345. /**
  346. * Generate Html to update recent tables.
  347. *
  348. * @return string html
  349. */
  350. public static function getHtmlUpdateRecentTables()
  351. {
  352. $params = array('ajax_request' => true, 'recent_table' => true);
  353. $url = 'index.php' . Url::getCommon($params);
  354. $retval = '<a class="hide" id="update_recent_tables"';
  355. $retval .= ' href="' . $url . '"></a>';
  356. return $retval;
  357. }
  358. /**
  359. * Return the name of the configuration storage table
  360. *
  361. * @return string|null pma table name
  362. */
  363. private function _getPmaTable()
  364. {
  365. $cfgRelation = $this->relation->getRelationsParam();
  366. if (! $cfgRelation['recentwork']) {
  367. return null;
  368. }
  369. if (! empty($cfgRelation['db'])
  370. && ! empty($cfgRelation[$this->_tableType])
  371. ) {
  372. return Util::backquote($cfgRelation['db']) . "."
  373. . Util::backquote($cfgRelation[$this->_tableType]);
  374. }
  375. return null;
  376. }
  377. }