PageRenderTime 25ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/libraries/classes/SavedSearches.php

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