SavedSearches.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Saved searches managing
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin;
  10. /**
  11. * Saved searches managing
  12. *
  13. * @package PhpMyAdmin
  14. */
  15. class SavedSearches
  16. {
  17. /**
  18. * Global configuration
  19. * @var array
  20. */
  21. private $_config = null;
  22. /**
  23. * Id
  24. * @var int|null
  25. */
  26. private $_id = null;
  27. /**
  28. * Username
  29. * @var string
  30. */
  31. private $_username = null;
  32. /**
  33. * DB name
  34. * @var string
  35. */
  36. private $_dbname = null;
  37. /**
  38. * Saved search name
  39. * @var string
  40. */
  41. private $_searchName = null;
  42. /**
  43. * Criterias
  44. * @var array
  45. */
  46. private $_criterias = null;
  47. /**
  48. * @var Relation
  49. */
  50. private $relation;
  51. /**
  52. * Public constructor
  53. *
  54. * @param array $config Global configuration
  55. * @param Relation $relation Relation instance
  56. */
  57. public function __construct(array $config, Relation $relation)
  58. {
  59. $this->setConfig($config);
  60. $this->relation = $relation;
  61. }
  62. /**
  63. * Setter of id
  64. *
  65. * @param int|null $searchId Id of search
  66. *
  67. * @return static
  68. */
  69. public function setId($searchId)
  70. {
  71. $searchId = (int) $searchId;
  72. if (empty($searchId)) {
  73. $searchId = null;
  74. }
  75. $this->_id = $searchId;
  76. return $this;
  77. }
  78. /**
  79. * Getter of id
  80. *
  81. * @return int|null
  82. */
  83. public function getId()
  84. {
  85. return $this->_id;
  86. }
  87. /**
  88. * Setter of searchName
  89. *
  90. * @param string $searchName Saved search name
  91. *
  92. * @return static
  93. */
  94. public function setSearchName($searchName)
  95. {
  96. $this->_searchName = $searchName;
  97. return $this;
  98. }
  99. /**
  100. * Getter of searchName
  101. *
  102. * @return string
  103. */
  104. public function getSearchName()
  105. {
  106. return $this->_searchName;
  107. }
  108. /**
  109. * Setter of config
  110. *
  111. * @param array $config Global configuration
  112. *
  113. * @return static
  114. */
  115. public function setConfig(array $config)
  116. {
  117. $this->_config = $config;
  118. return $this;
  119. }
  120. /**
  121. * Getter of config
  122. *
  123. * @return array
  124. */
  125. public function getConfig()
  126. {
  127. return $this->_config;
  128. }
  129. /**
  130. * Setter for criterias
  131. *
  132. * @param array|string $criterias Criterias of saved searches
  133. * @param bool $json Criterias are in JSON format
  134. *
  135. * @return static
  136. */
  137. public function setCriterias($criterias, $json = false)
  138. {
  139. if (true === $json && is_string($criterias)) {
  140. $this->_criterias = json_decode($criterias, true);
  141. return $this;
  142. }
  143. $aListFieldsToGet = [
  144. 'criteriaColumn',
  145. 'criteriaSort',
  146. 'criteriaShow',
  147. 'criteria',
  148. 'criteriaAndOrRow',
  149. 'criteriaAndOrColumn',
  150. 'rows',
  151. 'TableList',
  152. ];
  153. $data = [];
  154. $data['criteriaColumnCount'] = count($criterias['criteriaColumn']);
  155. foreach ($aListFieldsToGet as $field) {
  156. if (isset($criterias[$field])) {
  157. $data[$field] = $criterias[$field];
  158. }
  159. }
  160. /* Limit amount of rows */
  161. if (! isset($data['rows'])) {
  162. $data['rows'] = 0;
  163. } else {
  164. $data['rows'] = min(
  165. max(0, intval($data['rows'])),
  166. 100
  167. );
  168. }
  169. for ($i = 0; $i <= $data['rows']; $i++) {
  170. $data['Or' . $i] = $criterias['Or' . $i];
  171. }
  172. $this->_criterias = $data;
  173. return $this;
  174. }
  175. /**
  176. * Getter for criterias
  177. *
  178. * @return array
  179. */
  180. public function getCriterias()
  181. {
  182. return $this->_criterias;
  183. }
  184. /**
  185. * Setter for username
  186. *
  187. * @param string $username Username
  188. *
  189. * @return static
  190. */
  191. public function setUsername($username)
  192. {
  193. $this->_username = $username;
  194. return $this;
  195. }
  196. /**
  197. * Getter for username
  198. *
  199. * @return string
  200. */
  201. public function getUsername()
  202. {
  203. return $this->_username;
  204. }
  205. /**
  206. * Setter for DB name
  207. *
  208. * @param string $dbname DB name
  209. *
  210. * @return static
  211. */
  212. public function setDbname($dbname)
  213. {
  214. $this->_dbname = $dbname;
  215. return $this;
  216. }
  217. /**
  218. * Getter for DB name
  219. *
  220. * @return string
  221. */
  222. public function getDbname()
  223. {
  224. return $this->_dbname;
  225. }
  226. /**
  227. * Save the search
  228. *
  229. * @return boolean
  230. */
  231. public function save()
  232. {
  233. if (null == $this->getSearchName()) {
  234. $message = Message::error(
  235. __('Please provide a name for this bookmarked search.')
  236. );
  237. $response = Response::getInstance();
  238. $response->setRequestStatus($message->isSuccess());
  239. $response->addJSON('fieldWithError', 'searchName');
  240. $response->addJSON('message', $message);
  241. exit;
  242. }
  243. if (null == $this->getUsername()
  244. || null == $this->getDbname()
  245. || null == $this->getSearchName()
  246. || null == $this->getCriterias()
  247. ) {
  248. $message = Message::error(
  249. __('Missing information to save the bookmarked search.')
  250. );
  251. $response = Response::getInstance();
  252. $response->setRequestStatus($message->isSuccess());
  253. $response->addJSON('message', $message);
  254. exit;
  255. }
  256. $savedSearchesTbl
  257. = Util::backquote($this->_config['cfgRelation']['db']) . "."
  258. . Util::backquote($this->_config['cfgRelation']['savedsearches']);
  259. //If it's an insert.
  260. if (null === $this->getId()) {
  261. $wheres = [
  262. "search_name = '" . $GLOBALS['dbi']->escapeString($this->getSearchName())
  263. . "'",
  264. ];
  265. $existingSearches = $this->getList($wheres);
  266. if (! empty($existingSearches)) {
  267. $message = Message::error(
  268. __('An entry with this name already exists.')
  269. );
  270. $response = Response::getInstance();
  271. $response->setRequestStatus($message->isSuccess());
  272. $response->addJSON('fieldWithError', 'searchName');
  273. $response->addJSON('message', $message);
  274. exit;
  275. }
  276. $sqlQuery = "INSERT INTO " . $savedSearchesTbl
  277. . "(`username`, `db_name`, `search_name`, `search_data`)"
  278. . " VALUES ("
  279. . "'" . $GLOBALS['dbi']->escapeString($this->getUsername()) . "',"
  280. . "'" . $GLOBALS['dbi']->escapeString($this->getDbname()) . "',"
  281. . "'" . $GLOBALS['dbi']->escapeString($this->getSearchName()) . "',"
  282. . "'" . $GLOBALS['dbi']->escapeString(json_encode($this->getCriterias()))
  283. . "')";
  284. $result = (bool) $this->relation->queryAsControlUser($sqlQuery);
  285. if (! $result) {
  286. return false;
  287. }
  288. $this->setId($GLOBALS['dbi']->insertId());
  289. return true;
  290. }
  291. //Else, it's an update.
  292. $wheres = [
  293. "id != " . $this->getId(),
  294. "search_name = '" . $GLOBALS['dbi']->escapeString($this->getSearchName()) . "'",
  295. ];
  296. $existingSearches = $this->getList($wheres);
  297. if (! empty($existingSearches)) {
  298. $message = Message::error(
  299. __('An entry with this name already exists.')
  300. );
  301. $response = Response::getInstance();
  302. $response->setRequestStatus($message->isSuccess());
  303. $response->addJSON('fieldWithError', 'searchName');
  304. $response->addJSON('message', $message);
  305. exit;
  306. }
  307. $sqlQuery = "UPDATE " . $savedSearchesTbl
  308. . "SET `search_name` = '"
  309. . $GLOBALS['dbi']->escapeString($this->getSearchName()) . "', "
  310. . "`search_data` = '"
  311. . $GLOBALS['dbi']->escapeString(json_encode($this->getCriterias())) . "' "
  312. . "WHERE id = " . $this->getId();
  313. return (bool) $this->relation->queryAsControlUser($sqlQuery);
  314. }
  315. /**
  316. * Delete the search
  317. *
  318. * @return boolean
  319. */
  320. public function delete()
  321. {
  322. if (null == $this->getId()) {
  323. $message = Message::error(
  324. __('Missing information to delete the search.')
  325. );
  326. $response = Response::getInstance();
  327. $response->setRequestStatus($message->isSuccess());
  328. $response->addJSON('fieldWithError', 'searchId');
  329. $response->addJSON('message', $message);
  330. exit;
  331. }
  332. $savedSearchesTbl
  333. = Util::backquote($this->_config['cfgRelation']['db']) . "."
  334. . Util::backquote($this->_config['cfgRelation']['savedsearches']);
  335. $sqlQuery = "DELETE FROM " . $savedSearchesTbl
  336. . "WHERE id = '" . $GLOBALS['dbi']->escapeString($this->getId()) . "'";
  337. return (bool) $this->relation->queryAsControlUser($sqlQuery);
  338. }
  339. /**
  340. * Load the current search from an id.
  341. *
  342. * @return bool Success
  343. */
  344. public function load()
  345. {
  346. if (null == $this->getId()) {
  347. $message = Message::error(
  348. __('Missing information to load the search.')
  349. );
  350. $response = Response::getInstance();
  351. $response->setRequestStatus($message->isSuccess());
  352. $response->addJSON('fieldWithError', 'searchId');
  353. $response->addJSON('message', $message);
  354. exit;
  355. }
  356. $savedSearchesTbl = Util::backquote($this->_config['cfgRelation']['db'])
  357. . "."
  358. . Util::backquote($this->_config['cfgRelation']['savedsearches']);
  359. $sqlQuery = "SELECT id, search_name, search_data "
  360. . "FROM " . $savedSearchesTbl . " "
  361. . "WHERE id = '" . $GLOBALS['dbi']->escapeString($this->getId()) . "' ";
  362. $resList = $this->relation->queryAsControlUser($sqlQuery);
  363. if (false === ($oneResult = $GLOBALS['dbi']->fetchArray($resList))) {
  364. $message = Message::error(__('Error while loading the search.'));
  365. $response = Response::getInstance();
  366. $response->setRequestStatus($message->isSuccess());
  367. $response->addJSON('fieldWithError', 'searchId');
  368. $response->addJSON('message', $message);
  369. exit;
  370. }
  371. $this->setSearchName($oneResult['search_name'])
  372. ->setCriterias($oneResult['search_data'], true);
  373. return true;
  374. }
  375. /**
  376. * Get the list of saved searches of a user on a DB
  377. *
  378. * @param string[] $wheres List of filters
  379. *
  380. * @return array List of saved searches or empty array on failure
  381. */
  382. public function getList(array $wheres = [])
  383. {
  384. if (null == $this->getUsername()
  385. || null == $this->getDbname()
  386. ) {
  387. return [];
  388. }
  389. $savedSearchesTbl = Util::backquote($this->_config['cfgRelation']['db'])
  390. . "."
  391. . Util::backquote($this->_config['cfgRelation']['savedsearches']);
  392. $sqlQuery = "SELECT id, search_name "
  393. . "FROM " . $savedSearchesTbl . " "
  394. . "WHERE "
  395. . "username = '" . $GLOBALS['dbi']->escapeString($this->getUsername()) . "' "
  396. . "AND db_name = '" . $GLOBALS['dbi']->escapeString($this->getDbname()) . "' ";
  397. foreach ($wheres as $where) {
  398. $sqlQuery .= "AND " . $where . " ";
  399. }
  400. $sqlQuery .= "order by search_name ASC ";
  401. $resList = $this->relation->queryAsControlUser($sqlQuery);
  402. $list = [];
  403. while ($oneResult = $GLOBALS['dbi']->fetchArray($resList)) {
  404. $list[$oneResult['id']] = $oneResult['search_name'];
  405. }
  406. return $list;
  407. }
  408. }