Completed
Push — dedup ( c61919 )
by
unknown
05:54
created

ItemMapper::makeSelectQueryStatus()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 13
ccs 7
cts 7
cp 1
rs 9.4286
cc 1
eloc 9
nc 1
nop 5
crap 1
1
<?php
2
/**
3
 * ownCloud - News
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Alessandro Cosentino <[email protected]>
9
 * @author Bernhard Posselt <[email protected]>
10
 * @copyright Alessandro Cosentino 2012
11
 * @copyright Bernhard Posselt 2012, 2014
12
 */
13
14
namespace OCA\News\Db;
15
16
use OCP\IDBConnection;
17
18
19
class ItemMapper extends NewsMapper {
20
21 34
    public function __construct(IDBConnection $db){
22 34
        parent::__construct($db, 'news_items', Item::class);
23 34
    }
24
25
26 21
    private function makeSelectQuery($prependTo, $oldestFirst=false,
27
                                     $distinctFingerprint=false){
28 21
        if($oldestFirst) {
29 3
            $ordering = 'ASC';
30 3
        } else {
31 18
            $ordering = 'DESC';
32
        }
33
34
        return 'SELECT `items`.* FROM `*PREFIX*news_items` `items` '.
35 21
            'JOIN `*PREFIX*news_feeds` `feeds` ' .
36 21
                'ON `feeds`.`id` = `items`.`feed_id` '.
37 21
                'AND `feeds`.`deleted_at` = 0 ' .
38 21
                'AND `feeds`.`user_id` = ? ' .
39 21
                $prependTo .
40 21
            'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' .
41 21
                'ON `folders`.`id` = `feeds`.`folder_id` ' .
42 21
            'WHERE `feeds`.`folder_id` = 0 ' .
43 21
                'OR `folders`.`deleted_at` = 0 ' .
44 21
            'ORDER BY `items`.`id` ' . $ordering;
45
    }
46
47 18
    private function makeSelectQueryStatus($prependTo, $status,
48
                                           $oldestFirst=false, $search=[],
49
                                           $distinctFingerprint=false) {
50 18
        $status = (int) $status;
51 18
        $count = count($search);
52
53
        // WARNING: Potential SQL injection if you change this carelessly
54 18
        $sql = 'AND ((`items`.`status` & ' . $status . ') = ' . $status . ') ';
55 18
        $sql .= str_repeat('AND `items`.`search_index` LIKE ? ', $count);
56 18
        $sql .= $prependTo;
57
58 18
        return $this->makeSelectQuery($sql, $oldestFirst, $distinctFingerprint);
59
    }
60
61
	/**
62
	 * wrap and escape search parameters in a like statement
63
	 *
64
	 * @param string[] $search an array of strings that should be searched
65
	 * @return array with like parameters
66
	 */
67
    private function buildLikeParameters($search=[]) {
68 15
        return array_map(function ($param) {
69 3
            $param = addcslashes($param, '\\_%');
70 3
            return '%' . strtolower($param) . '%';
71 15
        }, $search);
72
    }
73
74 1
    public function find($id, $userId){
75 1
        $sql = $this->makeSelectQuery('AND `items`.`id` = ? ');
76 1
        return $this->findEntity($sql, [$userId, $id]);
77
    }
78
79 1
    public function starredCount($userId){
80
        $sql = 'SELECT COUNT(*) AS size FROM `*PREFIX*news_items` `items` '.
81 1
            'JOIN `*PREFIX*news_feeds` `feeds` ' .
82 1
                'ON `feeds`.`id` = `items`.`feed_id` '.
83 1
                'AND `feeds`.`deleted_at` = 0 ' .
84 1
                'AND `feeds`.`user_id` = ? ' .
85 1
                'AND ((`items`.`status` & ' . StatusFlag::STARRED . ') = ' .
86 1
                StatusFlag::STARRED . ')' .
87 1
            'LEFT OUTER JOIN `*PREFIX*news_folders` `folders` ' .
88 1
                'ON `folders`.`id` = `feeds`.`folder_id` ' .
89 1
            'WHERE `feeds`.`folder_id` = 0 ' .
90 1
                'OR `folders`.`deleted_at` = 0';
91
92 1
        $params = [$userId];
93
94 1
        $result = $this->execute($sql, $params)->fetch();
95
96 1
        return (int) $result['size'];
97
    }
98
99
100 1
    public function readAll($highestItemId, $time, $userId) {
101
        $sql = 'UPDATE `*PREFIX*news_items` ' .
102 1
            'SET `status` = `status` & ? ' .
103 1
            ', `last_modified` = ? ' .
104 1
            'WHERE `feed_id` IN (' .
105 1
                'SELECT `id` FROM `*PREFIX*news_feeds` ' .
106 1
                    'WHERE `user_id` = ? ' .
107 1
                ') '.
108 1
            'AND `id` <= ?';
109 1
        $params = [~StatusFlag::UNREAD, $time, $userId, $highestItemId];
110 1
        $this->execute($sql, $params);
111 1
    }
112
113
114 1
    public function readFolder($folderId, $highestItemId, $time, $userId) {
115
        $sql = 'UPDATE `*PREFIX*news_items` ' .
116 1
            'SET `status` = `status` & ? ' .
117 1
            ', `last_modified` = ? ' .
118 1
            'WHERE `feed_id` IN (' .
119 1
                'SELECT `id` FROM `*PREFIX*news_feeds` ' .
120 1
                    'WHERE `folder_id` = ? ' .
121 1
                    'AND `user_id` = ? ' .
122 1
                ') '.
123 1
            'AND `id` <= ?';
124 1
        $params = [~StatusFlag::UNREAD, $time, $folderId, $userId,
125 1
            $highestItemId];
126 1
        $this->execute($sql, $params);
127 1
    }
128
129
130
    public function readFeed($feedId, $highestItemId, $time, $userId){
131
        $sql = 'UPDATE `*PREFIX*news_items`
132
            SET `status` = `status` & ?,
133
                `last_modified` = ?
134
            WHERE `id` IN (
135
              SELECT `b`.`id` FROM `*PREFIX*news_items` `a`,
136
                                   `*PREFIX*news_items` `b`
137
              WHERE   `a`.`feed_id` = ?
138
                  AND `a`.`id` <= ?
139
                  AND `a`.`fingerprint` = `b`.`fingerprint`
140
                  AND `b`.`feed_id` IN (
141
                      SELECT `id` FROM `*PREFIX*news_feeds`
142
                      WHERE `user_id` = ?
143
                  )
144
            )';
145
        $params = [~StatusFlag::UNREAD, $time, $feedId, $highestItemId,
146
            $userId];
147
148
        $this->execute($sql, $params);
149
    }
150
151
    public function read($itemId, $time, $userId) {
152
        $sql = 'UPDATE `*PREFIX*news_items`
153
            SET `status` = `status` & ?,
154
                `last_modified` = ?
155
            WHERE `id` IN (
156
                SELECT `b`.`id` FROM `*PREFIX*news_items` `a`,
157
                                     `*PREFIX*news_items` `b`
158
                WHERE  `a`.`id` = ?
159
                    AND `a`.`fingerprint` = `b`.`fingerprint`
160
                    AND `b`.`feed_id` IN (
161
                        SELECT `id` FROM `*PREFIX*news_feeds`
162
                        WHERE `user_id` = ?
163
                    )
164
            )';
165
        $params = [~StatusFlag::UNREAD, $time, $itemId, $userId];
166
        $this->execute($sql, $params);
167
    }
168
169
170 12
    private function getOperator($oldestFirst) {
171 12
        if($oldestFirst) {
172 3
            return '>';
173
        } else {
174 9
            return '<';
175
        }
176
    }
177
178
179 1
    public function findAllNew($updatedSince, $status, $userId){
180 1
        $sql = $this->makeSelectQueryStatus(
181 1
            'AND `items`.`last_modified` >= ? ', $status);
182 1
        $params = [$userId, $updatedSince];
183 1
        return $this->findEntities($sql, $params);
184
    }
185
186
187 1
    public function findAllNewFolder($id, $updatedSince, $status, $userId){
188
        $sql = 'AND `feeds`.`folder_id` = ? ' .
189 1
                'AND `items`.`last_modified` >= ? ';
190 1
        $sql = $this->makeSelectQueryStatus($sql, $status);
191 1
        $params = [$userId, $id, $updatedSince];
192 1
        return $this->findEntities($sql, $params);
193
    }
194
195
196 1
    public function findAllNewFeed($id, $updatedSince, $status, $userId){
197
        $sql = 'AND `items`.`feed_id` = ? ' .
198 1
                'AND `items`.`last_modified` >= ? ';
199 1
        $sql = $this->makeSelectQueryStatus($sql, $status);
200 1
        $params = [$userId, $id, $updatedSince];
201 1
        return $this->findEntities($sql, $params);
202
    }
203
204
205 15
    private function findEntitiesIgnoringNegativeLimit($sql, $params, $limit) {
206
        // ignore limit if negative to offer a way to return all feeds
207 15
        if ($limit >= 0) {
208 12
            return $this->findEntities($sql, $params, $limit);
209
        } else {
210 3
            return $this->findEntities($sql, $params);
211
        }
212
    }
213
214
215 5
    public function findAllFeed($id, $limit, $offset, $status, $oldestFirst,
216
                                $userId, $search=[]){
217 5
        $params = [$userId];
218 5
        $params = array_merge($params, $this->buildLikeParameters($search));
219 5
        $params[] = $id;
220
221 5
        $sql = 'AND `items`.`feed_id` = ? ';
222 5
        if($offset !== 0){
223
            $sql .= 'AND `items`.`id` ' .
224 4
                $this->getOperator($oldestFirst) . ' ? ';
225 4
            $params[] = $offset;
226 4
        }
227 5
        $sql = $this->makeSelectQueryStatus($sql, $status, $oldestFirst,
228 5
                                            $search);
229 5
        return $this->findEntitiesIgnoringNegativeLimit($sql, $params, $limit);
230
    }
231
232
233 5
    public function findAllFolder($id, $limit, $offset, $status, $oldestFirst,
234
                                  $userId, $search=[]){
235 5
        $params = [$userId];
236 5
        $params = array_merge($params, $this->buildLikeParameters($search));
237 5
        $params[] = $id;
238
239 5
        $sql = 'AND `feeds`.`folder_id` = ? ';
240 5
        if($offset !== 0){
241
            $sql .= 'AND `items`.`id` ' .
242 4
                $this->getOperator($oldestFirst) . ' ? ';
243 4
            $params[] = $offset;
244 4
        }
245 5
        $sql = $this->makeSelectQueryStatus($sql, $status, $oldestFirst,
246 5
                                            $search);
247 5
        return $this->findEntitiesIgnoringNegativeLimit($sql, $params, $limit);
248
    }
249
250
251 5
    public function findAll($limit, $offset, $status, $oldestFirst, $userId,
252
                            $search=[]){
253 5
        $params = [$userId];
254 5
        $params = array_merge($params, $this->buildLikeParameters($search));
255 5
        $sql = '';
256 5
        if($offset !== 0){
257
            $sql .= 'AND `items`.`id` ' .
258 4
                $this->getOperator($oldestFirst) . ' ? ';
259 4
            $params[] = $offset;
260 4
        }
261 5
        $sql = $this->makeSelectQueryStatus($sql, $status, $oldestFirst,
262 5
                                            $search);
263
264 5
        return $this->findEntitiesIgnoringNegativeLimit($sql, $params, $limit);
265
    }
266
267
268 1
    public function findAllUnreadOrStarred($userId) {
269 1
        $params = [$userId];
270 1
        $status = StatusFlag::UNREAD | StatusFlag::STARRED;
271 1
        $sql = 'AND ((`items`.`status` & ' . $status . ') > 0) ';
272 1
        $sql = $this->makeSelectQuery($sql);
273 1
        return $this->findEntities($sql, $params);
274
    }
275
276
277 1
    public function findByGuidHash($guidHash, $feedId, $userId){
278 1
        $sql = $this->makeSelectQuery(
279
            'AND `items`.`guid_hash` = ? ' .
280 1
            'AND `feeds`.`id` = ? ');
281
282 1
        return $this->findEntity($sql, [$userId, $guidHash, $feedId]);
283
    }
284
285
286
    /**
287
     * Delete all items for feeds that have over $threshold unread and not
288
     * starred items
289
	 * @param int $threshold the number of items that should be deleted
290
     */
291 2
    public function deleteReadOlderThanThreshold($threshold){
292 2
        $status = StatusFlag::STARRED | StatusFlag::UNREAD;
293 2
        $params = [$status, $threshold];
294
295
        $sql = 'SELECT (COUNT(*) - `feeds`.`articles_per_update`) AS `size`, ' .
296 2
        '`feeds`.`id` AS `feed_id`, `feeds`.`articles_per_update` ' .
297 2
            'FROM `*PREFIX*news_items` `items` ' .
298 2
            'JOIN `*PREFIX*news_feeds` `feeds` ' .
299 2
                'ON `feeds`.`id` = `items`.`feed_id` ' .
300 2
                'AND NOT ((`items`.`status` & ?) > 0) ' .
301 2
            'GROUP BY `feeds`.`id`, `feeds`.`articles_per_update` ' .
302 2
            'HAVING COUNT(*) > ?';
303
304 2
        $result = $this->execute($sql, $params);
305
306 2
        while($row = $result->fetch()) {
307
308 2
            $size = (int) $row['size'];
309 2
            $limit = $size - $threshold;
310
311 2
            if($limit > 0) {
312 1
                $params = [$status, $row['feed_id'], $limit];
313
314
                $sql = 'DELETE FROM `*PREFIX*news_items` ' .
315 1
                'WHERE `id` IN (' .
316 1
                    'SELECT `id` FROM `*PREFIX*news_items` ' .
317 1
                    'WHERE NOT ((`status` & ?) > 0) ' .
318 1
                    'AND `feed_id` = ? ' .
319 1
                    'ORDER BY `id` ASC ' .
320 1
                    'LIMIT ?' .
321 1
                ')';
322
323 1
                $this->execute($sql, $params);
324 1
            }
325 2
        }
326
327 2
    }
328
329
330 2
    public function getNewestItemId($userId) {
331
        $sql = 'SELECT MAX(`items`.`id`) AS `max_id` ' .
332 2
            'FROM `*PREFIX*news_items` `items` '.
333 2
            'JOIN `*PREFIX*news_feeds` `feeds` ' .
334 2
                'ON `feeds`.`id` = `items`.`feed_id` '.
335 2
                'AND `feeds`.`user_id` = ?';
336 2
        $params = [$userId];
337
338 2
        $result = $this->findOneQuery($sql, $params);
339
340 1
        return (int) $result['max_id'];
341
    }
342
343
344
    /**
345
     * Deletes all items of a user
346
     * @param string $userId the name of the user
347
     */
348 1
    public function deleteUser($userId) {
349
        $sql = 'DELETE FROM `*PREFIX*news_items` ' .
350 1
            'WHERE `feed_id` IN (' .
351 1
                'SELECT `feeds`.`id` FROM `*PREFIX*news_feeds` `feeds` ' .
352 1
                    'WHERE `feeds`.`user_id` = ?' .
353 1
                ')';
354
355 1
        $this->execute($sql, [$userId]);
356 1
    }
357
358
359
    /**
360
     * Returns a list of ids and userid of all items
361
     */
362
    public function findAllItemIdsAndUsers() {
363
        $sql = 'SELECT `items`.`id`, `feeds`.`user_id` ' .
364
                'FROM `*PREFIX*news_items` `items` ' .
365
                'JOIN `*PREFIX*news_feeds` `feeds` ' .
366
                    'ON `items`.`feed_id` = `feeds`.`id`';
367
        return $this->execute($sql)->fetchAll();
368
    }
369
370
371
}
372