/src/daos/mysql/Sources.php

https://github.com/SSilence/selfoss · PHP · 307 lines · 163 code · 32 blank · 112 comment · 9 complexity · 5a673efea087a552525e6b56e2b49424 MD5 · raw file

  1. <?php
  2. namespace daos\mysql;
  3. use daos\DatabaseInterface;
  4. use helpers\Configuration;
  5. /**
  6. * Class for accessing persistent saved sources -- mysql
  7. *
  8. * @copyright Copyright (c) Tobias Zeising (http://www.aditu.de)
  9. * @license GPLv3 (https://www.gnu.org/licenses/gpl-3.0.html)
  10. * @author Tobias Zeising <tobias.zeising@aditu.de>
  11. */
  12. class Sources implements \daos\SourcesInterface {
  13. /** @var class-string SQL helper */
  14. protected static $stmt = Statements::class;
  15. /** @var Configuration configuration */
  16. private $configuration;
  17. /** @var DatabaseInterface database connection */
  18. protected $database;
  19. public function __construct(Configuration $configuration, DatabaseInterface $database) {
  20. $this->configuration = $configuration;
  21. $this->database = $database;
  22. }
  23. /**
  24. * add new source
  25. *
  26. * @param string $title
  27. * @param string[] $tags
  28. * @param string $filter
  29. * @param string $spout the source type
  30. * @param array $params depends from spout
  31. *
  32. * @return int new id
  33. */
  34. public function add($title, array $tags, $filter, $spout, array $params) {
  35. $stmt = static::$stmt;
  36. return $this->database->insert('INSERT INTO ' . $this->configuration->dbPrefix . 'sources (title, tags, filter, spout, params) VALUES (:title, :tags, :filter, :spout, :params)', [
  37. ':title' => trim($title),
  38. ':tags' => $stmt::csvRow($tags),
  39. ':filter' => $filter,
  40. ':spout' => $spout,
  41. ':params' => htmlentities(json_encode($params)),
  42. ]);
  43. }
  44. /**
  45. * edit source
  46. *
  47. * @param int $id the source id
  48. * @param string $title new title
  49. * @param string[] $tags new tags
  50. * @param string $filter
  51. * @param string $spout new spout
  52. * @param array $params the new params
  53. *
  54. * @return void
  55. */
  56. public function edit($id, $title, array $tags, $filter, $spout, array $params) {
  57. $stmt = static::$stmt;
  58. $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET title=:title, tags=:tags, filter=:filter, spout=:spout, params=:params WHERE id=:id', [
  59. ':title' => trim($title),
  60. ':tags' => $stmt::csvRow($tags),
  61. ':filter' => $filter,
  62. ':spout' => $spout,
  63. ':params' => htmlentities(json_encode($params)),
  64. ':id' => $id,
  65. ]);
  66. }
  67. /**
  68. * delete source
  69. *
  70. * @param int $id
  71. *
  72. * @return void
  73. */
  74. public function delete($id) {
  75. $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'sources WHERE id=:id', [':id' => $id]);
  76. // delete items of this source
  77. $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'items WHERE source=:id', [':id' => $id]);
  78. }
  79. /**
  80. * save error message
  81. *
  82. * @param int $id the source id
  83. * @param string $error error message
  84. *
  85. * @return void
  86. */
  87. public function error($id, $error) {
  88. if (strlen($error) === 0) {
  89. $arr = [
  90. ':id' => $id,
  91. ];
  92. $setarg = 'NULL';
  93. } else {
  94. $arr = [
  95. ':id' => $id,
  96. ':error' => $error,
  97. ];
  98. $setarg = ':error';
  99. }
  100. $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET error=' . $setarg . ' WHERE id=:id', $arr);
  101. }
  102. /**
  103. * sets the last updated timestamp
  104. *
  105. * @param int $id the source id
  106. * @param ?int $lastEntry timestamp of the newest item or NULL when no items were added
  107. *
  108. * @return void
  109. */
  110. public function saveLastUpdate($id, $lastEntry) {
  111. $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET lastupdate=:lastupdate WHERE id=:id',
  112. [
  113. ':id' => $id,
  114. ':lastupdate' => time(),
  115. ]);
  116. if ($lastEntry !== null) {
  117. $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET lastentry=:lastentry WHERE id=:id',
  118. [
  119. ':id' => $id,
  120. ':lastentry' => $lastEntry,
  121. ]);
  122. }
  123. }
  124. /**
  125. * returns all sources
  126. *
  127. * @return mixed all sources
  128. */
  129. public function getByLastUpdate() {
  130. $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . $this->configuration->dbPrefix . 'sources ORDER BY lastupdate ASC');
  131. return $ret;
  132. }
  133. /**
  134. * returns specified source (null if it doesnt exist)
  135. * or all sources if no id specified
  136. *
  137. * @param ?int $id specification of source id
  138. *
  139. * @return ?mixed specified source or all sources
  140. */
  141. public function get($id = null) {
  142. $stmt = static::$stmt;
  143. // select source by id if specified or return all sources
  144. if (isset($id)) {
  145. $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . $this->configuration->dbPrefix . 'sources WHERE id=:id', [':id' => $id]);
  146. $ret = $stmt::ensureRowTypes($ret, ['id' => DatabaseInterface::PARAM_INT]);
  147. if (isset($ret[0])) {
  148. $ret = $ret[0];
  149. } else {
  150. $ret = null;
  151. }
  152. } else {
  153. $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . $this->configuration->dbPrefix . 'sources ORDER BY error DESC, lower(title) ASC');
  154. $ret = $stmt::ensureRowTypes($ret, [
  155. 'id' => DatabaseInterface::PARAM_INT,
  156. 'tags' => DatabaseInterface::PARAM_CSV,
  157. ]);
  158. }
  159. return $ret;
  160. }
  161. /**
  162. * returns all sources including unread count
  163. *
  164. * @return mixed all sources
  165. */
  166. public function getWithUnread() {
  167. $stmt = static::$stmt;
  168. $ret = $this->database->exec('SELECT
  169. sources.id, sources.title, COUNT(items.id) AS unread
  170. FROM ' . $this->configuration->dbPrefix . 'sources AS sources
  171. LEFT OUTER JOIN ' . $this->configuration->dbPrefix . 'items AS items
  172. ON (items.source=sources.id AND ' . $stmt::isTrue('items.unread') . ')
  173. GROUP BY sources.id, sources.title
  174. ORDER BY lower(sources.title) ASC');
  175. return $stmt::ensureRowTypes($ret, [
  176. 'id' => DatabaseInterface::PARAM_INT,
  177. 'unread' => DatabaseInterface::PARAM_INT,
  178. ]);
  179. }
  180. /**
  181. * returns all sources including last icon
  182. *
  183. * @return mixed all sources
  184. */
  185. public function getWithIcon() {
  186. $stmt = static::$stmt;
  187. $ret = $this->database->exec('SELECT
  188. sources.id, sources.title, sources.tags, sources.spout,
  189. sources.params, sources.filter, sources.error, sources.lastentry,
  190. sourceicons.icon AS icon
  191. FROM ' . $this->configuration->dbPrefix . 'sources AS sources
  192. LEFT OUTER JOIN
  193. (SELECT items.source, icon
  194. FROM ' . $this->configuration->dbPrefix . 'items AS items,
  195. (SELECT source, MAX(id) as maxid
  196. FROM ' . $this->configuration->dbPrefix . 'items AS items
  197. WHERE icon IS NOT NULL AND icon != \'\'
  198. GROUP BY items.source) AS icons
  199. WHERE items.id=icons.maxid AND items.source=icons.source
  200. ) AS sourceicons
  201. ON sources.id=sourceicons.source
  202. ORDER BY ' . $stmt::nullFirst('sources.error', 'DESC') . ', lower(sources.title)');
  203. return $stmt::ensureRowTypes($ret, [
  204. 'id' => DatabaseInterface::PARAM_INT,
  205. 'tags' => DatabaseInterface::PARAM_CSV,
  206. ]);
  207. }
  208. /**
  209. * test if the value of a specified field is valid
  210. *
  211. * @param string $name
  212. * @param mixed $value
  213. *
  214. * @return bool
  215. */
  216. public function isValid($name, $value) {
  217. $return = false;
  218. switch ($name) {
  219. case 'id':
  220. $return = is_numeric($value);
  221. break;
  222. }
  223. return $return;
  224. }
  225. /**
  226. * returns all tags
  227. *
  228. * @return mixed all sources
  229. */
  230. public function getAllTags() {
  231. $result = $this->database->exec('SELECT tags FROM ' . $this->configuration->dbPrefix . 'sources');
  232. $tags = [];
  233. foreach ($result as $res) {
  234. $tags = array_merge($tags, explode(',', $res['tags']));
  235. }
  236. $tags = array_unique($tags);
  237. return $tags;
  238. }
  239. /**
  240. * returns tags of a source
  241. *
  242. * @param int $id
  243. *
  244. * @return mixed tags of a source
  245. */
  246. public function getTags($id) {
  247. $result = $this->database->exec('SELECT tags FROM ' . $this->configuration->dbPrefix . 'sources WHERE id=:id', [':id' => $id]);
  248. $tags = [];
  249. $tags = array_merge($tags, explode(',', $result[0]['tags']));
  250. $tags = array_unique($tags);
  251. return $tags;
  252. }
  253. /**
  254. * test if a source is already present using title, spout and params.
  255. * if present returns the id, else returns 0
  256. *
  257. * @param string $title
  258. * @param string $spout the source type
  259. * @param array $params depends from spout
  260. *
  261. * @return int id if any record is found
  262. */
  263. public function checkIfExists($title, $spout, array $params) {
  264. // Check if a entry exists with same title, spout and params
  265. $result = $this->database->exec('SELECT id FROM ' . $this->configuration->dbPrefix . 'sources WHERE title=:title AND spout=:spout AND params=:params', [
  266. ':title' => trim($title),
  267. ':spout' => $spout,
  268. ':params' => htmlentities(json_encode($params)),
  269. ]);
  270. if ($result) {
  271. return $result[0]['id'];
  272. }
  273. return 0;
  274. }
  275. }