Feeds::catchupAll()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nop 0
dl 0
loc 6
rs 10
1
<?php
2
require_once "colors.php";
3
4
class Feeds extends Handler_Protected {
5
    const NEVER_GROUP_FEEDS = [ -6, 0];
6
    const NEVER_GROUP_BY_DATE = [ -2, -1, -3];
7
8
    private $params;
9
10
    function csrf_ignore($method) {
11
        $csrf_ignored = array("index", "quickaddfeed", "search");
12
13
        return array_search($method, $csrf_ignored) !== false;
14
    }
15
16
    private function format_headline_subtoolbar($feed_site_url, $feed_title,
17
            $feed_id, $is_cat, $search,
18
            $error, $feed_last_updated) {
19
20
        if ($is_cat) {
21
            $cat_q = "&is_cat=$is_cat";
22
        }
23
24
        if ($search) {
25
            $search_q = "&q=$search";
26
        } else {
27
            $search_q = "";
28
        }
29
30
        $reply = "";
31
32
        $rss_link = htmlspecialchars(get_self_url_prefix().
33
            "/public.php?op=rss&id=$feed_id$cat_q$search_q");
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cat_q does not seem to be defined for all execution paths leading up to this point.
Loading history...
34
35
        $reply .= "<span class='left'>";
36
37
        $reply .= "<a href=\"#\"
38
				title=\"".__("Show as feed")."\"
39
				onclick=\"App.displayDlg('".__("Show as feed")."','generatedFeed', '$feed_id:$is_cat:$rss_link')\">
40
				<i class='icon-syndicate material-icons'>rss_feed</i></a>";
41
42
        $reply .= "<span id='feed_title'>";
43
44
        if ($feed_site_url) {
45
            $last_updated = T_sprintf("Last updated: %s", $feed_last_updated);
46
47
            $reply .= "<a title=\"$last_updated\" target='_blank' href=\"$feed_site_url\">".
48
                truncate_string(strip_tags($feed_title), 30)."</a>";
49
        } else {
50
            $reply .= strip_tags($feed_title);
51
        }
52
53
        if ($error) {
54
                    $reply .= " <i title=\"".htmlspecialchars($error)."\" class='material-icons icon-error'>error</i>";
55
        }
56
57
        $reply .= "</span>";
58
        $reply .= "<span id='feed_current_unread' style='display: none'></span>";
59
        $reply .= "</span>";
60
61
        $reply .= "<span class=\"right\">";
62
        $reply .= "<span id='selected_prompt'></span>";
63
        $reply .= "&nbsp;";
64
        $reply .= "<select dojoType=\"fox.form.Select\"
65
			onchange=\"Headlines.onActionChanged(this)\">";
66
67
        $reply .= "<option value=\"0\" disabled='1'>".__('Select...')."</option>";
68
69
        $reply .= "<option value=\"Headlines.select('all')\">".__('All')."</option>";
70
        $reply .= "<option value=\"Headlines.select('unread')\">".__('Unread')."</option>";
71
        $reply .= "<option value=\"Headlines.select('invert')\">".__('Invert')."</option>";
72
        $reply .= "<option value=\"Headlines.select('none')\">".__('None')."</option>";
73
74
        $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>";
75
76
        $reply .= "<option value=\"Headlines.selectionToggleUnread()\">".__('Unread')."</option>
77
			<option value=\"Headlines.selectionToggleMarked()\">".__('Starred')."</option>
78
			<option value=\"Headlines.selectionTogglePublished()\">".__('Published')."</option>";
79
80
        $reply .= "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>";
81
82
        $reply .= "<option value=\"Headlines.catchupSelection()\">".__('Mark as read')."</option>";
83
        $reply .= "<option value=\"Article.selectionSetScore()\">".__('Set score')."</option>";
84
85
        if ($feed_id == 0 && !$is_cat) {
86
            $reply .= "<option value=\"Headlines.archiveSelection()\">".__('Move back')."</option>";
87
            $reply .= "<option value=\"Headlines.deleteSelection()\">".__('Delete')."</option>";
88
        } else {
89
            $reply .= "<option value=\"Headlines.archiveSelection()\">".__('Archive')."</option>";
90
        }
91
92
        if (PluginHost::getInstance()->get_plugin("mail")) {
93
            $reply .= "<option value=\"Plugins.Mail.send()\">".__('Forward by email').
94
                "</option>";
95
        }
96
97
        if (PluginHost::getInstance()->get_plugin("mailto")) {
98
            $reply .= "<option value=\"Plugins.Mailto.send()\">".__('Forward by email').
99
                "</option>";
100
        }
101
102
        $reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>";
103
104
        //$reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>";
105
106
        $reply .= "<option value=\"App.displayDlg('".__("Show as feed")."','generatedFeed', '$feed_id:$is_cat:$rss_link')\">".
107
            __('Show as feed')."</option>";
108
109
        $reply .= "</select>";
110
111
        //$reply .= "</h2";
112
113
        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HEADLINE_TOOLBAR_BUTTON) as $p) {
114
                $reply .= $p->hook_headline_toolbar_button($feed_id, $is_cat);
115
        }
116
117
        $reply .= "</span>";
118
119
        return $reply;
120
    }
121
122
    private function format_headlines_list($feed, $method, $view_mode, $limit, $cat_view,
123
                    $offset, $override_order = false, $include_children = false, $check_first_id = false,
124
                    $skip_first_id_check = false, $order_by = false) {
125
126
        $disable_cache = false;
127
128
        $reply = array();
129
130
        $rgba_cache = array();
131
        $topmost_article_ids = array();
132
133
        if (!$offset) {
134
            $offset = 0;
135
        }
136
        if ($method == "undefined") {
137
            $method = "";
138
        }
139
140
        $method_split = explode(":", $method);
141
142
        if ($method == "ForceUpdate" && $feed > 0 && is_numeric($feed)) {
143
            $sth = $this->pdo->prepare("UPDATE ttrss_feeds
144
                            SET last_updated = '1970-01-01', last_update_started = '1970-01-01'
145
                            WHERE id = ?");
146
            $sth->execute([$feed]);
147
        }
148
149
        if ($method_split[0] == "MarkAllReadGR") {
150
            $this->catchup_feed($method_split[1], false);
151
        }
152
153
        // FIXME: might break tag display?
154
155
        if (is_numeric($feed) && $feed > 0 && !$cat_view) {
156
            $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? LIMIT 1");
157
            $sth->execute([$feed]);
158
159
            if (!$sth->fetch()) {
160
                $reply['content'] = "<div align='center'>".__('Feed not found.')."</div>";
161
            }
162
        }
163
164
        @$search = $_REQUEST["query"];
165
        @$search_language = $_REQUEST["search_language"]; // PGSQL only
166
167
        if ($search) {
168
            $disable_cache = true;
169
        }
170
171
        if (!$cat_view && is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) {
172
            $handler = PluginHost::getInstance()->get_feed_handler(
173
                PluginHost::feed_to_pfeed_id($feed));
174
175
            if ($handler) {
176
                $options = array(
177
                    "limit" => $limit,
178
                    "view_mode" => $view_mode,
179
                    "cat_view" => $cat_view,
180
                    "search" => $search,
181
                    "override_order" => $override_order,
182
                    "offset" => $offset,
183
                    "owner_uid" => $_SESSION["uid"],
184
                    "filter" => false,
185
                    "since_id" => 0,
186
                    "include_children" => $include_children,
187
                    "order_by" => $order_by);
188
189
                $qfh_ret = $handler->get_headlines(PluginHost::feed_to_pfeed_id($feed),
190
                    $options);
191
            }
192
193
        } else {
194
195
            $params = array(
196
                "feed" => $feed,
197
                "limit" => $limit,
198
                "view_mode" => $view_mode,
199
                "cat_view" => $cat_view,
200
                "search" => $search,
201
                "search_language" => $search_language,
202
                "override_order" => $override_order,
203
                "offset" => $offset,
204
                "include_children" => $include_children,
205
                "check_first_id" => $check_first_id,
206
                "skip_first_id_check" => $skip_first_id_check,
207
                "order_by" => $order_by
208
            );
209
210
            $qfh_ret = $this->queryFeedHeadlines($params);
211
        }
212
213
        $vfeed_group_enabled = get_pref("VFEED_GROUP_BY_FEED") &&
214
            !(in_array($feed, Feeds::NEVER_GROUP_FEEDS) && !$cat_view);
215
216
        $result = $qfh_ret[0]; // this could be either a PDO query result or a -1 if first id changed
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $qfh_ret does not seem to be defined for all execution paths leading up to this point.
Loading history...
217
        $feed_title = $qfh_ret[1];
218
        $feed_site_url = $qfh_ret[2];
219
        $last_error = $qfh_ret[3];
220
        $last_updated = strpos($qfh_ret[4], '1970-') === false ?
221
            make_local_datetime($qfh_ret[4], false) : __("Never");
222
        $highlight_words = $qfh_ret[5];
223
        $reply['first_id'] = $qfh_ret[6];
224
        $reply['is_vfeed'] = $qfh_ret[7];
225
        $query_error_override = $qfh_ret[8];
226
227
        $reply['search_query'] = [$search, $search_language];
228
        $reply['vfeed_group_enabled'] = $vfeed_group_enabled;
229
230
        $reply['toolbar'] = $this->format_headline_subtoolbar($feed_site_url,
231
            $feed_title,
232
            $feed, $cat_view, $search,
233
            $last_error, $last_updated);
234
235
        if ($offset == 0) {
236
            foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HEADLINES_BEFORE) as $p) {
237
                    $reply['content'] .= $p->hook_headlines_before($feed, $cat_view, $qfh_ret);
238
            }
239
        }
240
241
        $reply['content'] = [];
242
243
        $headlines_count = 0;
244
245
        if (is_object($result)) {
246
            while ($line = $result->fetch(PDO::FETCH_ASSOC)) {
247
248
                ++$headlines_count;
249
250
                if (!get_pref('SHOW_CONTENT_PREVIEW')) {
251
                    $line["content_preview"] = "";
252
                } else {
253
                    $line["content_preview"] = "&mdash; ".truncate_string(strip_tags($line["content"]), 250);
254
255
                    foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
256
                        $line = $p->hook_query_headlines($line, 250, false);
257
                    }
258
                }
259
260
                $id = $line["id"];
261
262
                // frontend doesn't expect pdo returning booleans as strings on mysql
263
                if (DB_TYPE == "mysql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
264
                    foreach (["unread", "marked", "published"] as $k) {
265
                        $line[$k] = $line[$k] === "1";
266
                    }
267
                }
268
269
                // normalize archived feed
270
                if ($line['feed_id'] === null) {
271
                    $line['feed_id'] = 0;
272
                    $line["feed_title"] = __("Archived articles");
273
                }
274
275
                $feed_id = $line["feed_id"];
276
277
                $label_cache = $line["label_cache"];
278
                $labels = false;
279
280
                if ($label_cache) {
281
                    $label_cache = json_decode($label_cache, true);
282
283
                    if ($label_cache) {
284
                        if ($label_cache["no-labels"] == 1) {
285
                                                    $labels = array();
286
                        } else {
287
                                                    $labels = $label_cache;
288
                        }
289
                    }
290
                }
291
292
                if (!is_array($labels)) {
293
                    $labels = Article::get_article_labels($id);
294
                }
295
296
                $labels_str = "<span class=\"HLLCTR-$id\">";
297
                $labels_str .= Article::format_article_labels($labels);
298
                $labels_str .= "</span>";
299
300
                $line["labels"] = $labels_str;
301
302
                if (count($topmost_article_ids) < 3) {
303
                    array_push($topmost_article_ids, $id);
304
                }
305
306
                if (!$line["feed_title"]) {
307
                    $line["feed_title"] = "";
308
                }
309
310
                $line["buttons_left"] = "";
311
                foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) {
312
                    $line["buttons_left"] .= $p->hook_article_left_button($line);
313
                }
314
315
                $line["buttons"] = "";
316
                foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) {
317
                    $line["buttons"] .= $p->hook_article_button($line);
318
                }
319
320
                $line["content"] = sanitize($line["content"],
321
                    $line['hide_images'], false, $line["site_url"], $highlight_words, $line["id"]);
322
323
                foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) {
324
                    $line = $p->hook_render_article_cdm($line);
325
                }
326
327
                $line['content'] = DiskCache::rewriteUrls($line['content']);
328
329
                if ($line['note']) {
330
                                    $line['note'] = Article::format_article_note($id, $line['note']);
331
                } else {
332
                                    $line['note'] = "";
333
                }
334
335
                if (!get_pref("CDM_EXPANDED")) {
336
                    $line["cdm_excerpt"] = "<span class='collapse'>
337
                        <i class='material-icons' onclick='return Article.cdmUnsetActive(event)'
338
                            title=\"" . __("Collapse article")."\">remove_circle</i></span>";
339
340
                    if (get_pref('SHOW_CONTENT_PREVIEW')) {
341
                        $line["cdm_excerpt"] .= "<span class='excerpt'>".$line["content_preview"]."</span>";
342
                    }
343
                }
344
345
                $line["enclosures"] = Article::format_article_enclosures($id, $line["always_display_enclosures"],
346
                    $line["content"], $line["hide_images"]);
347
348
                if ($line["orig_feed_id"]) {
349
350
                    $ofgh = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds
351
                    WHERE id = ? AND owner_uid = ?");
352
                    $ofgh->execute([$line["orig_feed_id"], $_SESSION['uid']]);
353
354
                    if ($tmp_line = $ofgh->fetch()) {
355
                        $line["orig_feed"] = [$tmp_line["title"], $tmp_line["site_url"], $tmp_line["feed_url"]];
356
                    }
357
                }
358
359
                $line["updated_long"] = make_local_datetime($line["updated"], true);
360
                $line["updated"] = make_local_datetime($line["updated"], false, false, false, true);
361
362
363
                $line['imported'] = T_sprintf("Imported at %s",
364
                    make_local_datetime($line["date_entered"], false));
365
366
                if ($line["tag_cache"]) {
367
                                    $tags = explode(",", $line["tag_cache"]);
368
                } else {
369
                                    $tags = false;
370
                }
371
372
                $line["tags_str"] = Article::format_tags_string($tags);
373
374
                if (feeds::feedHasIcon($feed_id)) {
375
                    $line['feed_icon'] = "<img class=\"icon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">";
0 ignored issues
show
Bug introduced by
The constant ICONS_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
376
                } else {
377
                    $line['feed_icon'] = "<i class='icon-no-feed material-icons'>rss_feed</i>";
378
                }
379
380
                //setting feed headline background color, needs to change text color based on dark/light
381
                $fav_color = $line['favicon_avg_color'];
382
383
                require_once "colors.php";
384
385
                if (!isset($rgba_cache[$feed_id])) {
386
                    if ($fav_color && $fav_color != 'fail') {
387
                        $rgba_cache[$feed_id] = _color_unpack($fav_color);
388
                    } else {
389
                        $rgba_cache[$feed_id] = _color_unpack($this->color_of($line['feed_title']));
390
                    }
391
                }
392
393
                if (isset($rgba_cache[$feed_id])) {
394
                    $line['feed_bg_color'] = 'rgba('.implode(",", $rgba_cache[$feed_id]).',0.3)';
395
                }
396
397
                /* we don't need those */
398
399
                foreach (["date_entered", "guid", "last_published", "last_marked", "tag_cache", "favicon_avg_color",
400
                                "uuid", "label_cache", "yyiw"] as $k) {
401
                                    unset($line[$k]);
402
                }
403
404
                array_push($reply['content'], $line);
405
            }
406
        }
407
408
        if (!$headlines_count) {
409
410
            if (is_object($result)) {
411
412
                if ($query_error_override) {
413
                    $message = $query_error_override;
414
                } else {
415
                    switch ($view_mode) {
416
                    case "unread":
417
                        $message = __("No unread articles found to display.");
418
                        break;
419
                    case "updated":
420
                        $message = __("No updated articles found to display.");
421
                        break;
422
                    case "marked":
423
                        $message = __("No starred articles found to display.");
424
                        break;
425
                    default:
426
                        if ($feed < LABEL_BASE_INDEX) {
427
                            $message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter.");
428
                        } else {
429
                            $message = __("No articles found to display.");
430
                        }
431
                    }
432
                }
433
434
                if (!$offset && $message) {
435
                    $reply['content'] = "<div class='whiteBox'>$message";
436
437
                    $reply['content'] .= "<p><span class=\"text-muted\">";
438
439
                    $sth = $this->pdo->prepare("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
440
                        WHERE owner_uid = ?");
441
                    $sth->execute([$_SESSION['uid']]);
442
                    $row = $sth->fetch();
443
444
                    $last_updated = make_local_datetime($row["last_updated"], false);
445
446
                    $reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
447
448
                    $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors
449
                        FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?");
450
                    $sth->execute([$_SESSION['uid']]);
451
                    $row = $sth->fetch();
452
453
                    $num_errors = $row["num_errors"];
454
455
                    if ($num_errors > 0) {
456
                        $reply['content'] .= "<br/>";
457
                        $reply['content'] .= "<a class=\"text-muted\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">".
458
                            __('Some feeds have update errors (click for details)')."</a>";
459
                    }
460
                    $reply['content'] .= "</span></p></div>";
461
462
                }
463
            } else if (is_numeric($result) && $result == -1) {
464
                $reply['first_id_changed'] = true;
465
            }
466
        }
467
468
        return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply);
469
    }
470
471
    public function catchupAll() {
472
        $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
473
						last_read = NOW(), unread = false WHERE unread = true AND owner_uid = ?");
474
        $sth->execute([$_SESSION['uid']]);
475
476
        CCache::zero_all($_SESSION["uid"]);
477
    }
478
479
    public function view() {
480
        $reply = array();
481
482
        $feed = $_REQUEST["feed"];
483
        $method = $_REQUEST["m"];
484
        $view_mode = $_REQUEST["view_mode"];
485
        $limit = 30;
486
        @$cat_view = $_REQUEST["cat"] == "true";
487
        @$next_unread_feed = $_REQUEST["nuf"];
488
        @$offset = $_REQUEST["skip"];
489
        $order_by = $_REQUEST["order_by"];
490
        $check_first_id = $_REQUEST["fid"];
491
492
        if (is_numeric($feed)) {
493
            $feed = (int) $feed;
494
        }
495
496
        /* Feed -5 is a special case: it is used to display auxiliary information
497
		 * when there's nothing to load - e.g. no stuff in fresh feed */
498
499
        if ($feed == -5) {
500
            print json_encode($this->generate_dashboard_feed());
501
            return;
502
        }
503
504
        $sth = false;
505
        if ($feed < LABEL_BASE_INDEX) {
506
507
            $label_feed = Labels::feed_to_label_id($feed);
508
509
            $sth = $this->pdo->prepare("SELECT id FROM ttrss_labels2 WHERE
510
							id = ? AND owner_uid = ?");
511
            $sth->execute([$label_feed, $_SESSION['uid']]);
512
513
        } else if (!$cat_view && is_numeric($feed) && $feed > 0) {
514
515
            $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE
516
							id = ? AND owner_uid = ?");
517
            $sth->execute([$feed, $_SESSION['uid']]);
518
519
        } else if ($cat_view && is_numeric($feed) && $feed > 0) {
520
521
            $sth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories WHERE
522
							id = ? AND owner_uid = ?");
523
524
            $sth->execute([$feed, $_SESSION['uid']]);
525
        }
526
527
        if ($sth && !$sth->fetch()) {
528
            print json_encode($this->generate_error_feed(__("Feed not found.")));
529
            return;
530
        }
531
532
        /* Updating a label ccache means recalculating all of the caches
533
		 * so for performance reasons we don't do that here */
534
535
        if ($feed >= 0) {
536
            CCache::update($feed, $_SESSION["uid"], $cat_view);
537
        }
538
539
        set_pref("_DEFAULT_VIEW_MODE", $view_mode);
540
        set_pref("_DEFAULT_VIEW_ORDER_BY", $order_by);
541
542
        /* bump login timestamp if needed */
543
        if (time() - $_SESSION["last_login_update"] > 3600) {
544
            $sth = $this->pdo->prepare("UPDATE ttrss_users SET last_login = NOW() WHERE id = ?");
545
            $sth->execute([$_SESSION['uid']]);
546
547
            $_SESSION["last_login_update"] = time();
548
        }
549
550
        if (!$cat_view && is_numeric($feed) && $feed > 0) {
551
            $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET last_viewed = NOW()
552
							WHERE id = ? AND owner_uid = ?");
553
            $sth->execute([$feed, $_SESSION['uid']]);
554
        }
555
556
        $reply['headlines'] = [];
557
558
        $override_order = false;
559
        $skip_first_id_check = false;
560
561
        switch ($order_by) {
562
        case "title":
563
            $override_order = "ttrss_entries.title, date_entered, updated";
564
            break;
565
        case "date_reverse":
566
            $override_order = "score DESC, date_entered, updated";
567
            $skip_first_id_check = true;
568
            break;
569
        case "feed_dates":
570
            $override_order = "updated DESC";
571
            break;
572
        }
573
574
        $ret = $this->format_headlines_list($feed, $method,
575
            $view_mode, $limit, $cat_view, $offset,
576
            $override_order, true, $check_first_id, $skip_first_id_check, $order_by);
577
578
        $headlines_count = $ret[1];
579
        $disable_cache = $ret[3];
580
        $reply['headlines'] = $ret[4];
581
582
        if (!$next_unread_feed) {
583
                    $reply['headlines']['id'] = $feed;
584
        } else {
585
                    $reply['headlines']['id'] = $next_unread_feed;
586
        }
587
588
        $reply['headlines']['is_cat'] = (bool) $cat_view;
589
590
        $reply['headlines-info'] = ["count" => (int) $headlines_count,
591
                                    "disable_cache" => (bool) $disable_cache];
592
593
        // this is parsed by handleRpcJson() on first viewfeed() to set cdm expanded, etc
594
        $reply['runtime-info'] = make_runtime_info();
595
596
        $reply_json = json_encode($reply);
597
598
        if (!$reply_json) {
599
            $reply_json = json_encode(["error" => ["code" => 15,
600
                "message" => json_last_error_msg()]]);
601
        }
602
603
        print $reply_json;
604
605
    }
606
607
    private function generate_dashboard_feed() {
608
        $reply = array();
609
610
        $reply['headlines']['id'] = -5;
611
        $reply['headlines']['is_cat'] = false;
612
613
        $reply['headlines']['toolbar'] = '';
614
615
        $reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.');
616
617
        $reply['headlines']['content'] .= "<p><span class=\"text-muted\">";
618
619
        $sth = $this->pdo->prepare("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds
620
			WHERE owner_uid = ?");
621
        $sth->execute([$_SESSION['uid']]);
622
        $row = $sth->fetch();
623
624
        $last_updated = make_local_datetime($row["last_updated"], false);
625
626
        $reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated);
627
628
        $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors
629
			FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?");
630
        $sth->execute([$_SESSION['uid']]);
631
        $row = $sth->fetch();
632
633
        $num_errors = $row["num_errors"];
634
635
        if ($num_errors > 0) {
636
            $reply['headlines']['content'] .= "<br/>";
637
            $reply['headlines']['content'] .= "<a class=\"text-muted\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">".
638
                __('Some feeds have update errors (click for details)')."</a>";
639
        }
640
        $reply['headlines']['content'] .= "</span></p>";
641
642
        $reply['headlines-info'] = array("count" => 0,
643
            "unread" => 0,
644
            "disable_cache" => true);
645
646
        return $reply;
647
    }
648
649
    private function generate_error_feed($error) {
650
        $reply = array();
651
652
        $reply['headlines']['id'] = -7;
653
        $reply['headlines']['is_cat'] = false;
654
655
        $reply['headlines']['toolbar'] = '';
656
        $reply['headlines']['content'] = "<div class='whiteBox'>".$error."</div>";
657
658
        $reply['headlines-info'] = array("count" => 0,
659
            "unread" => 0,
660
            "disable_cache" => true);
661
662
        return $reply;
663
    }
664
665
    public function quickAddFeed() {
666
        print "<form onsubmit='return false'>";
667
668
        print_hidden("op", "rpc");
669
        print_hidden("method", "addfeed");
670
671
        print "<div id='fadd_error_message' style='display : none' class='alert alert-danger'></div>";
672
673
        print "<div id='fadd_multiple_notify' style='display : none'>";
674
        print_notice("Provided URL is a HTML page referencing multiple feeds, please select required feed from the dropdown menu below.");
0 ignored issues
show
Unused Code introduced by
The call to print_notice() has too many arguments starting with 'Provided URL is a HTML ...e dropdown menu below.'. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

674
        /** @scrutinizer ignore-call */ 
675
        print_notice("Provided URL is a HTML page referencing multiple feeds, please select required feed from the dropdown menu below.");

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Deprecated Code introduced by
The function print_notice() has been deprecated: Use twig function noticeMessage ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

674
        /** @scrutinizer ignore-deprecated */ print_notice("Provided URL is a HTML page referencing multiple feeds, please select required feed from the dropdown menu below.");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
675
        print "<p></div>";
676
677
        print "<section>";
678
679
        print "<fieldset>";
680
        print "<div style='float : right'><img style='display : none' id='feed_add_spinner' src='images/indicator_white.gif'></div>";
681
        print "<input style='font-size : 16px; width : 500px;'
682
			placeHolder=\"".__("Feed or site URL")."\"
683
			dojoType='dijit.form.ValidationTextBox' required='1' name='feed' id='feedDlg_feedUrl'>";
684
685
        print "</fieldset>";
686
687
        print "<fieldset>";
688
689
        if (get_pref('ENABLE_FEED_CATS')) {
690
            print "<label class='inline'>".__('Place in category:')."</label> ";
691
            print_feed_cat_select("cat", false, 'dojoType="fox.form.Select"');
692
        }
693
694
        print "</fieldset>";
695
696
        print "</section>";
697
698
        print '<div id="feedDlg_feedsContainer" style="display : none">
699
				<header>' . __('Available feeds').'</header>
700
				<section>
701
					<fieldset>
702
						<select id="feedDlg_feedContainerSelect"
703
							dojoType="fox.form.Select" size="3">
704
							<script type="dojo/method" event="onChange" args="value">
705
								dijit.byId("feedDlg_feedUrl").attr("value", value);
706
							</script>
707
						</select>
708
					</fieldset>
709
				</section>
710
			</div>';
711
712
        print "<div id='feedDlg_loginContainer' style='display : none'>
713
				<section>
714
				<fieldset>
715
					<input dojoType=\"dijit.form.TextBox\" name='login'\"
716
						placeHolder=\"".__("Login")."\"
717
						autocomplete=\"new-password\"
718
						style=\"width : 10em;\">
719
					<input
720
						placeHolder=\"".__("Password")."\"
721
						dojoType=\"dijit.form.TextBox\" type='password'
722
						autocomplete=\"new-password\"
723
						style=\"width : 10em;\" name='pass'\">
724
				</fieldset>
725
				</section>
726
			</div>";
727
728
        print "<section>";
729
        print "<label>
730
			<label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox' id='feedDlg_loginCheck'
731
					onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'>
732
				".__('This feed requires authentication.')."</label>";
733
        print "</section>";
734
735
        print "<footer>";
736
        print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit'
737
				onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>";
738
739
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button>";
740
        print "</footer>";
741
742
        print "</form>";
743
    }
744
745
    public function search() {
746
        $this->params = explode(":", $_REQUEST["param"], 2);
747
748
        $active_feed_id = sprintf("%d", $this->params[0]);
749
        $is_cat = $this->params[1] != "false";
750
751
        print "<form onsubmit='return false;'>";
752
753
        print "<section>";
754
755
        print "<fieldset>";
756
        print "<input dojoType='dijit.form.ValidationTextBox' id='search_query'
757
			style='font-size : 16px; width : 540px;'
758
			placeHolder=\"".T_sprintf("Search %s...", $this->getFeedTitle($active_feed_id, $is_cat))."\"
759
			name='query' type='search' value=''>";
760
        print "</fieldset>";
761
762
        if (DB_TYPE == "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
763
            print "<fieldset>";
764
            print "<label class='inline'>".__("Language:")."</label>";
765
            print_select("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(),
766
                "dojoType='fox.form.Select' title=\"".__('Used for word stemming')."\"");
767
            print "</fieldset>";
768
        }
769
770
        print "</section>";
771
772
        print "<footer>";
773
774
        if (count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0) {
775
            print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/SearchSyntax\")'>
776
				<i class='material-icons'>help</i> ".__("Search syntax")."</button>";
777
        }
778
779
        print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"dijit.byId('searchDlg').execute()\">".__('Search')."</button>
780
			<button dojoType='dijit.form.Button' onclick=\"dijit.byId('searchDlg').hide()\">".__('Cancel')."</button>";
781
782
        print "</footer>";
783
784
        print "</form>";
785
    }
786
787
    public function update_debugger() {
788
        header("Content-type: text/html");
789
790
        Debug::set_enabled(true);
791
        Debug::set_loglevel($_REQUEST["xdebug"]);
792
793
        $feed_id = (int) $_REQUEST["feed_id"];
794
        @$do_update = $_REQUEST["action"] == "do_update";
795
        $csrf_token = $_REQUEST["csrf_token"];
796
797
        $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?");
798
        $sth->execute([$feed_id, $_SESSION['uid']]);
799
800
        if (!$sth->fetch()) {
801
            print "Access denied.";
802
            return;
803
        }
804
805
        $refetch_checked = isset($_REQUEST["force_refetch"]) ? "checked" : "";
806
        $rehash_checked = isset($_REQUEST["force_rehash"]) ? "checked" : "";
807
808
        ?>
809
		<!DOCTYPE html>
810
		<html>
811
		<head>
812
			<?php echo stylesheet_tag("css/default.css") ?>
0 ignored issues
show
Bug introduced by
Are you sure the usage of stylesheet_tag('css/default.css') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function stylesheet_tag() has been deprecated: Use Twig filter cssTag ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

812
			<?php echo /** @scrutinizer ignore-deprecated */ stylesheet_tag("css/default.css") ?>

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
813
			<title>Feed Debugger</title>
814
			<?php
815
                echo stylesheet_tag("css/default.css");
0 ignored issues
show
Deprecated Code introduced by
The function stylesheet_tag() has been deprecated: Use Twig filter cssTag ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

815
                echo /** @scrutinizer ignore-deprecated */ stylesheet_tag("css/default.css");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Bug introduced by
Are you sure the usage of stylesheet_tag('css/default.css') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
816
                echo javascript_tag("lib/prototype.js");
0 ignored issues
show
Bug introduced by
Are you sure the usage of javascript_tag('lib/prototype.js') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function javascript_tag() has been deprecated: Use Twig filter jsTag ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

816
                echo /** @scrutinizer ignore-deprecated */ javascript_tag("lib/prototype.js");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
817
                echo javascript_tag("lib/dojo/dojo.js");
0 ignored issues
show
Bug introduced by
Are you sure the usage of javascript_tag('lib/dojo/dojo.js') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Deprecated Code introduced by
The function javascript_tag() has been deprecated: Use Twig filter jsTag ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

817
                echo /** @scrutinizer ignore-deprecated */ javascript_tag("lib/dojo/dojo.js");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
818
                echo javascript_tag("lib/dojo/tt-rss-layer.js");
0 ignored issues
show
Deprecated Code introduced by
The function javascript_tag() has been deprecated: Use Twig filter jsTag ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

818
                echo /** @scrutinizer ignore-deprecated */ javascript_tag("lib/dojo/tt-rss-layer.js");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Bug introduced by
Are you sure the usage of javascript_tag('lib/dojo/tt-rss-layer.js') is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
819
            ?>
820
		</head>
821
		<body class="flat ttrss_utility feed_debugger">
822
		<script type="text/javascript">
823
			require(['dojo/parser', "dojo/ready", 'dijit/form/Button','dijit/form/CheckBox', 'dijit/form/Form',
824
				'dijit/form/Select','dijit/form/TextBox','dijit/form/ValidationTextBox'],function(parser, ready){
825
				ready(function() {
826
					parser.parse();
827
				});
828
			});
829
		</script>
830
831
			<div class="container">
832
				<h1>Feed Debugger: <?php echo "$feed_id: ".$this->getFeedTitle($feed_id) ?></h1>
833
				<div class="content">
834
					<form method="GET" action="">
835
						<input type="hidden" name="op" value="feeds">
836
						<input type="hidden" name="method" value="update_debugger">
837
						<input type="hidden" name="xdebug" value="1">
838
						<input type="hidden" name="csrf_token" value="<?php echo $csrf_token ?>">
839
						<input type="hidden" name="action" value="do_update">
840
						<input type="hidden" name="feed_id" value="<?php echo $feed_id ?>">
841
842
						<fieldset class="narrow">
843
							<label class="checkbox"><input dojoType="dijit.form.CheckBox" type="checkbox" name="force_refetch" value="1" <?php echo $refetch_checked ?>> Force refetch</label>
844
						</fieldset>
845
846
						<fieldset class="narrow">
847
							<label class="checkbox"><input dojoType="dijit.form.CheckBox" type="checkbox" name="force_rehash" value="1" <?php echo $rehash_checked ?>> Force rehash</label>
848
						</fieldset>
849
850
						<button type="submit" dojoType="dijit.form.Button" class="alt-primary">Continue</button>
851
					</form>
852
853
					<hr>
854
855
					<pre><?php
856
857
                    if ($do_update) {
858
                        RSSUtils::update_rss_feed($feed_id, true);
859
                    }
860
861
                    ?></pre>
862
				</div>
863
			</div>
864
		</body>
865
		</html>
866
		<?php
867
868
    }
869
870
    public static function catchup_feed($feed, $cat_view, $owner_uid = false, $mode = 'all', $search = false) {
871
872
        if (!$owner_uid) {
873
            $owner_uid = $_SESSION['uid'];
874
        }
875
876
        $pdo = Db::pdo();
877
878
        if (is_array($search) && $search[0]) {
879
            $search_qpart = "";
880
881
            foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) {
882
                list($search_qpart, $search_words) = $plugin->hook_search($search[0]);
883
                break;
884
            }
885
886
            // fall back in case of no plugins
887
            if (!$search_qpart) {
888
                list($search_qpart, $search_words) = Feeds::search_to_sql($search[0], $search[1]);
889
            }
890
        } else {
891
            $search_qpart = "true";
892
        }
893
894
        // TODO: all this interval stuff needs some generic generator function
895
896
        switch ($mode) {
897
        case "1day":
898
            if (DB_TYPE == "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
899
                $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
900
            } else {
901
                $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
902
            }
903
            break;
904
        case "1week":
905
            if (DB_TYPE == "pgsql") {
906
                $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
907
            } else {
908
                $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
909
            }
910
            break;
911
        case "2week":
912
            if (DB_TYPE == "pgsql") {
913
                $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
914
            } else {
915
                $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
916
            }
917
            break;
918
        default:
919
            $date_qpart = "true";
920
        }
921
922
        if (is_numeric($feed)) {
923
            if ($cat_view) {
924
925
                if ($feed >= 0) {
926
927
                    if ($feed > 0) {
928
                        $children = Feeds::getChildCategories($feed, $owner_uid);
929
                        array_push($children, $feed);
930
                        $children = array_map("intval", $children);
931
932
                        $children = join(",", $children);
933
934
                        $cat_qpart = "cat_id IN ($children)";
935
                    } else {
936
                        $cat_qpart = "cat_id IS NULL";
937
                    }
938
939
                    $sth = $pdo->prepare("UPDATE ttrss_user_entries
940
						SET unread = false, last_read = NOW() WHERE ref_id IN
941
							(SELECT id FROM
942
								(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
943
									AND owner_uid = ? AND unread = true AND feed_id IN
944
										(SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart AND $search_qpart) as tmp)");
945
                    $sth->execute([$owner_uid]);
946
947
                } else if ($feed == -2) {
948
949
                    $sth = $pdo->prepare("UPDATE ttrss_user_entries
950
						SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
951
							FROM ttrss_user_labels2, ttrss_entries WHERE article_id = ref_id AND id = ref_id AND $date_qpart AND $search_qpart) > 0
952
							AND unread = true AND owner_uid = ?");
953
                    $sth->execute([$owner_uid]);
954
                }
955
956
            } else if ($feed > 0) {
957
958
                $sth = $pdo->prepare("UPDATE ttrss_user_entries
959
					SET unread = false, last_read = NOW() WHERE ref_id IN
960
						(SELECT id FROM
961
							(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
962
								AND owner_uid = ? AND unread = true AND feed_id = ? AND $date_qpart AND $search_qpart) as tmp)");
963
                $sth->execute([$owner_uid, $feed]);
964
965
            } else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred
966
967
                if ($feed == -1) {
968
                    $sth = $pdo->prepare("UPDATE ttrss_user_entries
969
						SET unread = false, last_read = NOW() WHERE ref_id IN
970
							(SELECT id FROM
971
								(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
972
									AND owner_uid = ? AND unread = true AND marked = true AND $date_qpart AND $search_qpart) as tmp)");
973
                    $sth->execute([$owner_uid]);
974
                }
975
976
                if ($feed == -2) {
977
                    $sth = $pdo->prepare("UPDATE ttrss_user_entries
978
						SET unread = false, last_read = NOW() WHERE ref_id IN
979
							(SELECT id FROM
980
								(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
981
									AND owner_uid = ? AND unread = true AND published = true AND $date_qpart AND $search_qpart) as tmp)");
982
                    $sth->execute([$owner_uid]);
983
                }
984
985
                if ($feed == -3) {
986
987
                    $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE");
988
989
                    if (DB_TYPE == "pgsql") {
990
                        $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
991
                    } else {
992
                        $match_part = "date_entered > DATE_SUB(NOW(),
993
							INTERVAL $intl HOUR) ";
994
                    }
995
996
                    $sth = $pdo->prepare("UPDATE ttrss_user_entries
997
						SET unread = false, last_read = NOW() WHERE ref_id IN
998
							(SELECT id FROM
999
								(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1000
									AND owner_uid = ? AND score >= 0 AND unread = true AND $date_qpart AND $match_part AND $search_qpart) as tmp)");
1001
                    $sth->execute([$owner_uid]);
1002
                }
1003
1004
                if ($feed == -4) {
1005
                    $sth = $pdo->prepare("UPDATE ttrss_user_entries
1006
						SET unread = false, last_read = NOW() WHERE ref_id IN
1007
							(SELECT id FROM
1008
								(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1009
									AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)");
1010
                    $sth->execute([$owner_uid]);
1011
                }
1012
1013
            } else if ($feed < LABEL_BASE_INDEX) { // label
1014
1015
                $label_id = Labels::feed_to_label_id($feed);
1016
1017
                $sth = $pdo->prepare("UPDATE ttrss_user_entries
1018
					SET unread = false, last_read = NOW() WHERE ref_id IN
1019
						(SELECT id FROM
1020
							(SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1021
								AND label_id = ? AND ref_id = article_id
1022
								AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)");
1023
                $sth->execute([$label_id, $owner_uid]);
1024
1025
            }
1026
1027
            CCache::update($feed, $owner_uid, $cat_view);
1028
1029
        } else { // tag
1030
            $sth = $pdo->prepare("UPDATE ttrss_user_entries
1031
				SET unread = false, last_read = NOW() WHERE ref_id IN
1032
					(SELECT id FROM
1033
						(SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1034
							AND post_int_id = int_id AND tag_name = ?
1035
							AND ttrss_user_entries.owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)");
1036
            $sth->execute([$feed, $owner_uid]);
1037
1038
        }
1039
    }
1040
1041
    public static function getFeedArticles($feed, $is_cat = false, $unread_only = false,
1042
                                $owner_uid = false) {
1043
1044
        $n_feed = (int) $feed;
1045
        $need_entries = false;
1046
1047
        $pdo = Db::pdo();
1048
1049
        if (!$owner_uid) {
1050
            $owner_uid = $_SESSION["uid"];
1051
        }
1052
1053
        if ($unread_only) {
1054
            $unread_qpart = "unread = true";
1055
        } else {
1056
            $unread_qpart = "true";
1057
        }
1058
1059
        $match_part = "";
1060
1061
        if ($is_cat) {
1062
            return Feeds::getCategoryUnread($n_feed, $owner_uid);
1063
        } else if ($n_feed == -6) {
1064
            return 0;
1065
        } else if ($feed != "0" && $n_feed == 0) {
1066
1067
            $sth = $pdo->prepare("SELECT SUM((SELECT COUNT(int_id)
1068
				FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1069
					AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1070
				WHERE owner_uid = ? AND tag_name = ?");
1071
1072
            $sth->execute([$owner_uid, $feed]);
1073
            $row = $sth->fetch();
1074
1075
            return $row["count"];
1076
1077
        } else if ($n_feed == -1) {
1078
            $match_part = "marked = true";
1079
        } else if ($n_feed == -2) {
1080
            $match_part = "published = true";
1081
        } else if ($n_feed == -3) {
1082
            $match_part = "unread = true AND score >= 0";
1083
1084
            $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1085
1086
            if (DB_TYPE == "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1087
                $match_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
1088
            } else {
1089
                $match_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1090
            }
1091
1092
            $need_entries = true;
1093
1094
        } else if ($n_feed == -4) {
1095
            $match_part = "true";
1096
        } else if ($n_feed >= 0) {
1097
1098
            if ($n_feed != 0) {
1099
                $match_part = "feed_id = ".(int) $n_feed;
1100
            } else {
1101
                $match_part = "feed_id IS NULL";
1102
            }
1103
1104
        } else if ($feed < LABEL_BASE_INDEX) {
1105
1106
            $label_id = Labels::feed_to_label_id($feed);
1107
1108
            return Feeds::getLabelUnread($label_id, $owner_uid);
1109
        }
1110
1111
        if ($match_part) {
1112
1113
            if ($need_entries) {
1114
                $from_qpart = "ttrss_user_entries,ttrss_entries";
1115
                $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1116
            } else {
1117
                $from_qpart = "ttrss_user_entries";
1118
                $from_where = "";
1119
            }
1120
1121
            $sth = $pdo->prepare("SELECT count(int_id) AS unread
1122
				FROM $from_qpart WHERE
1123
				$unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = ?");
1124
            $sth->execute([$owner_uid]);
1125
            $row = $sth->fetch();
1126
1127
            return $row["unread"];
1128
1129
        } else {
1130
1131
            $sth = $pdo->prepare("SELECT COUNT(post_int_id) AS unread
1132
				FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1133
				WHERE tag_name = ? AND post_int_id = int_id AND ref_id = ttrss_entries.id
1134
				AND $unread_qpart AND ttrss_tags.owner_uid = ,");
1135
1136
            $sth->execute([$feed, $owner_uid]);
1137
            $row = $sth->fetch();
1138
1139
            return $row["unread"];
1140
        }
1141
    }
1142
1143
    /**
1144
     * @return array (code => Status code, message => error message if available)
1145
     *
1146
     *                 0 - OK, Feed already exists
1147
     *                 1 - OK, Feed added
1148
     *                 2 - Invalid URL
1149
     *                 3 - URL content is HTML, no feeds available
1150
     *                 4 - URL content is HTML which contains multiple feeds.
1151
     *                     Here you should call extractfeedurls in rpc-backend
1152
     *                     to get all possible feeds.
1153
     *                 5 - Couldn't download the URL content.
1154
     *                 6 - Content is an invalid XML.
1155
     */
1156
    public static function subscribe_to_feed($url, $cat_id = 0,
1157
                                $auth_login = '', $auth_pass = '') {
1158
1159
        global $fetch_last_error;
1160
        global $fetch_last_error_content;
1161
        global $fetch_last_content_type;
1162
1163
        $pdo = Db::pdo();
1164
1165
        $url = Feeds::fix_url($url);
1166
1167
        if (!$url || !Feeds::validate_feed_url($url)) {
1168
            return array("code" => 2);
1169
        }
1170
1171
        $contents = @fetch_file_contents($url, false, $auth_login, $auth_pass);
1172
1173
        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SUBSCRIBE_FEED) as $plugin) {
1174
            $contents = $plugin->hook_subscribe_feed($contents, $url, $auth_login, $auth_pass);
1175
        }
1176
1177
        if (!$contents) {
1178
            if (preg_match("/cloudflare\.com/", $fetch_last_error_content)) {
1179
                $fetch_last_error .= " (feed behind Cloudflare)";
1180
            }
1181
1182
            return array("code" => 5, "message" => $fetch_last_error);
1183
        }
1184
1185
        if (mb_strpos($fetch_last_content_type, "html") !== false && Feeds::is_html($contents)) {
1186
            $feedUrls = Feeds::get_feeds_from_html($url, $contents);
1187
1188
            if (count($feedUrls) == 0) {
1189
                return array("code" => 3);
1190
            } else if (count($feedUrls) > 1) {
1191
                return array("code" => 4, "feeds" => $feedUrls);
1192
            }
1193
            //use feed url as new URL
1194
            $url = key($feedUrls);
1195
        }
1196
1197
        if (!$cat_id) {
1198
            $cat_id = null;
1199
        }
1200
1201
        $sth = $pdo->prepare("SELECT id FROM ttrss_feeds
1202
			WHERE feed_url = ? AND owner_uid = ?");
1203
        $sth->execute([$url, $_SESSION['uid']]);
1204
1205
        if ($row = $sth->fetch()) {
1206
            return array("code" => 0, "feed_id" => (int) $row["id"]);
1207
        } else {
1208
            $sth = $pdo->prepare(
1209
                "INSERT INTO ttrss_feeds
1210
					(owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
1211
				VALUES (?, ?, ?, ?, ?, ?, 0, false)");
1212
1213
            $sth->execute([$_SESSION['uid'], $url, "[Unknown]", $cat_id, (string) $auth_login, (string) $auth_pass]);
1214
1215
            $sth = $pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ?
1216
					AND owner_uid = ?");
1217
            $sth->execute([$url, $_SESSION['uid']]);
1218
            $row = $sth->fetch();
1219
1220
            $feed_id = $row["id"];
1221
1222
            if ($feed_id) {
1223
                RSSUtils::set_basic_feed_info($feed_id);
1224
            }
1225
1226
            return array("code" => 1, "feed_id" => (int) $feed_id);
1227
1228
        }
1229
    }
1230
1231
    public static function getIconFile($feed_id) {
1232
        return ICONS_DIR."/$feed_id.ico";
0 ignored issues
show
Bug introduced by
The constant ICONS_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1233
    }
1234
1235
    public static function feedHasIcon($id) {
1236
        return is_file(ICONS_DIR."/$id.ico") && filesize(ICONS_DIR."/$id.ico") > 0;
0 ignored issues
show
Bug introduced by
The constant ICONS_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1237
    }
1238
1239
    public static function getFeedIcon($id) {
1240
        switch ($id) {
1241
        case 0:
1242
            return "archive";
1243
        case -1:
1244
            return "star";
1245
        case -2:
1246
            return "rss_feed";
1247
        case -3:
1248
            return "whatshot";
1249
        case -4:
1250
            return "inbox";
1251
        case -6:
1252
            return "restore";
1253
        default:
1254
            if ($id < LABEL_BASE_INDEX) {
1255
                return "label";
1256
            } else {
1257
                $icon = self::getIconFile($id);
1258
1259
                if ($icon && file_exists($icon)) {
1260
                    return ICONS_URL."/".basename($icon)."?".filemtime($icon);
0 ignored issues
show
Bug introduced by
The constant ICONS_URL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1261
                }
1262
            }
1263
        }
1264
1265
        return false;
1266
    }
1267
1268
    public static function getFeedTitle($id, $cat = false) {
1269
        $pdo = Db::pdo();
1270
1271
        if ($cat) {
1272
            return Feeds::getCategoryTitle($id);
1273
        } else if ($id == -1) {
1274
            return __("Starred articles");
1275
        } else if ($id == -2) {
1276
            return __("Published articles");
1277
        } else if ($id == -3) {
1278
            return __("Fresh articles");
1279
        } else if ($id == -4) {
1280
            return __("All articles");
1281
        } else if ($id === 0 || $id === "0") {
1282
            return __("Archived articles");
1283
        } else if ($id == -6) {
1284
            return __("Recently read");
1285
        } else if ($id < LABEL_BASE_INDEX) {
1286
1287
            $label_id = Labels::feed_to_label_id($id);
1288
1289
            $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 WHERE id = ?");
1290
            $sth->execute([$label_id]);
1291
1292
            if ($row = $sth->fetch()) {
1293
                return $row["caption"];
1294
            } else {
1295
                return "Unknown label ($label_id)";
1296
            }
1297
1298
        } else if (is_numeric($id) && $id > 0) {
1299
1300
            $sth = $pdo->prepare("SELECT title FROM ttrss_feeds WHERE id = ?");
1301
            $sth->execute([$id]);
1302
1303
            if ($row = $sth->fetch()) {
1304
                return $row["title"];
1305
            } else {
1306
                return "Unknown feed ($id)";
1307
            }
1308
1309
        } else {
1310
            return $id;
1311
        }
1312
    }
1313
1314
    public static function getCategoryUnread($cat, $owner_uid = false) {
1315
1316
        if (!$owner_uid) {
1317
            $owner_uid = $_SESSION["uid"];
1318
        }
1319
1320
        $pdo = Db::pdo();
1321
1322
        if ($cat >= 0) {
1323
1324
            if (!$cat) {
1325
                $cat = null;
1326
            }
1327
1328
            $sth = $pdo->prepare("SELECT id FROM ttrss_feeds
1329
                    WHERE (cat_id = :cat OR (:cat IS NULL AND cat_id IS NULL))
1330
					AND owner_uid = :uid");
1331
1332
            $sth->execute([":cat" => $cat, ":uid" => $owner_uid]);
1333
1334
            $cat_feeds = array();
1335
            while ($line = $sth->fetch()) {
1336
                array_push($cat_feeds, "feed_id = ".(int) $line["id"]);
1337
            }
1338
1339
            if (count($cat_feeds) == 0) {
1340
                return 0;
1341
            }
1342
1343
            $match_part = implode(" OR ", $cat_feeds);
1344
1345
            $sth = $pdo->prepare("SELECT COUNT(int_id) AS unread
1346
				FROM ttrss_user_entries
1347
				WHERE	unread = true AND ($match_part)
1348
				AND owner_uid = ?");
1349
            $sth->execute([$owner_uid]);
1350
1351
            $unread = 0;
1352
1353
            # this needs to be rewritten
1354
            while ($line = $sth->fetch()) {
1355
                $unread += $line["unread"];
1356
            }
1357
1358
            return $unread;
1359
        } else if ($cat == -1) {
1360
            return getFeedUnread(-1) + getFeedUnread(-2) + getFeedUnread(-3) + getFeedUnread(0);
1361
        } else if ($cat == -2) {
1362
1363
            $sth = $pdo->prepare("SELECT COUNT(unread) AS unread FROM
1364
					ttrss_user_entries, ttrss_user_labels2
1365
				WHERE article_id = ref_id AND unread = true
1366
					AND ttrss_user_entries.owner_uid = ?");
1367
            $sth->execute([$owner_uid]);
1368
            $row = $sth->fetch();
1369
1370
            return $row["unread"];
1371
        }
1372
    }
1373
1374
    // only accepts real cats (>= 0)
1375
    public static function getCategoryChildrenUnread($cat, $owner_uid = false) {
1376
        if (!$owner_uid) {
1377
            $owner_uid = $_SESSION["uid"];
1378
        }
1379
1380
        $pdo = Db::pdo();
1381
1382
        $sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories WHERE parent_cat = ?
1383
				AND owner_uid = ?");
1384
        $sth->execute([$cat, $owner_uid]);
1385
1386
        $unread = 0;
1387
1388
        while ($line = $sth->fetch()) {
1389
            $unread += Feeds::getCategoryUnread($line["id"], $owner_uid);
1390
            $unread += Feeds::getCategoryChildrenUnread($line["id"], $owner_uid);
1391
        }
1392
1393
        return $unread;
1394
    }
1395
1396
    public static function getGlobalUnread($user_id = false) {
1397
1398
        if (!$user_id) {
1399
            $user_id = $_SESSION["uid"];
1400
        }
1401
1402
        $pdo = Db::pdo();
1403
1404
        $sth = $pdo->prepare("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1405
			WHERE owner_uid = ? AND feed_id > 0");
1406
        $sth->execute([$user_id]);
1407
        $row = $sth->fetch();
1408
1409
        return $row["c_id"];
1410
    }
1411
1412
    public static function getCategoryTitle($cat_id) {
1413
1414
        if ($cat_id == -1) {
1415
            return __("Special");
1416
        } else if ($cat_id == -2) {
1417
            return __("Labels");
1418
        } else {
1419
1420
            $pdo = Db::pdo();
1421
1422
            $sth = $pdo->prepare("SELECT title FROM ttrss_feed_categories WHERE
1423
				id = ?");
1424
            $sth->execute([$cat_id]);
1425
1426
            if ($row = $sth->fetch()) {
1427
                return $row["title"];
1428
            } else {
1429
                return __("Uncategorized");
1430
            }
1431
        }
1432
    }
1433
1434
    public static function getLabelUnread($label_id, $owner_uid = false) {
1435
        if (!$owner_uid) {
1436
            $owner_uid = $_SESSION["uid"];
1437
        }
1438
1439
        $pdo = Db::pdo();
1440
1441
        $sth = $pdo->prepare("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1442
			WHERE owner_uid = ? AND unread = true AND label_id = ? AND article_id = ref_id");
1443
1444
        $sth->execute([$owner_uid, $label_id]);
1445
1446
        if ($row = $sth->fetch()) {
1447
            return $row["unread"];
1448
        } else {
1449
            return 0;
1450
        }
1451
    }
1452
1453
    public static function queryFeedHeadlines($params) {
1454
1455
        $pdo = Db::pdo();
1456
1457
        // WARNING: due to highly dynamic nature of this query its going to quote parameters
1458
        // right before adding them to SQL part
1459
1460
        $feed = $params["feed"];
1461
        $limit = isset($params["limit"]) ? $params["limit"] : 30;
1462
        $view_mode = $params["view_mode"];
1463
        $cat_view = isset($params["cat_view"]) ? $params["cat_view"] : false;
1464
        $search = isset($params["search"]) ? $params["search"] : false;
1465
        $search_language = isset($params["search_language"]) ? $params["search_language"] : "";
1466
        $override_order = isset($params["override_order"]) ? $params["override_order"] : false;
1467
        $offset = isset($params["offset"]) ? $params["offset"] : 0;
1468
        $owner_uid = isset($params["owner_uid"]) ? $params["owner_uid"] : $_SESSION["uid"];
1469
        $since_id = isset($params["since_id"]) ? $params["since_id"] : 0;
1470
        $include_children = isset($params["include_children"]) ? $params["include_children"] : false;
1471
        $ignore_vfeed_group = isset($params["ignore_vfeed_group"]) ? $params["ignore_vfeed_group"] : false;
1472
        $override_strategy = isset($params["override_strategy"]) ? $params["override_strategy"] : false;
1473
        $override_vfeed = isset($params["override_vfeed"]) ? $params["override_vfeed"] : false;
1474
        $start_ts = isset($params["start_ts"]) ? $params["start_ts"] : false;
1475
        $check_first_id = isset($params["check_first_id"]) ? $params["check_first_id"] : false;
1476
        $skip_first_id_check = isset($params["skip_first_id_check"]) ? $params["skip_first_id_check"] : false;
1477
        //$order_by = isset($params["order_by"]) ? $params["order_by"] : false;
1478
1479
        $ext_tables_part = "";
1480
        $limit_query_part = "";
1481
        $query_error_override = "";
1482
1483
        $search_words = [];
1484
1485
        if ($search) {
1486
            $search_query_part = "";
1487
1488
            foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) {
1489
                list($search_query_part, $search_words) = $plugin->hook_search($search);
1490
                break;
1491
            }
1492
1493
            // fall back in case of no plugins
1494
            if (!$search_query_part) {
1495
                list($search_query_part, $search_words) = Feeds::search_to_sql($search, $search_language);
1496
            }
1497
1498
            if (DB_TYPE == "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1499
                $test_sth = $pdo->prepare("select $search_query_part
1500
					FROM ttrss_entries, ttrss_user_entries WHERE id = ref_id limit 1");
1501
1502
                try {
1503
                    $test_sth->execute();
1504
                } catch (PDOException $e) {
1505
                    // looks like tsquery syntax is invalid
1506
                    $search_query_part = "false";
1507
1508
                    $query_error_override = T_sprintf("Incorrect search syntax: %s.", implode(" ", $search_words));
1509
                }
1510
            }
1511
1512
            $search_query_part .= " AND ";
1513
        } else {
1514
            $search_query_part = "";
1515
        }
1516
1517
        if ($since_id) {
1518
            $since_id_part = "ttrss_entries.id > ".$pdo->quote($since_id)." AND ";
1519
        } else {
1520
            $since_id_part = "";
1521
        }
1522
1523
        $view_query_part = "";
1524
1525
        if ($view_mode == "adaptive") {
1526
            if ($search) {
1527
                $view_query_part = " ";
1528
            } else if ($feed != -1) {
1529
1530
                $unread = getFeedUnread($feed, $cat_view);
1531
1532
                if ($cat_view && $feed > 0 && $include_children) {
1533
                                    $unread += Feeds::getCategoryChildrenUnread($feed);
1534
                }
1535
1536
                if ($unread > 0) {
1537
                    $view_query_part = " unread = true AND ";
1538
                }
1539
            }
1540
        }
1541
1542
        if ($view_mode == "marked") {
1543
            $view_query_part = " marked = true AND ";
1544
        }
1545
1546
        if ($view_mode == "has_note") {
1547
            $view_query_part = " (note IS NOT NULL AND note != '') AND ";
1548
        }
1549
1550
        if ($view_mode == "published") {
1551
            $view_query_part = " published = true AND ";
1552
        }
1553
1554
        if ($view_mode == "unread" && $feed != -6) {
1555
            $view_query_part = " unread = true AND ";
1556
        }
1557
1558
        if ($limit > 0) {
1559
            $limit_query_part = "LIMIT ".(int) $limit;
1560
        }
1561
1562
        $allow_archived = false;
1563
1564
        $vfeed_query_part = "";
1565
1566
        /* tags */
1567
        if (!is_numeric($feed)) {
1568
            $query_strategy_part = "true";
1569
            $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
1570
					id = feed_id) as feed_title,";
1571
        } else if ($feed > 0) {
1572
1573
            if ($cat_view) {
1574
1575
                if ($feed > 0) {
1576
                    if ($include_children) {
1577
                        # sub-cats
1578
                        $subcats = Feeds::getChildCategories($feed, $owner_uid);
1579
                        array_push($subcats, $feed);
1580
                        $subcats = array_map("intval", $subcats);
1581
1582
                        $query_strategy_part = "cat_id IN (".
1583
                            implode(",", $subcats).")";
1584
1585
                    } else {
1586
                        $query_strategy_part = "cat_id = ".$pdo->quote($feed);
1587
                    }
1588
1589
                } else {
1590
                    $query_strategy_part = "cat_id IS NULL";
1591
                }
1592
1593
                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1594
1595
            } else {
1596
                $query_strategy_part = "feed_id = ".$pdo->quote($feed);
1597
            }
1598
        } else if ($feed == 0 && !$cat_view) { // archive virtual feed
1599
            $query_strategy_part = "feed_id IS NULL";
1600
            $allow_archived = true;
1601
        } else if ($feed == 0 && $cat_view) { // uncategorized
1602
            $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
1603
            $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1604
        } else if ($feed == -1) { // starred virtual feed
1605
            $query_strategy_part = "marked = true";
1606
            $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1607
            $allow_archived = true;
1608
1609
            if (!$override_order) {
1610
                $override_order = "last_marked DESC, date_entered DESC, updated DESC";
1611
            }
1612
1613
        } else if ($feed == -2) { // published virtual feed OR labels category
1614
1615
            if (!$cat_view) {
1616
                $query_strategy_part = "published = true";
1617
                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1618
                $allow_archived = true;
1619
1620
                if (!$override_order) {
1621
                    $override_order = "last_published DESC, date_entered DESC, updated DESC";
1622
                }
1623
1624
            } else {
1625
                $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1626
1627
                $ext_tables_part = "ttrss_labels2,ttrss_user_labels2,";
1628
1629
                $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
1630
						ttrss_user_labels2.article_id = ref_id";
1631
1632
            }
1633
        } else if ($feed == -6) { // recently read
1634
            $query_strategy_part = "unread = false AND last_read IS NOT NULL";
1635
1636
            if (DB_TYPE == "pgsql") {
1637
                $query_strategy_part .= " AND last_read > NOW() - INTERVAL '1 DAY' ";
1638
            } else {
1639
                $query_strategy_part .= " AND last_read > DATE_SUB(NOW(), INTERVAL 1 DAY) ";
1640
            }
1641
1642
            $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1643
            $allow_archived = true;
1644
            $ignore_vfeed_group = true;
1645
1646
            if (!$override_order) {
1647
                $override_order = "last_read DESC";
1648
            }
1649
1650
        } else if ($feed == -3) { // fresh virtual feed
1651
            $query_strategy_part = "unread = true AND score >= 0";
1652
1653
            $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1654
1655
            if (DB_TYPE == "pgsql") {
1656
                $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
1657
            } else {
1658
                $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1659
            }
1660
1661
            $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1662
        } else if ($feed == -4) { // all articles virtual feed
1663
            $allow_archived = true;
1664
            $query_strategy_part = "true";
1665
            $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1666
        } else if ($feed <= LABEL_BASE_INDEX) { // labels
1667
            $label_id = Labels::feed_to_label_id($feed);
1668
1669
            $query_strategy_part = "label_id = ".$pdo->quote($label_id)." AND
1670
					ttrss_labels2.id = ttrss_user_labels2.label_id AND
1671
					ttrss_user_labels2.article_id = ref_id";
1672
1673
            $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
1674
            $ext_tables_part = "ttrss_labels2,ttrss_user_labels2,";
1675
            $allow_archived = true;
1676
1677
        } else {
1678
            $query_strategy_part = "true";
1679
        }
1680
1681
        $order_by = "score DESC, date_entered DESC, updated DESC";
1682
1683
        if ($override_order) {
1684
            $order_by = $override_order;
1685
        }
1686
1687
        if ($override_strategy) {
1688
            $query_strategy_part = $override_strategy;
1689
        }
1690
1691
        if ($override_vfeed) {
1692
            $vfeed_query_part = $override_vfeed;
1693
        }
1694
1695
        if ($search) {
1696
            $feed_title = T_sprintf("Search results: %s", $search);
1697
        } else {
1698
            if ($cat_view) {
1699
                $feed_title = Feeds::getCategoryTitle($feed);
1700
            } else {
1701
                if (is_numeric($feed) && $feed > 0) {
1702
                    $ssth = $pdo->prepare("SELECT title,site_url,last_error,last_updated
1703
							FROM ttrss_feeds WHERE id = ? AND owner_uid = ?");
1704
                    $ssth->execute([$feed, $owner_uid]);
1705
                    $row = $ssth->fetch();
1706
1707
                    $feed_title = $row["title"];
1708
                    $feed_site_url = $row["site_url"];
1709
                    $last_error = $row["last_error"];
1710
                    $last_updated = $row["last_updated"];
1711
                } else {
1712
                    $feed_title = Feeds::getFeedTitle($feed);
1713
                }
1714
            }
1715
        }
1716
1717
        $content_query_part = "content, ";
1718
1719
        if ($limit_query_part) {
1720
            $offset_query_part = "OFFSET ".(int) $offset;
1721
        } else {
1722
            $offset_query_part = "";
1723
        }
1724
1725
        if ($start_ts) {
1726
            $start_ts_formatted = date("Y/m/d H:i:s", strtotime($start_ts));
1727
            $start_ts_query_part = "date_entered >= '$start_ts_formatted' AND";
1728
        } else {
1729
            $start_ts_query_part = "";
1730
        }
1731
1732
        if (is_numeric($feed)) {
1733
            // proper override_order applied above
1734
            if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
1735
1736
                if (!(in_array($feed, Feeds::NEVER_GROUP_BY_DATE) && !$cat_view)) {
1737
                    $yyiw_desc = $order_by == "date_reverse" ? "" : "desc";
1738
                    $yyiw_order_qpart = "yyiw $yyiw_desc, ";
1739
                } else {
1740
                    $yyiw_order_qpart = "";
1741
                }
1742
1743
                if (!$override_order) {
1744
                    $order_by = "$yyiw_order_qpart ttrss_feeds.title, $order_by";
1745
                } else {
1746
                    $order_by = "$yyiw_order_qpart ttrss_feeds.title, $override_order";
1747
                }
1748
            }
1749
1750
            if (!$allow_archived) {
1751
                $from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id),ttrss_feeds";
1752
                $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
1753
1754
            } else {
1755
                $from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id)
1756
						LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
1757
            }
1758
1759
            if ($vfeed_query_part) {
1760
                $vfeed_query_part .= "favicon_avg_color,";
1761
            }
1762
1763
            $first_id = 0;
1764
            $first_id_query_strategy_part = $query_strategy_part;
1765
1766
            if ($feed == -3) {
1767
                            $first_id_query_strategy_part = "true";
1768
            }
1769
1770
            if (DB_TYPE == "pgsql") {
1771
                $sanity_interval_qpart = "date_entered >= NOW() - INTERVAL '1 hour' AND";
1772
                $yyiw_qpart = "to_char(date_entered, 'IYYY-IW') AS yyiw";
1773
            } else {
1774
                $sanity_interval_qpart = "date_entered >= DATE_SUB(NOW(), INTERVAL 1 hour) AND";
1775
                $yyiw_qpart = "date_format(date_entered, '%Y-%u') AS yyiw";
1776
            }
1777
1778
            if (!$search && !$skip_first_id_check) {
1779
                // if previous topmost article id changed that means our current pagination is no longer valid
1780
                $query = "SELECT DISTINCT
1781
							ttrss_feeds.title,
1782
							date_entered,
1783
                            $yyiw_qpart,
1784
							guid,
1785
							ttrss_entries.id,
1786
							ttrss_entries.title,
1787
							updated,
1788
							score,
1789
							marked,
1790
							published,
1791
							last_marked,
1792
							last_published,
1793
							last_read
1794
						FROM
1795
							$from_qpart
1796
						WHERE
1797
						$feed_check_qpart
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $feed_check_qpart does not seem to be defined for all execution paths leading up to this point.
Loading history...
1798
						ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND
1799
						$search_query_part
1800
						$start_ts_query_part
1801
						$since_id_part
1802
						$sanity_interval_qpart
1803
						$first_id_query_strategy_part ORDER BY $order_by LIMIT 1";
1804
1805
                /*if ($_REQUEST["debug"]) {
1806
					print $query;
1807
				}*/
1808
1809
                $res = $pdo->query($query);
1810
1811
                if ($row = $res->fetch()) {
1812
                    $first_id = (int) $row["id"];
1813
1814
                    if ($offset > 0 && $first_id && $check_first_id && $first_id != $check_first_id) {
1815
                        return array(-1, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $last_updated does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $feed_site_url does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $last_error does not seem to be defined for all execution paths leading up to this point.
Loading history...
1816
                    }
1817
                }
1818
            }
1819
1820
            $query = "SELECT DISTINCT
1821
						date_entered,
1822
                        $yyiw_qpart,
1823
						guid,
1824
						ttrss_entries.id,ttrss_entries.title,
1825
						updated,
1826
						label_cache,
1827
						tag_cache,
1828
						always_display_enclosures,
1829
						site_url,
1830
						note,
1831
						num_comments,
1832
						comments,
1833
						int_id,
1834
						uuid,
1835
						lang,
1836
						hide_images,
1837
						unread,feed_id,marked,published,link,last_read,orig_feed_id,
1838
						last_marked, last_published,
1839
						$vfeed_query_part
1840
						$content_query_part
1841
						author,score
1842
					FROM
1843
						$from_qpart
1844
					WHERE
1845
					$feed_check_qpart
1846
					ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND
1847
					$search_query_part
1848
					$start_ts_query_part
1849
					$view_query_part
1850
					$since_id_part
1851
					$query_strategy_part ORDER BY $order_by
1852
					$limit_query_part $offset_query_part";
1853
1854
            //if ($_REQUEST["debug"]) print $query;
1855
1856
            $res = $pdo->query($query);
1857
1858
        } else {
1859
            // browsing by tag
1860
1861
            $query = "SELECT DISTINCT
1862
							date_entered,
1863
							guid,
1864
							note,
1865
							ttrss_entries.id as id,
1866
							title,
1867
							updated,
1868
							unread,
1869
							feed_id,
1870
							orig_feed_id,
1871
							marked,
1872
							published,
1873
							num_comments,
1874
							comments,
1875
							int_id,
1876
							tag_cache,
1877
							label_cache,
1878
							link,
1879
							lang,
1880
							uuid,
1881
							last_read,
1882
							(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images,
1883
							last_marked, last_published,
1884
							$since_id_part
1885
							$vfeed_query_part
1886
							$content_query_part
1887
							author, score
1888
						FROM ttrss_entries, ttrss_user_entries, ttrss_tags
1889
						WHERE
1890
							ref_id = ttrss_entries.id AND
1891
							ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND
1892
							post_int_id = int_id AND
1893
							tag_name = ".$pdo->quote($feed)." AND
1894
							$view_query_part
1895
							$search_query_part
1896
							$start_ts_query_part
1897
							$query_strategy_part ORDER BY $order_by
1898
							$limit_query_part $offset_query_part";
1899
1900
            if ($_REQUEST["debug"]) {
1901
                print $query;
1902
            }
1903
1904
            $res = $pdo->query($query);
1905
        }
1906
1907
        return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $first_id does not seem to be defined for all execution paths leading up to this point.
Loading history...
1908
1909
    }
1910
1911
    public static function getParentCategories($cat, $owner_uid) {
1912
        $rv = array();
1913
1914
        $pdo = Db::pdo();
1915
1916
        $sth = $pdo->prepare("SELECT parent_cat FROM ttrss_feed_categories
1917
			WHERE id = ? AND parent_cat IS NOT NULL AND owner_uid = ?");
1918
        $sth->execute([$cat, $owner_uid]);
1919
1920
        while ($line = $sth->fetch()) {
1921
            array_push($rv, $line["parent_cat"]);
1922
            $rv = array_merge($rv, Feeds::getParentCategories($line["parent_cat"], $owner_uid));
1923
        }
1924
1925
        return $rv;
1926
    }
1927
1928
    public static function getChildCategories($cat, $owner_uid) {
1929
        $rv = array();
1930
1931
        $pdo = Db::pdo();
1932
1933
        $sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories
1934
			WHERE parent_cat = ? AND owner_uid = ?");
1935
        $sth->execute([$cat, $owner_uid]);
1936
1937
        while ($line = $sth->fetch()) {
1938
            array_push($rv, $line["id"]);
1939
            $rv = array_merge($rv, Feeds::getChildCategories($line["id"], $owner_uid));
1940
        }
1941
1942
        return $rv;
1943
    }
1944
1945
    public static function getFeedCategory($feed) {
1946
        $pdo = Db::pdo();
1947
1948
        $sth = $pdo->prepare("SELECT cat_id FROM ttrss_feeds
1949
				WHERE id = ?");
1950
        $sth->execute([$feed]);
1951
1952
        if ($row = $sth->fetch()) {
1953
            return $row["cat_id"];
1954
        } else {
1955
            return false;
1956
        }
1957
1958
    }
1959
1960
    function color_of($name) {
1961
        $colormap = ["#1cd7d7", "#d91111", "#1212d7", "#8e16e5", "#7b7b7b",
1962
            "#39f110", "#0bbea6", "#ec0e0e", "#1534f2", "#b9e416",
1963
            "#479af2", "#f36b14", "#10c7e9", "#1e8fe7", "#e22727"];
1964
1965
        $sum = 0;
1966
1967
        for ($i = 0; $i < strlen($name); $i++) {
1968
            $sum += ord($name[$i]);
1969
        }
1970
1971
        $sum %= count($colormap);
1972
1973
        return $colormap[$sum];
1974
    }
1975
1976
    public static function get_feeds_from_html($url, $content) {
1977
        $url     = Feeds::fix_url($url);
1978
        $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
1979
1980
        $feedUrls = [];
1981
1982
        $doc = new DOMDocument();
1983
        if ($doc->loadHTML($content)) {
1984
            $xpath = new DOMXPath($doc);
1985
            $entries = $xpath->query('/html/head/link[@rel="alternate" and '.
1986
                '(contains(@type,"rss") or contains(@type,"atom"))]|/html/head/link[@rel="feed"]');
1987
1988
            foreach ($entries as $entry) {
1989
                if ($entry->hasAttribute('href')) {
1990
                    $title = $entry->getAttribute('title');
1991
                    if ($title == '') {
1992
                        $title = $entry->getAttribute('type');
1993
                    }
1994
                    $feedUrl = rewrite_relative_url(
1995
                        $baseUrl, $entry->getAttribute('href')
1996
                    );
1997
                    $feedUrls[$feedUrl] = $title;
1998
                }
1999
            }
2000
        }
2001
        return $feedUrls;
2002
    }
2003
2004
    public static function is_html($content) {
2005
        return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 8192)) !== 0;
2006
    }
2007
2008
    public static function validate_feed_url($url) {
2009
        $parts = parse_url($url);
2010
2011
        return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
2012
    }
2013
2014
    /**
2015
     * Fixes incomplete URLs by prepending "http://".
2016
     * Also replaces feed:// with http://, and
2017
     * prepends a trailing slash if the url is a domain name only.
2018
     *
2019
     * @param string $url Possibly incomplete URL
2020
     *
2021
     * @return string Fixed URL.
2022
     */
2023
    public static function fix_url($url) {
2024
2025
        // support schema-less urls
2026
        if (strpos($url, '//') === 0) {
2027
            $url = 'https:'.$url;
2028
        }
2029
2030
        if (strpos($url, '://') === false) {
2031
            $url = 'http://'.$url;
2032
        } else if (substr($url, 0, 5) == 'feed:') {
2033
            $url = 'http:'.substr($url, 5);
2034
        }
2035
2036
        //prepend slash if the URL has no slash in it
2037
        // "http://www.example" -> "http://www.example/"
2038
        if (strpos($url, '/', strpos($url, ':') + 3) === false) {
2039
            $url .= '/';
2040
        }
2041
2042
        //convert IDNA hostname to punycode if possible
2043
        if (function_exists("idn_to_ascii")) {
2044
            $parts = parse_url($url);
2045
            if (mb_detect_encoding($parts['host']) != 'ASCII')
2046
            {
2047
                $parts['host'] = idn_to_ascii($parts['host']);
2048
                $url = build_url($parts);
2049
            }
2050
        }
2051
2052
        if ($url != "http:///") {
2053
                    return $url;
2054
        } else {
2055
                    return '';
2056
        }
2057
    }
2058
2059
    public static function add_feed_category($feed_cat, $parent_cat_id = false, $order_id = 0) {
2060
2061
        if (!$feed_cat) {
2062
            return false;
2063
        }
2064
2065
        $feed_cat = mb_substr($feed_cat, 0, 250);
2066
        if (!$parent_cat_id) {
2067
            $parent_cat_id = null;
2068
        }
2069
2070
        $pdo = Db::pdo();
2071
        $tr_in_progress = false;
2072
2073
        try {
2074
            $pdo->beginTransaction();
2075
        } catch (Exception $e) {
2076
            $tr_in_progress = true;
2077
        }
2078
2079
        $sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories
2080
				WHERE (parent_cat = :parent OR (:parent IS NULL AND parent_cat IS NULL))
2081
				AND title = :title AND owner_uid = :uid");
2082
        $sth->execute([':parent' => $parent_cat_id, ':title' => $feed_cat, ':uid' => $_SESSION['uid']]);
2083
2084
        if (!$sth->fetch()) {
2085
2086
            $sth = $pdo->prepare("INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat,order_id)
2087
					VALUES (?, ?, ?, ?)");
2088
            $sth->execute([$_SESSION['uid'], $feed_cat, $parent_cat_id, (int) $order_id]);
2089
2090
            if (!$tr_in_progress) {
2091
                $pdo->commit();
2092
            }
2093
2094
            return true;
2095
        }
2096
2097
        $pdo->commit();
2098
2099
        return false;
2100
    }
2101
2102
    public static function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
2103
2104
        if (!$owner_uid) {
2105
            $owner_uid = $_SESSION["uid"];
2106
        }
2107
2108
        $is_cat = bool_to_sql_bool($is_cat);
2109
2110
        $pdo = Db::pdo();
2111
2112
        $sth = $pdo->prepare("SELECT access_key FROM ttrss_access_keys
2113
				WHERE feed_id = ? AND is_cat = ?
2114
				AND owner_uid = ?");
2115
        $sth->execute([$feed_id, $is_cat, $owner_uid]);
2116
2117
        if ($row = $sth->fetch()) {
2118
            return $row["access_key"];
2119
        } else {
2120
            $key = uniqid_short();
2121
2122
            $sth = $pdo->prepare("INSERT INTO ttrss_access_keys
2123
					(access_key, feed_id, is_cat, owner_uid)
2124
					VALUES (?, ?, ?, ?)");
2125
2126
            $sth->execute([$key, $feed_id, $is_cat, $owner_uid]);
2127
2128
            return $key;
2129
        }
2130
    }
2131
2132
    /**
2133
     * Purge a feed old posts.
2134
     *
2135
     * @param mixed $link A database connection.
2136
     * @param mixed $feed_id The id of the purged feed.
2137
     * @param mixed $purge_interval Olderness of purged posts.
2138
     * @param boolean $debug Set to True to enable the debug. False by default.
2139
     * @access public
2140
     * @return void
2141
     */
2142
    public static function purge_feed($feed_id, $purge_interval) {
2143
2144
        if (!$purge_interval) {
2145
            $purge_interval = Feeds::feed_purge_interval($feed_id);
2146
        }
2147
2148
        $pdo = Db::pdo();
2149
2150
        $sth = $pdo->prepare("SELECT owner_uid FROM ttrss_feeds WHERE id = ?");
2151
        $sth->execute([$feed_id]);
2152
2153
        $owner_uid = false;
2154
2155
        if ($row = $sth->fetch()) {
2156
            $owner_uid = $row["owner_uid"];
2157
        }
2158
2159
        if ($purge_interval == -1 || !$purge_interval) {
2160
            if ($owner_uid) {
2161
                CCache::update($feed_id, $owner_uid);
2162
            }
2163
            return;
2164
        }
2165
2166
        if (!$owner_uid) {
2167
            return;
2168
        }
2169
2170
        if (FORCE_ARTICLE_PURGE == 0) {
0 ignored issues
show
Bug introduced by
The constant FORCE_ARTICLE_PURGE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2171
            $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
2172
                $owner_uid, false);
2173
        } else {
2174
            $purge_unread = true;
2175
            $purge_interval = FORCE_ARTICLE_PURGE;
2176
        }
2177
2178
        if (!$purge_unread) {
2179
                    $query_limit = " unread = false AND ";
2180
        } else {
2181
                    $query_limit = "";
2182
        }
2183
2184
        $purge_interval = (int) $purge_interval;
2185
2186
        if (DB_TYPE == "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2187
            $sth = $pdo->prepare("DELETE FROM ttrss_user_entries
2188
				USING ttrss_entries
2189
				WHERE ttrss_entries.id = ref_id AND
2190
				marked = false AND
2191
				feed_id = ? AND
2192
				$query_limit
2193
				ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
2194
            $sth->execute([$feed_id]);
2195
2196
        } else {
2197
            $sth = $pdo->prepare("DELETE FROM ttrss_user_entries
2198
				USING ttrss_user_entries, ttrss_entries
2199
				WHERE ttrss_entries.id = ref_id AND
2200
				marked = false AND
2201
				feed_id = ? AND
2202
				$query_limit
2203
				ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
2204
            $sth->execute([$feed_id]);
2205
2206
        }
2207
2208
        $rows = $sth->rowCount();
2209
2210
        CCache::update($feed_id, $owner_uid);
2211
2212
        Debug::log("Purged feed $feed_id ($purge_interval): deleted $rows articles");
2213
2214
        return $rows;
2215
    }
2216
2217
    public static function feed_purge_interval($feed_id) {
2218
2219
        $pdo = DB::pdo();
2220
2221
        $sth = $pdo->prepare("SELECT purge_interval, owner_uid FROM ttrss_feeds
2222
			WHERE id = ?");
2223
        $sth->execute([$feed_id]);
2224
2225
        if ($row = $sth->fetch()) {
2226
            $purge_interval = $row["purge_interval"];
2227
            $owner_uid = $row["owner_uid"];
2228
2229
            if ($purge_interval == 0) {
2230
                $purge_interval = get_pref(
2231
                'PURGE_OLD_DAYS', $owner_uid);
2232
            }
2233
2234
            return $purge_interval;
2235
2236
        } else {
2237
            return -1;
2238
        }
2239
    }
2240
2241
    public static function search_to_sql($search, $search_language) {
2242
2243
        $keywords = str_getcsv(trim($search), " ");
2244
        $query_keywords = array();
2245
        $search_words = array();
2246
        $search_query_leftover = array();
2247
2248
        $pdo = Db::pdo();
2249
2250
        if ($search_language) {
2251
                    $search_language = $pdo->quote(mb_strtolower($search_language));
2252
        } else {
2253
                    $search_language = $pdo->quote("english");
2254
        }
2255
2256
        foreach ($keywords as $k) {
2257
            if (strpos($k, "-") === 0) {
2258
                $k = substr($k, 1);
2259
                $not = "NOT";
2260
            } else {
2261
                $not = "";
2262
            }
2263
2264
            $commandpair = explode(":", mb_strtolower($k), 2);
2265
2266
            switch ($commandpair[0]) {
2267
            case "title":
2268
                if ($commandpair[1]) {
2269
                    array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE ".
2270
                        $pdo->quote('%'.mb_strtolower($commandpair[1]).'%')."))");
2271
                } else {
2272
                    array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2273
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2274
                    array_push($search_words, $k);
2275
                }
2276
                break;
2277
            case "author":
2278
                if ($commandpair[1]) {
2279
                    array_push($query_keywords, "($not (LOWER(author) LIKE ".
2280
                        $pdo->quote('%'.mb_strtolower($commandpair[1]).'%')."))");
2281
                } else {
2282
                    array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2283
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2284
                    array_push($search_words, $k);
2285
                }
2286
                break;
2287
            case "note":
2288
                if ($commandpair[1]) {
2289
                    if ($commandpair[1] == "true") {
2290
                                            array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2291
                    } else if ($commandpair[1] == "false") {
2292
                                            array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2293
                    } else {
2294
                                            array_push($query_keywords, "($not (LOWER(note) LIKE ".
2295
                            $pdo->quote('%'.mb_strtolower($commandpair[1]).'%')."))");
2296
                    }
2297
                } else {
2298
                    array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").")
2299
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2300
                    if (!$not) {
2301
                        array_push($search_words, $k);
2302
                    }
2303
                }
2304
                break;
2305
            case "star":
2306
2307
                if ($commandpair[1]) {
2308
                    if ($commandpair[1] == "true") {
2309
                                            array_push($query_keywords, "($not (marked = true))");
2310
                    } else {
2311
                                            array_push($query_keywords, "($not (marked = false))");
2312
                    }
2313
                } else {
2314
                    array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").")
2315
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2316
                    if (!$not) {
2317
                        array_push($search_words, $k);
2318
                    }
2319
                }
2320
                break;
2321
            case "pub":
2322
                if ($commandpair[1]) {
2323
                    if ($commandpair[1] == "true") {
2324
                                            array_push($query_keywords, "($not (published = true))");
2325
                    } else {
2326
                                            array_push($query_keywords, "($not (published = false))");
2327
                    }
2328
2329
                } else {
2330
                    array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2331
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2332
                    if (!$not) {
2333
                        array_push($search_words, $k);
2334
                    }
2335
                }
2336
                break;
2337
            case "unread":
2338
                if ($commandpair[1]) {
2339
                    if ($commandpair[1] == "true") {
2340
                                            array_push($query_keywords, "($not (unread = true))");
2341
                    } else {
2342
                                            array_push($query_keywords, "($not (unread = false))");
2343
                    }
2344
2345
                } else {
2346
                    array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").")
2347
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2348
                    if (!$not) {
2349
                        array_push($search_words, $k);
2350
                    }
2351
                }
2352
                break;
2353
            default:
2354
                if (strpos($k, "@") === 0) {
2355
2356
                    $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
2357
                    $orig_ts = strtotime(substr($k, 1));
2358
                    $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2359
2360
                    //$k = date("Y-m-d", strtotime(substr($k, 1)));
2361
2362
                    array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
2363
                } else {
2364
2365
                    if (DB_TYPE == "pgsql") {
0 ignored issues
show
Bug introduced by
The constant DB_TYPE was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2366
                        $k = mb_strtolower($k);
2367
                        array_push($search_query_leftover, $not ? "!$k" : $k);
2368
                    } else {
2369
                        array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").")
2370
								OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))");
2371
                    }
2372
2373
                    if (!$not) {
2374
                        array_push($search_words, $k);
2375
                    }
2376
                }
2377
            }
2378
        }
2379
2380
        if (count($search_query_leftover) > 0) {
2381
2382
            if (DB_TYPE == "pgsql") {
2383
2384
                // if there's no joiners consider this a "simple" search and
2385
                // concatenate everything with &, otherwise don't try to mess with tsquery syntax
2386
                if (preg_match("/[&|]/", implode(" ", $search_query_leftover))) {
2387
                    $tsquery = $pdo->quote(implode(" ", $search_query_leftover));
2388
                } else {
2389
                    $tsquery = $pdo->quote(implode(" & ", $search_query_leftover));
2390
                }
2391
2392
                array_push($query_keywords,
2393
                    "(tsvector_combined @@ to_tsquery($search_language, $tsquery))");
2394
            }
2395
2396
        }
2397
2398
        $search_query_part = implode("AND", $query_keywords);
2399
2400
        return array($search_query_part, $search_words);
2401
    }
2402
}
2403
2404