Article::create_published_article()   F
last analyzed

Complexity

Conditions 18
Paths 208

Size

Total Lines 138
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 18
eloc 70
c 1
b 0
f 0
nc 208
nop 5
dl 0
loc 138
rs 3.9333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
Unused Code introduced by
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...
Deprecated Code introduced by
The function print_error() has been deprecated: Use twig function errorMessage ( Ignorable by Annotation )

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

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

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...
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") {
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...
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']));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['id']) can also be of type array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

202
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST['id']));
Loading history...
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)));
0 ignored issues
show
Bug introduced by
It seems like $tags_str can also be of type array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

233
        $tags = array_unique(trim_array(explode(",", /** @scrutinizer ignore-type */ $tags_str)));
Loading history...
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);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->labelops(true) targeting Article::labelops() 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...
311
    }
312
313
    public function removefromlabel() {
314
        return $this->labelops(false);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->labelops(false) targeting Article::labelops() 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...
315
    }
316
317
    private function labelops($assign) {
318
        $reply = array();
319
320
        $ids = explode(",", clean($_REQUEST["ids"]));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['ids']) can also be of type array; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

320
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST["ids"]));
Loading history...
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) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $retval does not seem to be defined for all execution paths leading up to this point.
Loading history...
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") {
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...
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