Issues (1270)

classes/article.php (1 issue)

1
<?php
2
class Article extends Handler_Protected {
3
4
    public function csrf_ignore($method) {
5
        $csrf_ignored = array("redirect", "editarticletags");
6
7
        return array_search($method, $csrf_ignored) !== false;
8
    }
9
10
    public function redirect() {
11
        $id = clean($_REQUEST['id']);
12
13
        $sth = $this->pdo->prepare("SELECT link FROM ttrss_entries, ttrss_user_entries
14
						WHERE id = ? AND id = ref_id AND owner_uid = ?
15
						LIMIT 1");
16
        $sth->execute([$id, $_SESSION['uid']]);
17
18
        if ($row = $sth->fetch()) {
19
            $article_url = $row['link'];
20
            $article_url = str_replace("\n", "", $article_url);
21
22
            header("Location: $article_url");
23
            return;
24
25
        } else {
26
            print_error(__("Article not found."));
0 ignored issues
show
The call to print_error() has too many arguments starting with __('Article not found.'). ( Ignorable by Annotation )

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

26
            /** @scrutinizer ignore-call */ 
27
            print_error(__("Article not found."));

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...
27
        }
28
    }
29
30
    public static function create_published_article($title, $url, $content, $labels_str,
31
            $owner_uid) {
32
33
        $guid = 'SHA1:'.sha1("ttshared:".$url.$owner_uid); // include owner_uid to prevent global GUID clash
34
35
        if (!$content) {
36
            $pluginhost = new PluginHost();
37
            $pluginhost->load_all(PluginHost::KIND_ALL, $owner_uid);
38
            $pluginhost->load_data();
39
40
            foreach ($pluginhost->get_hooks(PluginHost::HOOK_GET_FULL_TEXT) as $p) {
41
                $extracted_content = $p->hook_get_full_text($url);
42
43
                if ($extracted_content) {
44
                    $content = $extracted_content;
45
                    break;
46
                }
47
            }
48
        }
49
50
        $content_hash = sha1($content);
51
52
        if ($labels_str != "") {
53
            $labels = explode(",", $labels_str);
54
        } else {
55
            $labels = array();
56
        }
57
58
        $rc = false;
59
60
        if (!$title) {
61
            $title = $url;
62
        }
63
        if (!$title && !$url) {
64
            return false;
65
        }
66
67
        if (filter_var($url, FILTER_VALIDATE_URL) === false) {
68
            return false;
69
        }
70
71
        $pdo = Db::pdo();
72
73
        $pdo->beginTransaction();
74
75
        // only check for our user data here, others might have shared this with different content etc
76
        $sth = $pdo->prepare("SELECT id FROM ttrss_entries, ttrss_user_entries WHERE
77
			guid = ? AND ref_id = id AND owner_uid = ? LIMIT 1");
78
        $sth->execute([$guid, $owner_uid]);
79
80
        if ($row = $sth->fetch()) {
81
            $ref_id = $row['id'];
82
83
            $sth = $pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE
84
				ref_id = ? AND owner_uid = ? LIMIT 1");
85
            $sth->execute([$ref_id, $owner_uid]);
86
87
            if ($row = $sth->fetch()) {
88
                $int_id = $row['int_id'];
89
90
                $sth = $pdo->prepare("UPDATE ttrss_entries SET
91
					content = ?, content_hash = ? WHERE id = ?");
92
                $sth->execute([$content, $content_hash, $ref_id]);
93
94
                if (DB_TYPE == "pgsql") {
95
                    $sth = $pdo->prepare("UPDATE ttrss_entries
96
					SET tsvector_combined = to_tsvector( :ts_content)
97
					WHERE id = :id");
98
                    $params = [
99
                        ":ts_content" => mb_substr(strip_tags($content), 0, 900000),
100
                        ":id" => $ref_id];
101
                    $sth->execute($params);
102
                }
103
104
                $sth = $pdo->prepare("UPDATE ttrss_user_entries SET published = true,
105
						last_published = NOW() WHERE
106
						int_id = ? AND owner_uid = ?");
107
                $sth->execute([$int_id, $owner_uid]);
108
109
            } else {
110
111
                $sth = $pdo->prepare("INSERT INTO ttrss_user_entries
112
					(ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache,
113
						last_read, note, unread, last_published)
114
					VALUES
115
					(?, '', NULL, NULL, ?, true, '', '', NOW(), '', false, NOW())");
116
                $sth->execute([$ref_id, $owner_uid]);
117
            }
118
119
            if (count($labels) != 0) {
120
                foreach ($labels as $label) {
121
                    Labels::add_article($ref_id, trim($label), $owner_uid);
122
                }
123
            }
124
125
            $rc = true;
126
127
        } else {
128
            $sth = $pdo->prepare("INSERT INTO ttrss_entries
129
				(title, guid, link, updated, content, content_hash, date_entered, date_updated)
130
				VALUES
131
				(?, ?, ?, NOW(), ?, ?, NOW(), NOW())");
132
            $sth->execute([$title, $guid, $url, $content, $content_hash]);
133
134
            $sth = $pdo->prepare("SELECT id FROM ttrss_entries WHERE guid = ?");
135
            $sth->execute([$guid]);
136
137
            if ($row = $sth->fetch()) {
138
                $ref_id = $row["id"];
139
                if (DB_TYPE == "pgsql") {
140
                    $sth = $pdo->prepare("UPDATE ttrss_entries
141
					SET tsvector_combined = to_tsvector( :ts_content)
142
					WHERE id = :id");
143
                    $params = [
144
                        ":ts_content" => mb_substr(strip_tags($content), 0, 900000),
145
                        ":id" => $ref_id];
146
                    $sth->execute($params);
147
                }
148
                $sth = $pdo->prepare("INSERT INTO ttrss_user_entries
149
					(ref_id, uuid, feed_id, orig_feed_id, owner_uid, published, tag_cache, label_cache,
150
						last_read, note, unread, last_published)
151
					VALUES
152
					(?, '', NULL, NULL, ?, true, '', '', NOW(), '', false, NOW())");
153
                $sth->execute([$ref_id, $owner_uid]);
154
155
                if (count($labels) != 0) {
156
                    foreach ($labels as $label) {
157
                        Labels::add_article($ref_id, trim($label), $owner_uid);
158
                    }
159
                }
160
161
                $rc = true;
162
            }
163
        }
164
165
        $pdo->commit();
166
167
        return $rc;
168
    }
169
170
    public function editArticleTags() {
171
172
        $param = clean($_REQUEST['param']);
173
174
        $tags = Article::get_article_tags($param);
175
176
        $tags_str = join(", ", $tags);
177
178
        print_hidden("id", "$param");
179
        print_hidden("op", "article");
180
        print_hidden("method", "setArticleTags");
181
182
        print "<header class='horizontal'>".__("Tags for this article (separated by commas):")."</header>";
183
184
        print "<section>";
185
        print "<textarea dojoType='dijit.form.SimpleTextarea' rows='4'
186
			style='height : 100px; font-size : 12px; width : 98%' id='tags_str'
187
			name='tags_str'>$tags_str</textarea>
188
		<div class='autocomplete' id='tags_choices'
189
				style='display:none'></div>";
190
        print "</section>";
191
192
        print "<footer>";
193
        print "<button dojoType='dijit.form.Button'
194
			type='submit' class='alt-primary' onclick=\"dijit.byId('editTagsDlg').execute()\">".__('Save')."</button> ";
195
        print "<button dojoType='dijit.form.Button'
196
			onclick=\"dijit.byId('editTagsDlg').hide()\">".__('Cancel')."</button>";
197
        print "</footer>";
198
199
    }
200
201
    public function setScore() {
202
        $ids = explode(",", clean($_REQUEST['id']));
203
        $score = (int) clean($_REQUEST['score']);
204
205
        $ids_qmarks = arr_qmarks($ids);
206
207
        $sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET
208
			score = ? WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
209
210
        $sth->execute(array_merge([$score], $ids, [$_SESSION['uid']]));
211
212
        print json_encode(["id" => $ids, "score" => (int) $score]);
213
    }
214
215
    public function getScore() {
216
        $id = clean($_REQUEST['id']);
217
218
        $sth = $this->pdo->prepare("SELECT score FROM ttrss_user_entries WHERE ref_id = ? AND owner_uid = ?");
219
        $sth->execute([$id, $_SESSION['uid']]);
220
        $row = $sth->fetch();
221
222
        $score = $row['score'];
223
224
        print json_encode(["id" => $id, "score" => (int) $score]);
225
    }
226
227
228
    public function setArticleTags() {
229
230
        $id = clean($_REQUEST["id"]);
231
232
        $tags_str = clean($_REQUEST["tags_str"]);
233
        $tags = array_unique(trim_array(explode(",", $tags_str)));
234
235
        $this->pdo->beginTransaction();
236
237
        $sth = $this->pdo->prepare("SELECT int_id FROM ttrss_user_entries WHERE
238
				ref_id = ? AND owner_uid = ? LIMIT 1");
239
        $sth->execute([$id, $_SESSION['uid']]);
240
241
        if ($row = $sth->fetch()) {
242
243
            $tags_to_cache = array();
244
245
            $int_id = $row['int_id'];
246
247
            $sth = $this->pdo->prepare("DELETE FROM ttrss_tags WHERE
248
				post_int_id = ? AND owner_uid = ?");
249
            $sth->execute([$int_id, $_SESSION['uid']]);
250
251
            $tags = FeedItem_Common::normalize_categories($tags);
252
253
            foreach ($tags as $tag) {
254
                if ($tag != '') {
255
                    $sth = $this->pdo->prepare("INSERT INTO ttrss_tags
256
								(post_int_id, owner_uid, tag_name)
257
								VALUES (?, ?, ?)");
258
259
                    $sth->execute([$int_id, $_SESSION['uid'], $tag]);
260
                }
261
262
                array_push($tags_to_cache, $tag);
263
            }
264
265
            /* update tag cache */
266
267
            $tags_str = join(",", $tags_to_cache);
268
269
            $sth = $this->pdo->prepare("UPDATE ttrss_user_entries
270
				SET tag_cache = ? WHERE ref_id = ? AND owner_uid = ?");
271
            $sth->execute([$tags_str, $id, $_SESSION['uid']]);
272
        }
273
274
        $this->pdo->commit();
275
276
        $tags = Article::get_article_tags($id);
277
        $tags_str = $this->format_tags_string($tags);
278
        $tags_str_full = join(", ", $tags);
279
280
        if (!$tags_str_full) {
281
            $tags_str_full = __("no tags");
282
        }
283
284
        print json_encode([
285
            "id" => (int) $id,
286
            "content" => $tags_str,
287
            "content_full" => $tags_str_full
288
        ]);
289
    }
290
291
292
    public function completeTags() {
293
        $search = clean($_REQUEST["search"]);
294
295
        $sth = $this->pdo->prepare("SELECT DISTINCT tag_name FROM ttrss_tags
296
				WHERE owner_uid = ? AND
297
				tag_name LIKE ? ORDER BY tag_name
298
				LIMIT 10");
299
300
        $sth->execute([$_SESSION['uid'], "$search%"]);
301
302
        print "<ul>";
303
        while ($line = $sth->fetch()) {
304
            print "<li>".$line["tag_name"]."</li>";
305
        }
306
        print "</ul>";
307
    }
308
309
    public function assigntolabel() {
310
        return $this->labelops(true);
311
    }
312
313
    public function removefromlabel() {
314
        return $this->labelops(false);
315
    }
316
317
    private function labelops($assign) {
318
        $reply = array();
319
320
        $ids = explode(",", clean($_REQUEST["ids"]));
321
        $label_id = clean($_REQUEST["lid"]);
322
323
        $label = Labels::find_caption($label_id, $_SESSION["uid"]);
324
325
        $reply["info-for-headlines"] = array();
326
327
        if ($label) {
328
329
            foreach ($ids as $id) {
330
331
                if ($assign) {
332
                                    Labels::add_article($id, $label, $_SESSION["uid"]);
333
                } else {
334
                                    Labels::remove_article($id, $label, $_SESSION["uid"]);
335
                }
336
337
                $labels = $this->get_article_labels($id, $_SESSION["uid"]);
338
339
                array_push($reply["info-for-headlines"],
340
                array("id" => $id, "labels" => $this->format_article_labels($labels)));
341
342
            }
343
        }
344
345
        $reply["message"] = "UPDATE_COUNTERS";
346
347
        print json_encode($reply);
348
    }
349
350
    public function getArticleFeed($id) {
351
        $sth = $this->pdo->prepare("SELECT feed_id FROM ttrss_user_entries
352
			WHERE ref_id = ? AND owner_uid = ?");
353
        $sth->execute([$id, $_SESSION['uid']]);
354
355
        if ($row = $sth->fetch()) {
356
            return $row["feed_id"];
357
        } else {
358
            return 0;
359
        }
360
    }
361
362
    public static function format_article_enclosures($id, $always_display_enclosures,
363
                                        $article_content, $hide_images = false) {
364
365
        $result = Article::get_article_enclosures($id);
366
        $rv = '';
367
368
        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FORMAT_ENCLOSURES) as $plugin) {
369
            $retval = $plugin->hook_format_enclosures($rv, $result, $id, $always_display_enclosures, $article_content, $hide_images);
370
            if (is_array($retval)) {
371
                $rv = $retval[0];
372
                $result = $retval[1];
373
            } else {
374
                $rv = $retval;
375
            }
376
        }
377
        unset($retval); // Unset to prevent breaking render if there are no HOOK_RENDER_ENCLOSURE hooks below.
378
379
        if ($rv === '' && !empty($result)) {
380
            $entries_html = array();
381
            $entries = array();
382
            $entries_inline = array();
383
384
            foreach ($result as $line) {
385
386
                foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ENCLOSURE_ENTRY) as $plugin) {
387
                    $line = $plugin->hook_enclosure_entry($line, $id);
388
                }
389
390
                $url = $line["content_url"];
391
                $ctype = $line["content_type"];
392
                $title = $line["title"];
393
                $width = $line["width"];
394
                $height = $line["height"];
395
396
                if (!$ctype) {
397
                    $ctype = __("unknown type");
398
                }
399
400
                //$filename = substr($url, strrpos($url, "/")+1);
401
                $filename = basename($url);
402
403
                $player = format_inline_player($url, $ctype);
404
405
                if ($player) {
406
                    array_push($entries_inline, $player);
407
                }
408
409
#				$entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\" rel=\"noopener noreferrer\">" .
410
#					$filename . " (" . $ctype . ")" . "</a>";
411
412
                $entry = "<div onclick=\"popupOpenUrl('".htmlspecialchars($url)."')\"
413
					dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
414
415
                array_push($entries_html, $entry);
416
417
                $entry = array();
418
419
                $entry["type"] = $ctype;
420
                $entry["filename"] = $filename;
421
                $entry["url"] = $url;
422
                $entry["title"] = $title;
423
                $entry["width"] = $width;
424
                $entry["height"] = $height;
425
426
                array_push($entries, $entry);
427
            }
428
429
            if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
430
                if ($always_display_enclosures ||
431
                    !preg_match("/<img/i", $article_content)) {
432
433
                    foreach ($entries as $entry) {
434
435
                        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ENCLOSURE) as $plugin) {
436
                                                    $retval = $plugin->hook_render_enclosure($entry, $hide_images);
437
                        }
438
439
440
                        if ($retval) {
441
                            $rv .= $retval;
442
                        } else {
443
444
                            if (preg_match("/image/", $entry["type"])) {
445
446
                                if (!$hide_images) {
447
                                    $encsize = '';
448
                                    if ($entry['height'] > 0) {
449
                                                                            $encsize .= ' height="'.intval($entry['height']).'"';
450
                                    }
451
                                    if ($entry['width'] > 0) {
452
                                                                            $encsize .= ' width="'.intval($entry['width']).'"';
453
                                    }
454
                                    $rv .= "<p><img
455
										alt=\"".htmlspecialchars($entry["filename"])."\"
456
										src=\"" .htmlspecialchars($entry["url"])."\"
457
										" . $encsize." /></p>";
458
                                } else {
459
                                    $rv .= "<p><a target=\"_blank\" rel=\"noopener noreferrer\"
460
										href=\"".htmlspecialchars($entry["url"])."\"
461
										>" .htmlspecialchars($entry["url"])."</a></p>";
462
                                }
463
464
                                if ($entry['title']) {
465
                                    $rv .= "<div class=\"enclosure_title\">${entry['title']}</div>";
466
                                }
467
                            }
468
                        }
469
                    }
470
                }
471
            }
472
473
            if (count($entries_inline) > 0) {
474
                //$rv .= "<hr clear='both'/>";
475
                foreach ($entries_inline as $entry) { $rv .= $entry; };
476
                $rv .= "<br clear='both'/>";
477
            }
478
479
            $rv .= "<div class=\"attachments\" dojoType=\"fox.form.DropDownButton\">".
480
                "<span>".__('Attachments')."</span>";
481
482
            $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
483
484
            foreach ($entries as $entry) {
485
                if ($entry["title"]) {
486
                                    $title = " &mdash; ".truncate_string($entry["title"], 30);
487
                } else {
488
                                    $title = "";
489
                }
490
491
                if ($entry["filename"]) {
492
                                    $filename = truncate_middle(htmlspecialchars($entry["filename"]), 60);
493
                } else {
494
                                    $filename = "";
495
                }
496
497
                $rv .= "<div onclick='popupOpenUrl(\"".htmlspecialchars($entry["url"])."\")'
498
					dojoType=\"dijit.MenuItem\">".$filename.$title."</div>";
499
500
            };
501
502
            $rv .= "</div>";
503
            $rv .= "</div>";
504
        }
505
506
        return $rv;
507
    }
508
509
    public static function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
510
511
        $a_id = $id;
512
513
        if (!$owner_uid) {
514
            $owner_uid = $_SESSION["uid"];
515
        }
516
517
        $pdo = Db::pdo();
518
519
        $sth = $pdo->prepare("SELECT DISTINCT tag_name,
520
			owner_uid as owner FROM	ttrss_tags
521
			WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
522
			ref_id = ? AND owner_uid = ? LIMIT 1) ORDER BY tag_name");
523
524
        $tags = array();
525
526
        /* check cache first */
527
528
        if ($tag_cache === false) {
529
            $csth = $pdo->prepare("SELECT tag_cache FROM ttrss_user_entries
530
				WHERE ref_id = ? AND owner_uid = ?");
531
            $csth->execute([$id, $owner_uid]);
532
533
            if ($row = $csth->fetch()) {
534
                $tag_cache = $row["tag_cache"];
535
            }
536
        }
537
538
        if ($tag_cache) {
539
            $tags = explode(",", $tag_cache);
540
        } else {
541
542
            /* do it the hard way */
543
544
            $sth->execute([$a_id, $owner_uid]);
545
546
            while ($tmp_line = $sth->fetch()) {
547
                array_push($tags, $tmp_line["tag_name"]);
548
            }
549
550
            /* update the cache */
551
552
            $tags_str = join(",", $tags);
553
554
            $sth = $pdo->prepare("UPDATE ttrss_user_entries
555
				SET tag_cache = ? WHERE ref_id = ?
556
				AND owner_uid = ?");
557
            $sth->execute([$tags_str, $id, $owner_uid]);
558
        }
559
560
        return $tags;
561
    }
562
563
    public static function format_tags_string($tags) {
564
        if (!is_array($tags) || count($tags) == 0) {
565
            return __("no tags");
566
        } else {
567
            $maxtags = min(5, count($tags));
568
            $tags_str = "";
569
570
            for ($i = 0; $i < $maxtags; $i++) {
571
                $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"Feeds.open({feed:'".$tags[$i]."'})\">".$tags[$i]."</a>, ";
572
            }
573
574
            $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str) - 2);
575
576
            if (count($tags) > $maxtags) {
577
                            $tags_str .= ", &hellip;";
578
            }
579
580
            return $tags_str;
581
        }
582
    }
583
584
    public static function format_article_labels($labels) {
585
586
        if (!is_array($labels)) {
587
            return '';
588
        }
589
590
        $labels_str = "";
591
592
        foreach ($labels as $l) {
593
            $labels_str .= sprintf("<div class='label'
594
				style='color : %s; background-color : %s'>%s</div>",
595
                $l[2], $l[3], $l[1]);
596
        }
597
598
        return $labels_str;
599
600
    }
601
602
    public static function format_article_note($id, $note, $allow_edit = true) {
603
604
        if ($allow_edit) {
605
            $onclick = "onclick='Plugins.Note.edit($id)'";
606
            $note_class = 'editable';
607
        } else {
608
            $onclick = '';
609
            $note_class = '';
610
        }
611
612
        return "<div class='article-note $note_class'>
613
			<i class='material-icons'>note</i>
614
			<div $onclick class='body'>$note</div>
615
			</div>";
616
    }
617
618
    public static function get_article_enclosures($id) {
619
620
        $pdo = Db::pdo();
621
622
        $sth = $pdo->prepare("SELECT * FROM ttrss_enclosures
623
			WHERE post_id = ? AND content_url != ''");
624
        $sth->execute([$id]);
625
626
        $rv = array();
627
628
        $cache = new DiskCache("images");
629
630
        while ($line = $sth->fetch()) {
631
632
            if ($cache->exists(sha1($line["content_url"]))) {
633
                $line["content_url"] = $cache->getUrl(sha1($line["content_url"]));
634
            }
635
636
            array_push($rv, $line);
637
        }
638
639
        return $rv;
640
    }
641
642
    public static function purge_orphans() {
643
644
        // purge orphaned posts in main content table
645
646
        if (DB_TYPE == "mysql") {
647
                    $limit_qpart = "LIMIT 5000";
648
        } else {
649
                    $limit_qpart = "";
650
        }
651
652
        $pdo = Db::pdo();
653
        $res = $pdo->query("DELETE FROM ttrss_entries WHERE
654
			NOT EXISTS (SELECT ref_id FROM ttrss_user_entries WHERE ref_id = id) $limit_qpart");
655
656
        if (Debug::enabled()) {
657
            $rows = $res->rowCount();
658
            Debug::log("purged $rows orphaned posts.");
659
        }
660
    }
661
662
    public static function catchupArticlesById($ids, $cmode, $owner_uid = false) {
663
664
        if (!$owner_uid) {
665
            $owner_uid = $_SESSION["uid"];
666
        }
667
668
        $pdo = Db::pdo();
669
670
        $ids_qmarks = arr_qmarks($ids);
671
672
        if ($cmode == 1) {
673
            $sth = $pdo->prepare("UPDATE ttrss_user_entries SET
674
				unread = true
675
					WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
676
        } else if ($cmode == 2) {
677
            $sth = $pdo->prepare("UPDATE ttrss_user_entries SET
678
				unread = NOT unread,last_read = NOW()
679
					WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
680
        } else {
681
            $sth = $pdo->prepare("UPDATE ttrss_user_entries SET
682
				unread = false,last_read = NOW()
683
					WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
684
        }
685
686
        $sth->execute(array_merge($ids, [$owner_uid]));
687
688
        /* update ccache */
689
690
        $sth = $pdo->prepare("SELECT DISTINCT feed_id FROM ttrss_user_entries
691
			WHERE ref_id IN ($ids_qmarks) AND owner_uid = ?");
692
        $sth->execute(array_merge($ids, [$owner_uid]));
693
694
        while ($line = $sth->fetch()) {
695
            CCache::update($line["feed_id"], $owner_uid);
696
        }
697
    }
698
699
    public static function getLastArticleId() {
700
        $pdo = DB::pdo();
701
702
        $sth = $pdo->prepare("SELECT ref_id AS id FROM ttrss_user_entries
703
			WHERE owner_uid = ? ORDER BY ref_id DESC LIMIT 1");
704
        $sth->execute([$_SESSION['uid']]);
705
706
        if ($row = $sth->fetch()) {
707
            return $row['id'];
708
        } else {
709
            return -1;
710
        }
711
    }
712
713
    public static function get_article_labels($id, $owner_uid = false) {
714
        $rv = array();
715
716
        if (!$owner_uid) {
717
            $owner_uid = $_SESSION["uid"];
718
        }
719
720
        $pdo = Db::pdo();
721
722
        $sth = $pdo->prepare("SELECT label_cache FROM
723
			ttrss_user_entries WHERE ref_id = ? AND owner_uid = ?");
724
        $sth->execute([$id, $owner_uid]);
725
726
        if ($row = $sth->fetch()) {
727
            $label_cache = $row["label_cache"];
728
729
            if ($label_cache) {
730
                $tmp = json_decode($label_cache, true);
731
732
                if (!$tmp || $tmp["no-labels"] == 1) {
733
                                    return $rv;
734
                } else {
735
                                    return $tmp;
736
                }
737
            }
738
        }
739
740
        $sth = $pdo->prepare("SELECT DISTINCT label_id,caption,fg_color,bg_color
741
				FROM ttrss_labels2, ttrss_user_labels2
742
			WHERE id = label_id
743
				AND article_id = ?
744
				AND owner_uid = ?
745
			ORDER BY caption");
746
        $sth->execute([$id, $owner_uid]);
747
748
        while ($line = $sth->fetch()) {
749
            $rk = array(Labels::label_to_feed_id($line["label_id"]),
750
                $line["caption"], $line["fg_color"],
751
                $line["bg_color"]);
752
            array_push($rv, $rk);
753
        }
754
755
        if (count($rv) > 0) {
756
                    Labels::update_cache($owner_uid, $id, $rv);
757
        } else {
758
                    Labels::update_cache($owner_uid, $id, array("no-labels" => 1));
759
        }
760
761
        return $rv;
762
    }
763
764
    public static function get_article_image($enclosures, $content, $site_url) {
765
766
        $article_image = "";
767
        $article_stream = "";
768
769
        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_IMAGE) as $p) {
770
            list ($article_image, $article_stream, $content) = $p->hook_article_image($enclosures, $content, $site_url);
771
        }
772
773
        if (!$article_image && !$article_stream) {
774
            $tmpdoc = new DOMDocument();
775
776
            if (@$tmpdoc->loadHTML('<?xml encoding="UTF-8">'.mb_substr($content, 0, 131070))) {
777
                $tmpxpath = new DOMXPath($tmpdoc);
778
                $elems = $tmpxpath->query('(//img[@src]|//video[@poster]|//iframe[contains(@src , "youtube.com/embed/")])');
779
780
                foreach ($elems as $e) {
781
                    if ($e->nodeName == "iframe") {
782
                        $matches = [];
783
                        if (preg_match("/\/embed\/([\w-]+)/", $e->getAttribute("src"), $matches)) {
784
                            $article_image = "https://img.youtube.com/vi/".$matches[1]."/hqdefault.jpg";
785
                            $article_stream = "https://youtu.be/".$matches[1];
786
                            break;
787
                        }
788
                    } else if ($e->nodeName == "video") {
789
                        $article_image = $e->getAttribute("poster");
790
791
                        $src = $tmpxpath->query("//source[@src]", $e)->item(0);
792
793
                        if ($src) {
794
                            $article_stream = $src->getAttribute("src");
795
                        }
796
797
                        break;
798
                    } else if ($e->nodeName == 'img') {
799
                        if (mb_strpos($e->getAttribute("src"), "data:") !== 0) {
800
                            $article_image = $e->getAttribute("src");
801
                        }
802
                        break;
803
                    }
804
                }
805
            }
806
807
            if (!$article_image) {
808
                            foreach ($enclosures as $enc) {
809
                    if (strpos($enc["content_type"], "image/") !== false) {
810
                        $article_image = $enc["content_url"];
811
            }
812
                        break;
813
                    }
814
                }
815
816
            if ($article_image) {
817
                            $article_image = rewrite_relative_url($site_url, $article_image);
818
            }
819
820
            if ($article_stream) {
821
                            $article_stream = rewrite_relative_url($site_url, $article_stream);
822
            }
823
        }
824
825
        $cache = new DiskCache("images");
826
827
        if ($article_image && $cache->exists(sha1($article_image))) {
828
                    $article_image = $cache->getUrl(sha1($article_image));
829
        }
830
831
        if ($article_stream && $cache->exists(sha1($article_stream))) {
832
                    $article_stream = $cache->getUrl(sha1($article_stream));
833
        }
834
835
        return [$article_image, $article_stream];
836
    }
837
838
}
839