1 | <?php |
||
20 | class ItemMapper extends NewsMapper { |
||
21 | |||
22 | 16 | public function __construct(IDBConnection $db){ |
|
25 | |||
26 | |||
27 | 11 | private function makeSelectQuery($prependTo='', $oldestFirst=false, |
|
28 | $distinctFingerprint=false){ |
||
29 | 11 | if($oldestFirst) { |
|
30 | $ordering = 'ASC'; |
||
31 | } else { |
||
32 | 11 | $ordering = 'DESC'; |
|
33 | } |
||
34 | |||
35 | return 'SELECT `items`.* FROM `*PREFIX*news_items` `items` '. |
||
36 | 11 | 'JOIN `*PREFIX*news_feeds` `feeds` ' . |
|
37 | 11 | 'ON `feeds`.`id` = `items`.`feed_id` '. |
|
38 | 11 | 'AND `feeds`.`deleted_at` = 0 ' . |
|
39 | 11 | 'AND `feeds`.`user_id` = ? ' . |
|
40 | 11 | $prependTo . |
|
41 | 11 | 'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' . |
|
42 | 11 | 'ON `folders`.`id` = `feeds`.`folder_id` ' . |
|
43 | 11 | 'WHERE `feeds`.`folder_id` = 0 ' . |
|
44 | 11 | 'OR `folders`.`deleted_at` = 0 ' . |
|
45 | 11 | 'ORDER BY `items`.`id` ' . $ordering; |
|
46 | } |
||
47 | |||
48 | 3 | private function makeSelectQueryStatus($prependTo, $status, |
|
49 | $oldestFirst=false, $search=[], |
||
50 | $distinctFingerprint=false) { |
||
51 | 3 | $status = (int) $status; |
|
52 | 3 | $count = count($search); |
|
53 | |||
54 | // WARNING: Potential SQL injection if you change this carelessly |
||
55 | 3 | $sql = 'AND ((`items`.`status` & ' . $status . ') = ' . $status . ') '; |
|
56 | 3 | $sql .= str_repeat('AND `items`.`search_index` LIKE ? ', $count); |
|
57 | 3 | $sql .= $prependTo; |
|
58 | |||
59 | 3 | return $this->makeSelectQuery($sql, $oldestFirst, $distinctFingerprint); |
|
60 | } |
||
61 | |||
62 | /** |
||
63 | * wrap and escape search parameters in a like statement |
||
64 | * |
||
65 | * @param string[] $search an array of strings that should be searched |
||
66 | * @return array with like parameters |
||
67 | */ |
||
68 | private function buildLikeParameters($search=[]) { |
||
69 | 3 | return array_map(function ($param) { |
|
70 | $param = addcslashes($param, '\\_%'); |
||
71 | return '%' . mb_strtolower($param, 'UTF-8') . '%'; |
||
72 | 3 | }, $search); |
|
73 | } |
||
74 | |||
75 | /** |
||
76 | * @param int $id |
||
77 | * @param string $userId |
||
78 | * @return \OCA\News\Db\Item |
||
79 | */ |
||
80 | 10 | public function find($id, $userId){ |
|
81 | 10 | $sql = $this->makeSelectQuery('AND `items`.`id` = ? '); |
|
82 | 10 | return $this->findEntity($sql, [$userId, $id]); |
|
83 | } |
||
84 | |||
85 | 1 | public function starredCount($userId){ |
|
104 | |||
105 | |||
106 | 1 | public function readAll($highestItemId, $time, $userId) { |
|
118 | |||
119 | |||
120 | 1 | public function readFolder($folderId, $highestItemId, $time, $userId) { |
|
134 | |||
135 | |||
136 | 1 | public function readFeed($feedId, $highestItemId, $time, $userId){ |
|
137 | $sql = 'UPDATE `*PREFIX*news_items` ' . |
||
138 | 1 | 'SET `status` = `status` & ? ' . |
|
139 | 1 | ', `last_modified` = ? ' . |
|
140 | 1 | 'WHERE `feed_id` = ? ' . |
|
141 | 1 | 'AND `id` <= ? ' . |
|
142 | 1 | 'AND EXISTS (' . |
|
143 | 1 | 'SELECT * FROM `*PREFIX*news_feeds` ' . |
|
144 | 1 | 'WHERE `user_id` = ? ' . |
|
145 | 1 | 'AND `id` = ? ) '; |
|
146 | 1 | $params = [~StatusFlag::UNREAD, $time, $feedId, $highestItemId, |
|
147 | 1 | $userId, $feedId]; |
|
148 | |||
149 | 1 | $this->execute($sql, $params); |
|
150 | 1 | } |
|
151 | |||
152 | |||
153 | private function getOperator($oldestFirst) { |
||
154 | if($oldestFirst) { |
||
155 | return '>'; |
||
156 | } else { |
||
157 | return '<'; |
||
158 | } |
||
159 | } |
||
160 | |||
161 | |||
162 | public function findAllNew($updatedSince, $status, $userId){ |
||
163 | $sql = $this->makeSelectQueryStatus( |
||
164 | 'AND `items`.`last_modified` >= ? ', $status); |
||
165 | $params = [$userId, $updatedSince]; |
||
166 | return $this->findEntities($sql, $params); |
||
167 | } |
||
168 | |||
169 | |||
170 | public function findAllNewFolder($id, $updatedSince, $status, $userId){ |
||
171 | $sql = 'AND `feeds`.`folder_id` = ? ' . |
||
172 | 'AND `items`.`last_modified` >= ? '; |
||
173 | $sql = $this->makeSelectQueryStatus($sql, $status); |
||
174 | $params = [$userId, $id, $updatedSince]; |
||
175 | return $this->findEntities($sql, $params); |
||
176 | } |
||
177 | |||
178 | |||
179 | public function findAllNewFeed($id, $updatedSince, $status, $userId){ |
||
180 | $sql = 'AND `items`.`feed_id` = ? ' . |
||
181 | 'AND `items`.`last_modified` >= ? '; |
||
182 | $sql = $this->makeSelectQueryStatus($sql, $status); |
||
183 | $params = [$userId, $id, $updatedSince]; |
||
184 | return $this->findEntities($sql, $params); |
||
185 | } |
||
186 | |||
187 | |||
188 | 3 | private function findEntitiesIgnoringNegativeLimit($sql, $params, $limit) { |
|
189 | // ignore limit if negative to offer a way to return all feeds |
||
190 | 3 | if ($limit >= 0) { |
|
191 | 3 | return $this->findEntities($sql, $params, $limit); |
|
192 | } else { |
||
193 | return $this->findEntities($sql, $params); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | |||
198 | public function findAllFeed($id, $limit, $offset, $status, $oldestFirst, |
||
199 | $userId, $search=[]){ |
||
200 | $params = [$userId]; |
||
201 | $params = array_merge($params, $this->buildLikeParameters($search)); |
||
202 | $params[] = $id; |
||
203 | |||
204 | $sql = 'AND `items`.`feed_id` = ? '; |
||
205 | if($offset !== 0){ |
||
206 | $sql .= 'AND `items`.`id` ' . |
||
207 | $this->getOperator($oldestFirst) . ' ? '; |
||
208 | $params[] = $offset; |
||
209 | } |
||
210 | $sql = $this->makeSelectQueryStatus($sql, $status, $oldestFirst, |
||
211 | $search); |
||
212 | return $this->findEntitiesIgnoringNegativeLimit($sql, $params, $limit); |
||
213 | } |
||
214 | |||
215 | |||
216 | public function findAllFolder($id, $limit, $offset, $status, $oldestFirst, |
||
217 | $userId, $search=[]){ |
||
218 | $params = [$userId]; |
||
219 | $params = array_merge($params, $this->buildLikeParameters($search)); |
||
220 | $params[] = $id; |
||
221 | |||
222 | $sql = 'AND `feeds`.`folder_id` = ? '; |
||
223 | if($offset !== 0){ |
||
224 | $sql .= 'AND `items`.`id` ' . |
||
225 | $this->getOperator($oldestFirst) . ' ? '; |
||
226 | $params[] = $offset; |
||
227 | } |
||
228 | $sql = $this->makeSelectQueryStatus($sql, $status, $oldestFirst, |
||
229 | $search); |
||
230 | return $this->findEntitiesIgnoringNegativeLimit($sql, $params, $limit); |
||
231 | } |
||
232 | |||
233 | |||
234 | 3 | public function findAll($limit, $offset, $status, $oldestFirst, $userId, |
|
235 | $search=[]){ |
||
236 | 3 | $params = [$userId]; |
|
237 | 3 | $params = array_merge($params, $this->buildLikeParameters($search)); |
|
238 | 3 | $sql = ''; |
|
239 | 3 | if($offset !== 0){ |
|
240 | $sql .= 'AND `items`.`id` ' . |
||
241 | $this->getOperator($oldestFirst) . ' ? '; |
||
242 | $params[] = $offset; |
||
243 | } |
||
244 | 3 | $sql = $this->makeSelectQueryStatus($sql, $status, $oldestFirst, |
|
245 | 3 | $search); |
|
246 | |||
247 | 3 | return $this->findEntitiesIgnoringNegativeLimit($sql, $params, $limit); |
|
248 | } |
||
249 | |||
250 | |||
251 | 1 | public function findAllUnreadOrStarred($userId) { |
|
258 | |||
259 | |||
260 | public function findByGuidHash($guidHash, $feedId, $userId){ |
||
261 | $sql = $this->makeSelectQuery( |
||
262 | 'AND `items`.`guid_hash` = ? ' . |
||
263 | 'AND `feeds`.`id` = ? '); |
||
264 | |||
265 | return $this->findEntity($sql, [$userId, $guidHash, $feedId]); |
||
266 | } |
||
267 | |||
268 | |||
269 | /** |
||
270 | * Delete all items for feeds that have over $threshold unread and not |
||
271 | * starred items |
||
272 | * @param int $threshold the number of items that should be deleted |
||
273 | */ |
||
274 | 2 | public function deleteReadOlderThanThreshold($threshold){ |
|
311 | |||
312 | |||
313 | 2 | public function getNewestItemId($userId) { |
|
325 | |||
326 | |||
327 | /** |
||
328 | * Deletes all items of a user |
||
329 | * @param string $userId the name of the user |
||
330 | */ |
||
331 | 1 | public function deleteUser($userId) { |
|
340 | |||
341 | |||
342 | /** |
||
343 | * Returns a list of ids and userid of all items |
||
344 | */ |
||
345 | public function findAllIds($limit=null, $offset=null) { |
||
346 | $sql = 'SELECT `id` FROM `*PREFIX*news_items`'; |
||
347 | return $this->execute($sql, [], $limit, $offset)->fetchAll(); |
||
348 | } |
||
349 | |||
350 | /** |
||
351 | * Update search indices of all items |
||
352 | */ |
||
353 | public function updateSearchIndices() { |
||
367 | |||
368 | private function updateSearchIndex(array $items=[]) { |
||
381 | |||
382 | 2 | public function readItem($itemId, $isRead, $lastModified, $userId) { |
|
383 | 2 | $item = $this->find($itemId, $userId); |
|
384 | |||
385 | // reading an item should set all of the same items as read, whereas |
||
386 | // marking an item as unread should only mark the selected instance |
||
387 | // as unread |
||
388 | 2 | if ($isRead) { |
|
389 | $sql = 'UPDATE `*PREFIX*news_items` |
||
390 | SET `status` = `status` & ?, |
||
391 | `last_modified` = ? |
||
392 | WHERE `fingerprint` = ? |
||
406 | |||
407 | } |
||
408 |