Pref_Feeds::editsaveops()   F
last analyzed

Complexity

Conditions 26
Paths 3

Size

Total Lines 178
Code Lines 109

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 109
c 0
b 0
f 0
nc 3
nop 1
dl 0
loc 178
rs 3.3333

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 Pref_Feeds extends Handler_Protected {
3
    public function csrf_ignore($method) {
4
        $csrf_ignored = array("index", "getfeedtree", "add", "editcats", "editfeed",
5
            "savefeedorder", "uploadicon", "feedswitherrors", "inactivefeeds",
6
            "batchsubscribe");
7
8
        return array_search($method, $csrf_ignored) !== false;
9
    }
10
11
    public static function get_ts_languages() {
12
        $rv = [];
13
14
        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...
15
            $dbh = Db::pdo();
16
17
            $res = $dbh->query("SELECT cfgname FROM pg_ts_config");
18
19
            while ($row = $res->fetch()) {
20
                array_push($rv, ucfirst($row['cfgname']));
21
            }
22
        }
23
24
        return $rv;
25
    }
26
27
    public function batch_edit_cbox($elem, $label = false) {
28
        print "<input type=\"checkbox\" title=\"".__("Check to enable field")."\"
29
			onchange=\"dijit.byId('feedEditDlg').toggleField(this, '$elem', '$label')\">";
30
    }
31
32
    public function renamecat() {
33
        $title = clean($_REQUEST['title']);
34
        $id = clean($_REQUEST['id']);
35
36
        if ($title) {
37
            $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories SET
38
				title = ? WHERE id = ? AND owner_uid = ?");
39
            $sth->execute([$title, $id, $_SESSION['uid']]);
40
        }
41
    }
42
43
    private function get_category_items($cat_id) {
44
45
        if (clean($_REQUEST['mode']) != 2) {
46
                    $search = $_SESSION["prefs_feed_search"];
47
        } else {
48
                    $search = "";
49
        }
50
51
        // first one is set by API
52
        $show_empty_cats = clean($_REQUEST['force_show_empty']) ||
53
            (clean($_REQUEST['mode']) != 2 && !$search);
54
55
        $items = array();
56
57
        $sth = $this->pdo->prepare("SELECT id, title FROM ttrss_feed_categories
58
				WHERE owner_uid = ? AND parent_cat = ? ORDER BY order_id, title");
59
        $sth->execute([$_SESSION['uid'], $cat_id]);
60
61
        while ($line = $sth->fetch()) {
62
63
            $cat = array();
64
            $cat['id'] = 'CAT:'.$line['id'];
65
            $cat['bare_id'] = (int) $line['id'];
66
            $cat['name'] = $line['title'];
67
            $cat['items'] = array();
68
            $cat['checkbox'] = false;
69
            $cat['type'] = 'category';
70
            $cat['unread'] = -1;
71
            $cat['child_unread'] = -1;
72
            $cat['auxcounter'] = -1;
73
            $cat['parent_id'] = $cat_id;
74
75
            $cat['items'] = $this->get_category_items($line['id']);
76
77
            $num_children = $this->calculate_children_count($cat);
78
            $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children);
0 ignored issues
show
Bug introduced by
$num_children of type integer is incompatible with the type array expected by parameter $args of vsprintf(). ( Ignorable by Annotation )

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

78
            $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), /** @scrutinizer ignore-type */ $num_children);
Loading history...
79
80
            if ($num_children > 0 || $show_empty_cats) {
81
                            array_push($items, $cat);
82
            }
83
84
        }
85
86
        $fsth = $this->pdo->prepare("SELECT id, title, last_error,
87
			".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval
88
			FROM ttrss_feeds
89
			WHERE cat_id = :cat AND
90
			owner_uid = :uid AND
91
			(:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search))
92
			ORDER BY order_id, title");
93
94
        $fsth->execute([":cat" => $cat_id, ":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]);
95
96
        while ($feed_line = $fsth->fetch()) {
97
            $feed = array();
98
            $feed['id'] = 'FEED:'.$feed_line['id'];
99
            $feed['bare_id'] = (int) $feed_line['id'];
100
            $feed['auxcounter'] = -1;
101
            $feed['name'] = $feed_line['title'];
102
            $feed['checkbox'] = false;
103
            $feed['unread'] = -1;
104
            $feed['error'] = $feed_line['last_error'];
105
            $feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
106
            $feed['param'] = make_local_datetime(
107
                $feed_line['last_updated'], true);
108
            $feed['updates_disabled'] = (int) ($feed_line['update_interval'] < 0);
109
110
            array_push($items, $feed);
111
        }
112
113
        return $items;
114
    }
115
116
    public function getfeedtree() {
117
        print json_encode($this->makefeedtree());
118
    }
119
120
    public function makefeedtree() {
121
122
        if (clean($_REQUEST['mode']) != 2) {
123
                    $search = $_SESSION["prefs_feed_search"];
124
        } else {
125
                    $search = "";
126
        }
127
128
        $root = array();
129
        $root['id'] = 'root';
130
        $root['name'] = __('Feeds');
131
        $root['items'] = array();
132
        $root['type'] = 'category';
133
134
        $enable_cats = get_pref('ENABLE_FEED_CATS');
135
136
        if (clean($_REQUEST['mode']) == 2) {
137
138
            if ($enable_cats) {
139
                $cat = $this->feedlist_init_cat(-1);
140
            } else {
141
                $cat['items'] = array();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$cat was never initialized. Although not strictly required by PHP, it is generally a good practice to add $cat = array(); before regardless.
Loading history...
142
            }
143
144
            foreach (array(-4, -3, -1, -2, 0, -6) as $i) {
145
                array_push($cat['items'], $this->feedlist_init_feed($i));
146
            }
147
148
            /* Plugin feeds for -1 */
149
150
            $feeds = PluginHost::getInstance()->get_feeds(-1);
151
152
            if ($feeds) {
153
                foreach ($feeds as $feed) {
154
                    $feed_id = PluginHost::pfeed_to_feed_id($feed['id']);
155
156
                    $item = array();
157
                    $item['id'] = 'FEED:'.$feed_id;
158
                    $item['bare_id'] = (int) $feed_id;
159
                    $item['auxcounter'] = -1;
160
                    $item['name'] = $feed['title'];
161
                    $item['checkbox'] = false;
162
                    $item['error'] = '';
163
                    $item['icon'] = $feed['icon'];
164
165
                    $item['param'] = '';
166
                    $item['unread'] = -1;
167
                    $item['type'] = 'feed';
168
169
                    array_push($cat['items'], $item);
170
                }
171
            }
172
173
            if ($enable_cats) {
174
                array_push($root['items'], $cat);
175
            } else {
176
                $root['items'] = array_merge($root['items'], $cat['items']);
177
            }
178
179
            $sth = $this->pdo->prepare("SELECT * FROM
180
				ttrss_labels2 WHERE owner_uid = ? ORDER by caption");
181
            $sth->execute([$_SESSION['uid']]);
182
183
            if (get_pref('ENABLE_FEED_CATS')) {
184
                $cat = $this->feedlist_init_cat(-2);
185
            } else {
186
                $cat['items'] = array();
187
            }
188
189
            $num_labels = 0;
190
            while ($line = $sth->fetch()) {
191
                ++$num_labels;
192
193
                $label_id = Labels::label_to_feed_id($line['id']);
194
195
                $feed = $this->feedlist_init_feed($label_id, false, 0);
196
197
                $feed['fg_color'] = $line['fg_color'];
198
                $feed['bg_color'] = $line['bg_color'];
199
200
                array_push($cat['items'], $feed);
201
            }
202
203
            if ($num_labels) {
204
                if ($enable_cats) {
205
                    array_push($root['items'], $cat);
206
                } else {
207
                    $root['items'] = array_merge($root['items'], $cat['items']);
208
                }
209
            }
210
        }
211
212
        if ($enable_cats) {
213
            $show_empty_cats = clean($_REQUEST['force_show_empty']) ||
214
                (clean($_REQUEST['mode']) != 2 && !$search);
215
216
            $sth = $this->pdo->prepare("SELECT id, title FROM ttrss_feed_categories
217
				WHERE owner_uid = ? AND parent_cat IS NULL ORDER BY order_id, title");
218
            $sth->execute([$_SESSION['uid']]);
219
220
            while ($line = $sth->fetch()) {
221
                $cat = array();
222
                $cat['id'] = 'CAT:'.$line['id'];
223
                $cat['bare_id'] = (int) $line['id'];
224
                $cat['auxcounter'] = -1;
225
                $cat['name'] = $line['title'];
226
                $cat['items'] = array();
227
                $cat['checkbox'] = false;
228
                $cat['type'] = 'category';
229
                $cat['unread'] = -1;
230
                $cat['child_unread'] = -1;
231
232
                $cat['items'] = $this->get_category_items($line['id']);
233
234
                $num_children = $this->calculate_children_count($cat);
235
                $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children);
0 ignored issues
show
Bug introduced by
$num_children of type integer is incompatible with the type array expected by parameter $args of vsprintf(). ( Ignorable by Annotation )

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

235
                $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), /** @scrutinizer ignore-type */ $num_children);
Loading history...
236
237
                if ($num_children > 0 || $show_empty_cats) {
238
                                    array_push($root['items'], $cat);
239
                }
240
241
                $root['param'] += count($cat['items']);
242
            }
243
244
            /* Uncategorized is a special case */
245
246
            $cat = array();
247
            $cat['id'] = 'CAT:0';
248
            $cat['bare_id'] = 0;
249
            $cat['auxcounter'] = -1;
250
            $cat['name'] = __("Uncategorized");
251
            $cat['items'] = array();
252
            $cat['type'] = 'category';
253
            $cat['checkbox'] = false;
254
            $cat['unread'] = -1;
255
            $cat['child_unread'] = -1;
256
257
            $fsth = $this->pdo->prepare("SELECT id, title,last_error,
258
				".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval
259
				FROM ttrss_feeds
260
				WHERE cat_id IS NULL AND
261
				owner_uid = :uid AND
262
				(:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search))
263
				ORDER BY order_id, title");
264
            $fsth->execute([":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]);
265
266
            while ($feed_line = $fsth->fetch()) {
267
                $feed = array();
268
                $feed['id'] = 'FEED:'.$feed_line['id'];
269
                $feed['bare_id'] = (int) $feed_line['id'];
270
                $feed['auxcounter'] = -1;
271
                $feed['name'] = $feed_line['title'];
272
                $feed['checkbox'] = false;
273
                $feed['error'] = $feed_line['last_error'];
274
                $feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
275
                $feed['param'] = make_local_datetime(
276
                    $feed_line['last_updated'], true);
277
                $feed['unread'] = -1;
278
                $feed['type'] = 'feed';
279
                $feed['updates_disabled'] = (int) ($feed_line['update_interval'] < 0);
280
281
                array_push($cat['items'], $feed);
282
            }
283
284
            $cat['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($cat['items'])), count($cat['items']));
285
286
            if (count($cat['items']) > 0 || $show_empty_cats) {
287
                            array_push($root['items'], $cat);
288
            }
289
290
            $num_children = $this->calculate_children_count($root);
291
            $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', (int) $num_children), $num_children);
292
293
        } else {
294
            $fsth = $this->pdo->prepare("SELECT id, title, last_error,
295
				".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated, update_interval
296
				FROM ttrss_feeds
297
				WHERE owner_uid = :uid AND
298
				(:search = '' OR (LOWER(title) LIKE :search OR LOWER(feed_url) LIKE :search))
299
				ORDER BY order_id, title");
300
            $fsth->execute([":uid" => $_SESSION['uid'], ":search" => $search ? "%$search%" : ""]);
301
302
            while ($feed_line = $fsth->fetch()) {
303
                $feed = array();
304
                $feed['id'] = 'FEED:'.$feed_line['id'];
305
                $feed['bare_id'] = (int) $feed_line['id'];
306
                $feed['auxcounter'] = -1;
307
                $feed['name'] = $feed_line['title'];
308
                $feed['checkbox'] = false;
309
                $feed['error'] = $feed_line['last_error'];
310
                $feed['icon'] = Feeds::getFeedIcon($feed_line['id']);
311
                $feed['param'] = make_local_datetime(
312
                    $feed_line['last_updated'], true);
313
                $feed['unread'] = -1;
314
                $feed['type'] = 'feed';
315
                $feed['updates_disabled'] = (int) ($feed_line['update_interval'] < 0);
316
317
                array_push($root['items'], $feed);
318
            }
319
320
            $root['param'] = vsprintf(_ngettext('(%d feed)', '(%d feeds)', count($root['items'])), count($root['items']));
321
        }
322
323
        $fl = array();
324
        $fl['identifier'] = 'id';
325
        $fl['label'] = 'name';
326
327
        if (clean($_REQUEST['mode']) != 2) {
328
            $fl['items'] = array($root);
329
        } else {
330
            $fl['items'] = $root['items'];
331
        }
332
333
        return $fl;
334
    }
335
336
    public function catsortreset() {
337
        $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories
338
				SET order_id = 0 WHERE owner_uid = ?");
339
        $sth->execute([$_SESSION['uid']]);
340
    }
341
342
    public function feedsortreset() {
343
        $sth = $this->pdo->prepare("UPDATE ttrss_feeds
344
				SET order_id = 0 WHERE owner_uid = ?");
345
        $sth->execute([$_SESSION['uid']]);
346
    }
347
348
    private function process_category_order(&$data_map, $item_id, $parent_id = false, $nest_level = 0) {
349
350
        $prefix = "";
351
        for ($i = 0; $i < $nest_level; $i++) {
352
                    $prefix .= "   ";
353
        }
354
355
        Debug::log("$prefix C: $item_id P: $parent_id");
356
357
        $bare_item_id = substr($item_id, strpos($item_id, ':') + 1);
358
359
        if ($item_id != 'root') {
360
            if ($parent_id && $parent_id != 'root') {
361
                $parent_bare_id = substr($parent_id, strpos($parent_id, ':') + 1);
362
                $parent_qpart = $parent_bare_id;
363
            } else {
364
                $parent_qpart = null;
365
            }
366
367
            $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories
368
				SET parent_cat = ? WHERE id = ? AND
369
				owner_uid = ?");
370
            $sth->execute([$parent_qpart, $bare_item_id, $_SESSION['uid']]);
371
        }
372
373
        $order_id = 1;
374
375
        $cat = $data_map[$item_id];
376
377
        if ($cat && is_array($cat)) {
378
            foreach ($cat as $item) {
379
                $id = $item['_reference'];
380
                $bare_id = substr($id, strpos($id, ':') + 1);
381
382
                Debug::log("$prefix [$order_id] $id/$bare_id");
383
384
                if ($item['_reference']) {
385
386
                    if (strpos($id, "FEED") === 0) {
387
388
                        $cat_id = ($item_id != "root") ? $bare_item_id : null;
389
390
                        $sth = $this->pdo->prepare("UPDATE ttrss_feeds
391
							SET order_id = ?, cat_id = ?
392
							WHERE id = ? AND owner_uid = ?");
393
394
                        $sth->execute([$order_id, $cat_id ? $cat_id : null, $bare_id, $_SESSION['uid']]);
395
396
                    } else if (strpos($id, "CAT:") === 0) {
397
                        $this->process_category_order($data_map, $item['_reference'], $item_id,
398
                            $nest_level + 1);
399
400
                        $sth = $this->pdo->prepare("UPDATE ttrss_feed_categories
401
								SET order_id = ? WHERE id = ? AND
402
								owner_uid = ?");
403
                        $sth->execute([$order_id, $bare_id, $_SESSION['uid']]);
404
                    }
405
                }
406
407
                ++$order_id;
408
            }
409
        }
410
    }
411
412
    public function savefeedorder() {
413
        $data = json_decode($_POST['payload'], true);
414
415
        #file_put_contents("/tmp/saveorder.json", clean($_POST['payload']));
416
        #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true);
417
418
        if (!is_array($data['items'])) {
419
                    $data['items'] = json_decode($data['items'], true);
420
        }
421
422
#		print_r($data['items']);
423
424
        if (is_array($data) && is_array($data['items'])) {
425
#			$cat_order_id = 0;
426
427
            $data_map = array();
428
            $root_item = false;
429
430
            foreach ($data['items'] as $item) {
431
432
#				if ($item['id'] != 'root') {
433
                    if (is_array($item['items'])) {
434
                        if (isset($item['items']['_reference'])) {
435
                            $data_map[$item['id']] = array($item['items']);
436
                        } else {
437
                            $data_map[$item['id']] = $item['items'];
438
                        }
439
                    }
440
                if ($item['id'] == 'root') {
441
                    $root_item = $item['id'];
442
                }
443
            }
444
445
            $this->process_category_order($data_map, $root_item);
446
        }
447
    }
448
449
    public function removeicon() {
450
        $feed_id = clean($_REQUEST["feed_id"]);
451
452
        $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?");
453
        $sth->execute([$feed_id, $_SESSION['uid']]);
454
455
        if ($sth->fetch()) {
456
            @unlink(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...
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

456
            /** @scrutinizer ignore-unhandled */ @unlink(ICONS_DIR."/$feed_id.ico");

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
457
458
            $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = NULL where id = ?");
459
            $sth->execute([$feed_id]);
460
        }
461
    }
462
463
    public function uploadicon() {
464
        header("Content-type: text/html");
465
466
        if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) {
467
            $tmp_file = tempnam(CACHE_DIR.'/upload', 'icon');
0 ignored issues
show
Bug introduced by
The constant CACHE_DIR was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
468
469
            $result = move_uploaded_file($_FILES['icon_file']['tmp_name'],
470
                $tmp_file);
471
472
            if (!$result) {
473
                return;
474
            }
475
        } else {
476
            return;
477
        }
478
479
        $icon_file = $tmp_file;
480
        $feed_id = clean($_REQUEST["feed_id"]);
481
        $rc = 2; // failed
482
483
        if (is_file($icon_file) && $feed_id) {
484
            if (filesize($icon_file) < 65535) {
485
486
                $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?");
487
                $sth->execute([$feed_id, $_SESSION['uid']]);
488
489
                if ($sth->fetch()) {
490
                    @unlink(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...
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

490
                    /** @scrutinizer ignore-unhandled */ @unlink(ICONS_DIR."/$feed_id.ico");

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
491
                    if (rename($icon_file, ICONS_DIR."/$feed_id.ico")) {
492
493
                        $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET favicon_avg_color = '' WHERE id = ?");
494
                        $sth->execute([$feed_id]);
495
496
                        $rc = 0;
497
                    }
498
                }
499
            } else {
500
                $rc = 1;
501
            }
502
        }
503
504
        if (is_file($icon_file)) {
505
            @unlink($icon_file);
506
        }
507
508
        print $rc;
509
        return;
510
    }
511
512
    public function editfeed() {
513
        global $purge_intervals;
514
        global $update_intervals;
515
516
517
        $feed_id = clean($_REQUEST["id"]);
518
519
        $sth = $this->pdo->prepare("SELECT * FROM ttrss_feeds WHERE id = ? AND
520
				owner_uid = ?");
521
        $sth->execute([$feed_id, $_SESSION['uid']]);
522
523
        if ($row = $sth->fetch()) {
524
            print '<div dojoType="dijit.layout.TabContainer" style="height : 450px">
525
        		<div dojoType="dijit.layout.ContentPane" title="'.__('General').'">';
526
527
            $title = htmlspecialchars($row["title"]);
528
529
            print_hidden("id", "$feed_id");
530
            print_hidden("op", "pref-feeds");
531
            print_hidden("method", "editSave");
532
533
            print "<header>".__("Feed")."</header>";
534
            print "<section>";
535
536
            /* Title */
537
538
            print "<fieldset>";
539
540
            print "<input dojoType='dijit.form.ValidationTextBox' required='1'
541
				placeHolder=\"".__("Feed Title")."\"
542
				style='font-size : 16px; width: 500px' name='title' value=\"$title\">";
543
544
            print "</fieldset>";
545
546
            /* Feed URL */
547
548
            $feed_url = htmlspecialchars($row["feed_url"]);
549
550
            print "<fieldset>";
551
552
            print "<label>".__('URL:')."</label> ";
553
            print "<input dojoType='dijit.form.ValidationTextBox' required='1'
554
				placeHolder=\"".__("Feed URL")."\"
555
				regExp='^(http|https)://.*' style='width : 300px'
556
				name='feed_url' value=\"$feed_url\">";
557
558
            $last_error = $row["last_error"];
559
560
            if ($last_error) {
561
                print "&nbsp;<i class=\"material-icons\"
562
					title=\"".htmlspecialchars($last_error)."\">error</i>";
563
            }
564
565
            print "</fieldset>";
566
567
            /* Category */
568
569
            if (get_pref('ENABLE_FEED_CATS')) {
570
571
                $cat_id = $row["cat_id"];
572
573
                print "<fieldset>";
574
575
                print "<label>".__('Place in category:')."</label> ";
576
577
                print_feed_cat_select("cat_id", $cat_id,
578
                    'dojoType="fox.form.Select"');
579
580
                print "</fieldset>";
581
            }
582
583
            /* Site URL  */
584
585
            $site_url = htmlspecialchars($row["site_url"]);
586
587
            print "<fieldset>";
588
589
            print "<label>".__('Site URL:')."</label> ";
590
            print "<input dojoType='dijit.form.ValidationTextBox' required='1'
591
				placeHolder=\"".__("Site URL")."\"
592
				regExp='^(http|https)://.*' style='width : 300px'
593
				name='site_url' value=\"$site_url\">";
594
595
            print "</fieldset>";
596
597
            /* FTS Stemming Language */
598
599
            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...
600
                $feed_language = $row["feed_language"];
601
602
                if (!$feed_language) {
603
                                    $feed_language = get_pref('DEFAULT_SEARCH_LANGUAGE');
604
                }
605
606
                print "<fieldset>";
607
608
                print "<label>".__('Language:')."</label> ";
609
                print_select("feed_language", $feed_language, $this::get_ts_languages(),
610
                    'dojoType="fox.form.Select"');
611
612
                print "</fieldset>";
613
            }
614
615
            print "</section>";
616
617
            print "<header>".__("Update")."</header>";
618
            print "<section>";
619
620
            /* Update Interval */
621
622
            $update_interval = $row["update_interval"];
623
624
            print "<fieldset>";
625
626
            print "<label>".__("Interval:")."</label> ";
627
628
            print_select_hash("update_interval", $update_interval, $update_intervals,
629
                'dojoType="fox.form.Select"');
630
631
            print "</fieldset>";
632
633
            /* Purge intl */
634
635
            $purge_interval = $row["purge_interval"];
636
637
            print "<fieldset>";
638
639
            print "<label>".__('Article purging:')."</label> ";
640
641
            print_select_hash("purge_interval", $purge_interval, $purge_intervals,
642
                'dojoType="fox.form.Select" '.
643
                ((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"'));
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...
644
645
            print "</fieldset>";
646
647
            print "</section>";
648
649
            $auth_login = htmlspecialchars($row["auth_login"]);
650
            $auth_pass = htmlspecialchars($row["auth_pass"]);
651
652
            $auth_enabled = $auth_login !== '' || $auth_pass !== '';
653
654
            $auth_style = $auth_enabled ? '' : 'display: none';
655
            print "<div id='feedEditDlg_loginContainer' style='$auth_style'>";
656
            print "<header>".__("Authentication")."</header>";
657
            print "<section>";
658
659
            print "<fieldset>";
660
661
            print "<input dojoType='dijit.form.TextBox' id='feedEditDlg_login'
662
				placeHolder='".__("Login")."'
663
				autocomplete='new-password'
664
				name='auth_login' value=\"$auth_login\">";
665
666
            print "</fieldset><fieldset>";
667
668
            print "<input dojoType='dijit.form.TextBox' type='password' name='auth_pass'
669
				autocomplete='new-password'
670
				placeHolder='".__("Password")."'
671
				value=\"$auth_pass\">";
672
673
            print "<div dojoType='dijit.Tooltip' connectId='feedEditDlg_login' position='below'>
674
				".__('<b>Hint:</b> you need to fill in your login information if your feed requires authentication, except for Twitter feeds.')."
675
				</div>";
676
677
            print "</fieldset>";
678
679
            print "</section></div>";
680
681
            $auth_checked = $auth_enabled ? 'checked' : '';
682
            print "<label class='checkbox'>
683
				<input type='checkbox' $auth_checked name='need_auth' dojoType='dijit.form.CheckBox' id='feedEditDlg_loginCheck'
684
						onclick='displayIfChecked(this, \"feedEditDlg_loginContainer\")'>
685
					".__('This feed requires authentication.')."</label>";
686
687
            print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">';
688
689
            print "<section class='narrow'>";
690
691
            $include_in_digest = $row["include_in_digest"];
692
693
            if ($include_in_digest) {
694
                $checked = "checked=\"1\"";
695
            } else {
696
                $checked = "";
697
            }
698
699
            print "<fieldset class='narrow'>";
700
701
            print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"include_in_digest\"
702
				name=\"include_in_digest\"
703
				$checked> ".__('Include in e-mail digest')."</label>";
704
705
            print "</fieldset>";
706
707
            $always_display_enclosures = $row["always_display_enclosures"];
708
709
            if ($always_display_enclosures) {
710
                $checked = "checked";
711
            } else {
712
                $checked = "";
713
            }
714
715
            print "<fieldset class='narrow'>";
716
717
            print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"always_display_enclosures\"
718
				name=\"always_display_enclosures\"
719
				$checked> ".__('Always display image attachments')."</label>";
720
721
            print "</fieldset>";
722
723
            $hide_images = $row["hide_images"];
724
725
            if ($hide_images) {
726
                $checked = "checked=\"1\"";
727
            } else {
728
                $checked = "";
729
            }
730
731
            print "<fieldset class='narrow'>";
732
733
            print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='hide_images'
734
				name='hide_images' $checked> ".__('Do not embed media')."</label>";
735
736
            print "</fieldset>";
737
738
            $cache_images = $row["cache_images"];
739
740
            if ($cache_images) {
741
                $checked = "checked=\"1\"";
742
            } else {
743
                $checked = "";
744
            }
745
746
            print "<fieldset class='narrow'>";
747
748
            print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='cache_images'
749
				name='cache_images' $checked> ".__('Cache media')."</label>";
750
751
            print "</fieldset>";
752
753
            $mark_unread_on_update = $row["mark_unread_on_update"];
754
755
            if ($mark_unread_on_update) {
756
                $checked = "checked";
757
            } else {
758
                $checked = "";
759
            }
760
761
            print "<fieldset class='narrow'>";
762
763
            print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='mark_unread_on_update'
764
				name='mark_unread_on_update' $checked> ".__('Mark updated articles as unread')."</label>";
765
766
            print "</fieldset>";
767
768
            print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Icon').'">';
769
770
            /* Icon */
771
772
            print "<img class='feedIcon feed-editor-icon' src=\"".Feeds::getFeedIcon($feed_id)."\">";
0 ignored issues
show
Bug introduced by
Are you sure Feeds::getFeedIcon($feed_id) of type false|string can be used in concatenation? ( Ignorable by Annotation )

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

772
            print "<img class='feedIcon feed-editor-icon' src=\""./** @scrutinizer ignore-type */ Feeds::getFeedIcon($feed_id)."\">";
Loading history...
773
774
            print "<form onsubmit='return false;' id='feed_icon_upload_form'
775
				enctype='multipart/form-data' method='POST'>
776
			<label class='dijitButton'>".__("Choose file...")."
777
				<input style='display: none' id='icon_file' size='10' name='icon_file' type='file'>
778
			</label>
779
			<input type='hidden' name='op' value='pref-feeds'>
780
			<input type='hidden' name='feed_id' value='$feed_id'>
781
			<input type='hidden' name='method' value='uploadicon'>
782
			<button dojoType='dijit.form.Button' onclick=\"return CommonDialogs.uploadFeedIcon();\"
783
				type='submit'>".__('Replace')."</button>
784
			<button class='alt-danger' dojoType='dijit.form.Button' onclick=\"return CommonDialogs.removeFeedIcon($feed_id);\"
785
				type='submit'>".__('Remove')."</button>
786
			</form>";
787
788
            print "</section>";
789
790
            print '</div><div dojoType="dijit.layout.ContentPane" title="'.__('Plugins').'">';
791
792
            PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED,
793
                "hook_prefs_edit_feed", $feed_id);
794
795
            print "</div></div>";
796
797
            $title = htmlspecialchars($title, ENT_QUOTES);
798
799
            print "<footer>
800
				<button style='float : left' class='alt-danger' dojoType='dijit.form.Button' onclick='return CommonDialogs.unsubscribeFeed($feed_id, \"$title\")'>".
801
                    __('Unsubscribe')."</button>
802
				<button dojoType='dijit.form.Button' class='alt-primary' onclick=\"return dijit.byId('feedEditDlg').execute()\">".__('Save')."</button>
803
				<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('feedEditDlg').hide()\">".__('Cancel')."</button>
804
				</footer>";
805
        }
806
    }
807
808
    public function editfeeds() {
809
        global $purge_intervals;
810
        global $update_intervals;
811
812
        $feed_ids = clean($_REQUEST["ids"]);
813
814
        print_notice("Enable the options you wish to apply using checkboxes on the right:");
0 ignored issues
show
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

814
        /** @scrutinizer ignore-deprecated */ print_notice("Enable the options you wish to apply using checkboxes on the right:");

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...
Unused Code introduced by
The call to print_notice() has too many arguments starting with 'Enable the options you ...eckboxes on the right:'. ( Ignorable by Annotation )

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

814
        /** @scrutinizer ignore-call */ 
815
        print_notice("Enable the options you wish to apply using checkboxes on the right:");

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...
815
816
        print "<p>";
817
818
        print_hidden("ids", "$feed_ids");
819
        print_hidden("op", "pref-feeds");
820
        print_hidden("method", "batchEditSave");
821
822
        print "<header>".__("Feed")."</header>";
823
        print "<section>";
824
825
        /* Category */
826
827
        if (get_pref('ENABLE_FEED_CATS')) {
828
829
            print "<fieldset>";
830
831
            print "<label>".__('Place in category:')."</label> ";
832
833
            print_feed_cat_select("cat_id", false,
834
                'disabled="1" dojoType="fox.form.Select"');
835
836
            $this->batch_edit_cbox("cat_id");
837
838
            print "</fieldset>";
839
        }
840
841
        /* FTS Stemming Language */
842
843
        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...
844
            print "<fieldset>";
845
846
            print "<label>".__('Language:')."</label> ";
847
            print_select("feed_language", "", $this::get_ts_languages(),
848
                'disabled="1" dojoType="fox.form.Select"');
849
850
            $this->batch_edit_cbox("feed_language");
851
852
            print "</fieldset>";
853
        }
854
855
        print "</section>";
856
857
        print "<header>".__("Update")."</header>";
858
        print "<section>";
859
860
        /* Update Interval */
861
862
        print "<fieldset>";
863
864
        print "<label>".__("Interval:")."</label> ";
865
866
        print_select_hash("update_interval", "", $update_intervals,
867
            'disabled="1" dojoType="fox.form.Select"');
868
869
        $this->batch_edit_cbox("update_interval");
870
871
        print "</fieldset>";
872
873
        /* Purge intl */
874
875
        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...
876
877
            print "<fieldset>";
878
879
            print "<label>".__('Article purging:')."</label> ";
880
881
            print_select_hash("purge_interval", "", $purge_intervals,
882
                'disabled="1" dojoType="fox.form.Select"');
883
884
            $this->batch_edit_cbox("purge_interval");
885
886
            print "</fieldset>";
887
        }
888
889
        print "</section>";
890
        print "<header>".__("Authentication")."</header>";
891
        print "<section>";
892
893
        print "<fieldset>";
894
895
        print "<input dojoType='dijit.form.TextBox'
896
			placeHolder=\"".__("Login")."\" disabled='1'
897
			autocomplete='new-password'
898
			name='auth_login' value=''>";
899
900
        $this->batch_edit_cbox("auth_login");
901
902
        print "<input dojoType='dijit.form.TextBox' type='password' name='auth_pass'
903
			autocomplete='new-password'
904
			placeHolder=\"".__("Password")."\" disabled='1'
905
			value=''>";
906
907
        $this->batch_edit_cbox("auth_pass");
908
909
        print "</fieldset>";
910
911
        print "</section>";
912
        print "<header>".__("Options")."</header>";
913
        print "<section>";
914
915
        print "<fieldset class='narrow'>";
916
        print "<label class='checkbox'><input disabled='1' type='checkbox' id='include_in_digest'
917
			name='include_in_digest' dojoType='dijit.form.CheckBox'>&nbsp;".__('Include in e-mail digest')."</label>";
918
919
        print "&nbsp;"; $this->batch_edit_cbox("include_in_digest", "include_in_digest_l");
920
921
        print "</fieldset><fieldset class='narrow'>";
922
923
        print "<label class='checkbox'><input disabled='1' type='checkbox' id='always_display_enclosures'
924
			name='always_display_enclosures' dojoType='dijit.form.CheckBox'>&nbsp;".__('Always display image attachments')."</label>";
925
926
        print "&nbsp;"; $this->batch_edit_cbox("always_display_enclosures", "always_display_enclosures_l");
927
928
        print "</fieldset><fieldset class='narrow'>";
929
930
        print "<label class='checkbox'><input disabled='1' type='checkbox' id='hide_images'
931
			name='hide_images' dojoType='dijit.form.CheckBox'>&nbsp;". __('Do not embed media')."</label>";
932
933
        print "&nbsp;"; $this->batch_edit_cbox("hide_images", "hide_images_l");
934
935
        print "</fieldset><fieldset class='narrow'>";
936
937
        print "<label class='checkbox'><input disabled='1' type='checkbox' id='cache_images'
938
			name='cache_images' dojoType='dijit.form.CheckBox'>&nbsp;".__('Cache media')."</label>";
939
940
        print "&nbsp;"; $this->batch_edit_cbox("cache_images", "cache_images_l");
941
942
        print "</fieldset><fieldset class='narrow'>";
943
944
        print "<label class='checkbox'><input disabled='1' type='checkbox' id='mark_unread_on_update'
945
			name='mark_unread_on_update' dojoType='dijit.form.CheckBox'>&nbsp;".__('Mark updated articles as unread')."</label>";
946
947
        print "&nbsp;"; $this->batch_edit_cbox("mark_unread_on_update", "mark_unread_on_update_l");
948
949
        print "</fieldset>";
950
951
        print "</section>";
952
953
        print "<footer>
954
			<button dojoType='dijit.form.Button' type='submit' class='alt-primary'
955
				onclick=\"return dijit.byId('feedEditDlg').execute()\">".
956
                __('Save')."</button>
957
			<button dojoType='dijit.form.Button'
958
			onclick=\"return dijit.byId('feedEditDlg').hide()\">".
959
                __('Cancel')."</button>
960
			</footer>";
961
962
        return;
963
    }
964
965
    public function batchEditSave() {
966
        return $this->editsaveops(true);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->editsaveops(true) targeting Pref_Feeds::editsaveops() 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...
967
    }
968
969
    public function editSave() {
970
        return $this->editsaveops(false);
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->editsaveops(false) targeting Pref_Feeds::editsaveops() 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...
971
    }
972
973
    public function editsaveops($batch) {
974
975
        $feed_title = trim(clean($_POST["title"]));
0 ignored issues
show
Bug introduced by
It seems like clean($_POST['title']) can also be of type array; however, parameter $str of trim() 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

975
        $feed_title = trim(/** @scrutinizer ignore-type */ clean($_POST["title"]));
Loading history...
976
        $feed_url = trim(clean($_POST["feed_url"]));
977
        $site_url = trim(clean($_POST["site_url"]));
978
        $upd_intl = (int) clean($_POST["update_interval"]);
979
        $purge_intl = (int) clean($_POST["purge_interval"]);
980
        $feed_id = (int) clean($_POST["id"]); /* editSave */
981
        $feed_ids = explode(",", clean($_POST["ids"])); /* batchEditSave */
0 ignored issues
show
Bug introduced by
It seems like clean($_POST['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

981
        $feed_ids = explode(",", /** @scrutinizer ignore-type */ clean($_POST["ids"])); /* batchEditSave */
Loading history...
982
        $cat_id = (int) clean($_POST["cat_id"]);
983
        $auth_login = trim(clean($_POST["auth_login"]));
984
        $auth_pass = trim(clean($_POST["auth_pass"]));
985
        $private = checkbox_to_sql_bool(clean($_POST["private"]));
986
        $include_in_digest = checkbox_to_sql_bool(
987
            clean($_POST["include_in_digest"]));
988
        $cache_images = checkbox_to_sql_bool(
989
            clean($_POST["cache_images"]));
990
        $hide_images = checkbox_to_sql_bool(
991
            clean($_POST["hide_images"]));
992
        $always_display_enclosures = checkbox_to_sql_bool(
993
            clean($_POST["always_display_enclosures"]));
994
995
        $mark_unread_on_update = checkbox_to_sql_bool(
996
            clean($_POST["mark_unread_on_update"]));
997
998
        $feed_language = trim(clean($_POST["feed_language"]));
999
1000
        if (!$batch) {
1001
            if (clean($_POST["need_auth"]) !== 'on') {
1002
                $auth_login = '';
1003
                $auth_pass = '';
1004
            }
1005
1006
            /* $sth = $this->pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ?");
1007
			$sth->execute([$feed_id]);
1008
			$row = $sth->fetch();$orig_feed_url = $row["feed_url"];
1009
1010
			$reset_basic_info = $orig_feed_url != $feed_url; */
1011
1012
            $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET
1013
				cat_id = :cat_id,
1014
				title = :title,
1015
				feed_url = :feed_url,
1016
				site_url = :site_url,
1017
				update_interval = :upd_intl,
1018
				purge_interval = :purge_intl,
1019
				auth_login = :auth_login,
1020
				auth_pass = :auth_pass,
1021
				auth_pass_encrypted = false,
1022
				private = :private,
1023
				cache_images = :cache_images,
1024
				hide_images = :hide_images,
1025
				include_in_digest = :include_in_digest,
1026
				always_display_enclosures = :always_display_enclosures,
1027
				mark_unread_on_update = :mark_unread_on_update,
1028
				feed_language = :feed_language
1029
			WHERE id = :id AND owner_uid = :uid");
1030
1031
            $sth->execute([":title" => $feed_title,
1032
                    ":cat_id" => $cat_id ? $cat_id : null,
1033
                    ":feed_url" => $feed_url,
1034
                    ":site_url" => $site_url,
1035
                    ":upd_intl" => $upd_intl,
1036
                    ":purge_intl" => $purge_intl,
1037
                    ":auth_login" => $auth_login,
1038
                    ":auth_pass" => $auth_pass,
1039
                    ":private" => (int) $private,
1040
                    ":cache_images" => (int) $cache_images,
1041
                    ":hide_images" => (int) $hide_images,
1042
                    ":include_in_digest" => (int) $include_in_digest,
1043
                    ":always_display_enclosures" => (int) $always_display_enclosures,
1044
                    ":mark_unread_on_update" => (int) $mark_unread_on_update,
1045
                    ":feed_language" => $feed_language,
1046
                    ":id" => $feed_id,
1047
                    ":uid" => $_SESSION['uid']]);
1048
1049
/*			if ($reset_basic_info) {
1050
				RSSUtils::set_basic_feed_info($feed_id);
1051
			} */
1052
1053
            PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_SAVE_FEED,
1054
                "hook_prefs_save_feed", $feed_id);
1055
1056
        } else {
1057
            $feed_data = array();
1058
1059
            foreach (array_keys($_POST) as $k) {
1060
                if ($k != "op" && $k != "method" && $k != "ids") {
1061
                    $feed_data[$k] = clean($_POST[$k]);
1062
                }
1063
            }
1064
1065
            $this->pdo->beginTransaction();
1066
1067
            $feed_ids_qmarks = arr_qmarks($feed_ids);
1068
1069
            foreach (array_keys($feed_data) as $k) {
1070
1071
                $qpart = "";
1072
1073
                switch ($k) {
1074
                case "title":
1075
                    $qpart = "title = ".$this->pdo->quote($feed_title);
1076
                    break;
1077
1078
                case "feed_url":
1079
                    $qpart = "feed_url = ".$this->pdo->quote($feed_url);
1080
                    break;
1081
1082
                case "update_interval":
1083
                    $qpart = "update_interval = ".$this->pdo->quote($upd_intl);
1084
                    break;
1085
1086
                case "purge_interval":
1087
                    $qpart = "purge_interval =".$this->pdo->quote($purge_intl);
1088
                    break;
1089
1090
                case "auth_login":
1091
                    $qpart = "auth_login = ".$this->pdo->quote($auth_login);
1092
                    break;
1093
1094
                case "auth_pass":
1095
                    $qpart = "auth_pass =".$this->pdo->quote($auth_pass).", auth_pass_encrypted = false";
1096
                    break;
1097
1098
                case "private":
1099
                    $qpart = "private = ".$this->pdo->quote($private);
1100
                    break;
1101
1102
                case "include_in_digest":
1103
                    $qpart = "include_in_digest = ".$this->pdo->quote($include_in_digest);
1104
                    break;
1105
1106
                case "always_display_enclosures":
1107
                    $qpart = "always_display_enclosures = ".$this->pdo->quote($always_display_enclosures);
1108
                    break;
1109
1110
                case "mark_unread_on_update":
1111
                    $qpart = "mark_unread_on_update = ".$this->pdo->quote($mark_unread_on_update);
1112
                    break;
1113
1114
                case "cache_images":
1115
                    $qpart = "cache_images = ".$this->pdo->quote($cache_images);
1116
                    break;
1117
1118
                case "hide_images":
1119
                    $qpart = "hide_images = ".$this->pdo->quote($hide_images);
1120
                    break;
1121
1122
                case "cat_id":
1123
                    if (get_pref('ENABLE_FEED_CATS')) {
1124
                        if ($cat_id) {
1125
                            $qpart = "cat_id = ".$this->pdo->quote($cat_id);
1126
                        } else {
1127
                            $qpart = 'cat_id = NULL';
1128
                        }
1129
                    } else {
1130
                        $qpart = "";
1131
                    }
1132
1133
                    break;
1134
1135
                case "feed_language":
1136
                    $qpart = "feed_language = ".$this->pdo->quote($feed_language);
1137
                    break;
1138
1139
                }
1140
1141
                if ($qpart) {
1142
                    $sth = $this->pdo->prepare("UPDATE ttrss_feeds SET $qpart WHERE id IN ($feed_ids_qmarks)
1143
						AND owner_uid = ?");
1144
                    $sth->execute(array_merge($feed_ids, [$_SESSION['uid']]));
1145
                }
1146
            }
1147
1148
            $this->pdo->commit();
1149
        }
1150
        return;
1151
    }
1152
1153
    public function remove() {
1154
1155
        $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

1155
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST["ids"]));
Loading history...
1156
1157
        foreach ($ids as $id) {
1158
            Pref_Feeds::remove_feed($id, $_SESSION["uid"]);
1159
        }
1160
1161
        return;
1162
    }
1163
1164
    public function removeCat() {
1165
        $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

1165
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST["ids"]));
Loading history...
1166
        foreach ($ids as $id) {
1167
            $this->remove_feed_category($id, $_SESSION["uid"]);
1168
        }
1169
    }
1170
1171
    public function addCat() {
1172
        $feed_cat = trim(clean($_REQUEST["cat"]));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['cat']) can also be of type array; however, parameter $str of trim() 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

1172
        $feed_cat = trim(/** @scrutinizer ignore-type */ clean($_REQUEST["cat"]));
Loading history...
1173
1174
        Feeds::add_feed_category($feed_cat);
1175
    }
1176
1177
    public function index() {
1178
1179
        print "<div dojoType='dijit.layout.AccordionContainer' region='center'>";
1180
        print "<div style='padding : 0px' dojoType='dijit.layout.AccordionPane'
1181
			title=\"<i class='material-icons'>rss_feed</i> ".__('Feeds')."\">";
1182
1183
        $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors
1184
			FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?");
1185
        $sth->execute([$_SESSION['uid']]);
1186
1187
        if ($row = $sth->fetch()) {
1188
            $num_errors = $row["num_errors"];
1189
        } else {
1190
            $num_errors = 0;
1191
        }
1192
1193
        if ($num_errors > 0) {
1194
1195
            $error_button = "<button dojoType=\"dijit.form.Button\"
1196
			  		onclick=\"CommonDialogs.showFeedsWithErrors()\" id=\"errorButton\">" .
1197
                __("Feeds with errors")."</button>";
1198
        }
1199
1200
        $inactive_button = "<button dojoType=\"dijit.form.Button\"
1201
				id=\"pref_feeds_inactive_btn\"
1202
				style=\"display : none\"
1203
				onclick=\"dijit.byId('feedTree').showInactiveFeeds()\">" .
1204
                __("Inactive feeds")."</button>";
1205
1206
        $feed_search = clean($_REQUEST["search"]);
1207
1208
        if (array_key_exists("search", $_REQUEST)) {
1209
            $_SESSION["prefs_feed_search"] = $feed_search;
1210
        } else {
1211
            $feed_search = $_SESSION["prefs_feed_search"];
1212
        }
1213
1214
        print '<div dojoType="dijit.layout.BorderContainer" gutters="false">';
1215
1216
        print "<div region='top' dojoType=\"fox.Toolbar\">"; #toolbar
1217
1218
        print "<div style='float : right; padding-right : 4px;'>
1219
			<input dojoType=\"dijit.form.TextBox\" id=\"feed_search\" size=\"20\" type=\"search\"
1220
				value=\"$feed_search\">
1221
			<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('feedTree').reload()\">".
1222
                __('Search')."</button>
1223
			</div>";
1224
1225
        print "<div dojoType=\"fox.form.DropDownButton\">".
1226
                "<span>".__('Select')."</span>";
1227
        print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1228
        print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(true)\"
1229
			dojoType=\"dijit.MenuItem\">".__('All')."</div>";
1230
        print "<div onclick=\"dijit.byId('feedTree').model.setAllChecked(false)\"
1231
			dojoType=\"dijit.MenuItem\">".__('None')."</div>";
1232
        print "</div></div>";
1233
1234
        print "<div dojoType=\"fox.form.DropDownButton\">".
1235
                "<span>".__('Feeds')."</span>";
1236
        print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1237
        print "<div onclick=\"CommonDialogs.quickAddFeed()\"
1238
			dojoType=\"dijit.MenuItem\">".__('Subscribe to feed')."</div>";
1239
        print "<div onclick=\"dijit.byId('feedTree').editSelectedFeed()\"
1240
			dojoType=\"dijit.MenuItem\">".__('Edit selected feeds')."</div>";
1241
        print "<div onclick=\"dijit.byId('feedTree').resetFeedOrder()\"
1242
			dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>";
1243
        print "<div onclick=\"dijit.byId('feedTree').batchSubscribe()\"
1244
			dojoType=\"dijit.MenuItem\">".__('Batch subscribe')."</div>";
1245
        print "<div dojoType=\"dijit.MenuItem\" onclick=\"dijit.byId('feedTree').removeSelectedFeeds()\">"
1246
            .__('Unsubscribe')."</div> ";
1247
        print "</div></div>";
1248
1249
        if (get_pref('ENABLE_FEED_CATS')) {
1250
            print "<div dojoType=\"fox.form.DropDownButton\">".
1251
                    "<span>".__('Categories')."</span>";
1252
            print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1253
            print "<div onclick=\"dijit.byId('feedTree').createCategory()\"
1254
				dojoType=\"dijit.MenuItem\">".__('Add category')."</div>";
1255
            print "<div onclick=\"dijit.byId('feedTree').resetCatOrder()\"
1256
				dojoType=\"dijit.MenuItem\">".__('Reset sort order')."</div>";
1257
            print "<div onclick=\"dijit.byId('feedTree').removeSelectedCategories()\"
1258
				dojoType=\"dijit.MenuItem\">".__('Remove selected')."</div>";
1259
            print "</div></div>";
1260
1261
        }
1262
1263
        print $error_button;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $error_button does not seem to be defined for all execution paths leading up to this point.
Loading history...
1264
        print $inactive_button;
1265
1266
        print "</div>"; # toolbar
1267
1268
        //print '</div>';
1269
        print '<div style="padding : 0px" dojoType="dijit.layout.ContentPane" region="center">';
1270
1271
        print "<div id=\"feedlistLoading\">
1272
		<img src='images/indicator_tiny.gif'>".
1273
            __("Loading, please wait...")."</div>";
1274
1275
        $auto_expand = $feed_search != "" ? "true" : "false";
1276
1277
        print "<div dojoType=\"fox.PrefFeedStore\" jsId=\"feedStore\"
1278
			url=\"backend.php?op=pref-feeds&method=getfeedtree\">
1279
		</div>
1280
		<div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"feedModel\" store=\"feedStore\"
1281
		query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Feeds\"
1282
			childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
1283
		</div>
1284
		<div dojoType=\"fox.PrefFeedTree\" id=\"feedTree\"
1285
			dndController=\"dijit.tree.dndSource\"
1286
			betweenThreshold=\"5\"
1287
			autoExpand='$auto_expand'
1288
			model=\"feedModel\" openOnClick=\"false\">
1289
		<script type=\"dojo/method\" event=\"onClick\" args=\"item\">
1290
			var id = String(item.id);
1291
			var bare_id = id.substr(id.indexOf(':')+1);
1292
1293
			if (id.match('FEED:')) {
1294
				CommonDialogs.editFeed(bare_id);
1295
			} else if (id.match('CAT:')) {
1296
				dijit.byId('feedTree').editCategory(bare_id, item);
1297
			}
1298
		</script>
1299
		<script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
1300
			Element.hide(\"feedlistLoading\");
1301
1302
			dijit.byId('feedTree').checkInactiveFeeds();
1303
		</script>
1304
		</div>";
1305
1306
#		print "<div dojoType=\"dijit.Tooltip\" connectId=\"feedTree\" position=\"below\">
1307
#			".__('<b>Hint:</b> you can drag feeds and categories around.')."
1308
#			</div>";
1309
1310
        print '</div>';
1311
        print '</div>';
1312
1313
        print "</div>"; # feeds pane
1314
1315
        print "<div dojoType='dijit.layout.AccordionPane'
1316
			title='<i class=\"material-icons\">import_export</i> ".__('OPML')."'>";
1317
1318
        print "<h3>".__("Using OPML you can export and import your feeds, filters, labels and Tiny Tiny RSS settings.")."</h3>";
1319
1320
        print_notice("Only main settings profile can be migrated using OPML.");
0 ignored issues
show
Unused Code introduced by
The call to print_notice() has too many arguments starting with 'Only main settings prof...e migrated using OPML.'. ( Ignorable by Annotation )

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

1320
        /** @scrutinizer ignore-call */ 
1321
        print_notice("Only main settings profile can be migrated using OPML.");

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

1320
        /** @scrutinizer ignore-deprecated */ print_notice("Only main settings profile can be migrated using OPML.");

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...
1321
1322
        print "<iframe id=\"upload_iframe\"
1323
			name=\"upload_iframe\" onload=\"Helpers.OPML.onImportComplete(this)\"
1324
			style=\"width: 400px; height: 100px; display: none;\"></iframe>";
1325
1326
        print "<form  name='opml_form' style='display : inline-block' target='upload_iframe'
1327
			enctype='multipart/form-data' method='POST'
1328
			action='backend.php'>
1329
			<label class='dijitButton'>".__("Choose file...")."
1330
				<input style='display : none' id='opml_file' name='opml_file' type='file'>&nbsp;
1331
			</label>
1332
			<input type='hidden' name='op' value='dlg'>
1333
			<input type='hidden' name='method' value='importOpml'>
1334
			<button dojoType='dijit.form.Button' class='alt-primary' onclick=\"return Helpers.OPML.import();\" type=\"submit\">" .
1335
            __('Import OPML')."</button>";
1336
1337
        print "</form>";
1338
1339
        print "<form dojoType='dijit.form.Form' id='opmlExportForm' style='display : inline-block'>";
1340
1341
        print "<button dojoType='dijit.form.Button'
1342
			onclick='Helpers.OPML.export()' >" .
1343
            __('Export OPML')."</button>";
1344
1345
        print " <label class='checkbox'>";
1346
        print_checkbox("include_settings", true, "1", "");
1347
        print " ".__("Include settings");
1348
        print "</label>";
1349
1350
        print "</form>";
1351
1352
        print "<p/>";
1353
1354
        print "<h2>".__("Published OPML")."</h2>";
1355
1356
        print "<p>".__('Your OPML can be published publicly and can be subscribed by anyone who knows the URL below.').
1357
            " ".
1358
            __("Published OPML does not include your Tiny Tiny RSS settings, feeds that require authentication or feeds hidden from Popular feeds.")."</p>";
1359
1360
        print "<button dojoType='dijit.form.Button' class='alt-primary' onclick=\"return App.displayDlg('".__("Public OPML URL")."','pubOPMLUrl')\">".
1361
            __('Display published OPML URL')."</button> ";
1362
1363
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
1364
            "hook_prefs_tab_section", "prefFeedsOPML");
1365
1366
        print "</div>"; # pane
1367
1368
        print "<div dojoType=\"dijit.layout.AccordionPane\"
1369
			title=\"<i class='material-icons'>share</i> ".__('Published & shared articles / Generated feeds')."\">";
1370
1371
        print "<h3>".__('Published articles can be subscribed by anyone who knows the following URL:')."</h3>";
1372
1373
        $rss_url = '-2::'.htmlspecialchars(get_self_url_prefix().
1374
                "/public.php?op=rss&id=-2&view-mode=all_articles"); ;
1375
1376
        print "<button dojoType='dijit.form.Button' class='alt-primary' onclick=\"return App.displayDlg('".__("Show as feed")."','generatedFeed', '$rss_url')\">".
1377
            __('Display URL')."</button> ";
1378
1379
        print "<button class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"return Helpers.clearFeedAccessKeys()\">".
1380
            __('Clear all generated URLs')."</button> ";
1381
1382
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB_SECTION,
1383
            "hook_prefs_tab_section", "prefFeedsPublishedGenerated");
1384
1385
        print "</div>"; #pane
1386
1387
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
1388
            "hook_prefs_tab", "prefFeeds");
1389
1390
        print "</div>"; #container
1391
    }
1392
1393
    private function feedlist_init_cat($cat_id) {
1394
        $obj = array();
1395
        $cat_id = (int) $cat_id;
1396
1397
        if ($cat_id > 0) {
1398
            $cat_unread = CCache::find($cat_id, $_SESSION["uid"], true);
1399
        } else if ($cat_id == 0 || $cat_id == -2) {
1400
            $cat_unread = Feeds::getCategoryUnread($cat_id);
1401
        }
1402
1403
        $obj['id'] = 'CAT:'.$cat_id;
1404
        $obj['items'] = array();
1405
        $obj['name'] = Feeds::getCategoryTitle($cat_id);
1406
        $obj['type'] = 'category';
1407
        $obj['unread'] = (int) $cat_unread;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $cat_unread does not seem to be defined for all execution paths leading up to this point.
Loading history...
1408
        $obj['bare_id'] = $cat_id;
1409
1410
        return $obj;
1411
    }
1412
1413
    private function feedlist_init_feed($feed_id, $title = false, $unread = false, $error = '', $updated = '') {
1414
        $obj = array();
1415
        $feed_id = (int) $feed_id;
1416
1417
        if (!$title) {
1418
                    $title = Feeds::getFeedTitle($feed_id, false);
1419
        }
1420
1421
        if ($unread === false) {
1422
                    $unread = getFeedUnread($feed_id, false);
1423
        }
1424
1425
        $obj['id'] = 'FEED:'.$feed_id;
1426
        $obj['name'] = $title;
1427
        $obj['unread'] = (int) $unread;
1428
        $obj['type'] = 'feed';
1429
        $obj['error'] = $error;
1430
        $obj['updated'] = $updated;
1431
        $obj['icon'] = Feeds::getFeedIcon($feed_id);
1432
        $obj['bare_id'] = $feed_id;
1433
        $obj['auxcounter'] = 0;
1434
1435
        return $obj;
1436
    }
1437
1438
    public function inactiveFeeds() {
1439
1440
        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...
1441
            $interval_qpart = "NOW() - INTERVAL '3 months'";
1442
        } else {
1443
            $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
1444
        }
1445
1446
        $sth = $this->pdo->prepare("SELECT ttrss_feeds.title, ttrss_feeds.site_url,
1447
		  		ttrss_feeds.feed_url, ttrss_feeds.id, MAX(updated) AS last_article
1448
			FROM ttrss_feeds, ttrss_entries, ttrss_user_entries WHERE
1449
				(SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
1450
					ttrss_entries.id = ref_id AND
1451
						ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart
1452
			AND ttrss_feeds.owner_uid = ? AND
1453
				ttrss_user_entries.feed_id = ttrss_feeds.id AND
1454
				ttrss_entries.id = ref_id
1455
			GROUP BY ttrss_feeds.title, ttrss_feeds.id, ttrss_feeds.site_url, ttrss_feeds.feed_url
1456
			ORDER BY last_article");
1457
        $sth->execute([$_SESSION['uid']]);
1458
1459
        print "<div dojoType='fox.Toolbar'>";
1460
        print "<div dojoType='fox.form.DropDownButton'>".
1461
                "<span>".__('Select')."</span>";
1462
        print "<div dojoType='dijit.Menu' style='display: none'>";
1463
        print "<div onclick=\"Tables.select('inactive-feeds-list', true)\"
1464
			dojoType='dijit.MenuItem'>".__('All')."</div>";
1465
        print "<div onclick=\"Tables.select('inactive-feeds-list', false)\"
1466
			dojoType='dijit.MenuItem'>".__('None')."</div>";
1467
        print "</div></div>";
1468
        print "</div>"; #toolbar
1469
1470
        print "<div class='panel panel-scrollable'>";
1471
        print "<table width='100%' id='inactive-feeds-list'>";
1472
1473
        $lnum = 1;
1474
1475
        while ($line = $sth->fetch()) {
1476
1477
            $feed_id = $line["id"];
1478
1479
            print "<tr data-row-id='$feed_id'>";
1480
1481
            print "<td width='5%' align='center'><input
1482
				onclick='Tables.onRowChecked(this);' dojoType='dijit.form.CheckBox'
1483
				type='checkbox'></td>";
1484
            print "<td>";
1485
1486
            print "<a href='#' ".
1487
                "title=\"".__("Click to edit feed")."\" ".
1488
                "onclick=\"CommonDialogs.editFeed(".$line["id"].")\">".
1489
                htmlspecialchars($line["title"])."</a>";
1490
1491
            print "</td><td class='text-muted' align='right'>";
1492
            print make_local_datetime($line['last_article'], false);
1493
            print "</td>";
1494
            print "</tr>";
1495
1496
            ++$lnum;
1497
        }
1498
1499
        print "</table>";
1500
        print "</div>";
1501
1502
        print "<footer>
1503
			<button style='float : left' class=\"alt-danger\" dojoType='dijit.form.Button' onclick=\"dijit.byId('inactiveFeedsDlg').removeSelected()\">"
1504
            .__('Unsubscribe from selected feeds')."</button>
1505
			<button dojoType='dijit.form.Button' onclick=\"dijit.byId('inactiveFeedsDlg').hide()\">"
1506
            .__('Close this window')."</button>
1507
			</footer>";
1508
1509
    }
1510
1511
    public function feedsWithErrors() {
1512
        $sth = $this->pdo->prepare("SELECT id,title,feed_url,last_error,site_url
1513
			FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?");
1514
        $sth->execute([$_SESSION['uid']]);
1515
1516
        print "<div dojoType=\"fox.Toolbar\">";
1517
        print "<div dojoType=\"fox.form.DropDownButton\">".
1518
                "<span>".__('Select')."</span>";
1519
        print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
1520
        print "<div onclick=\"Tables.select('error-feeds-list', true)\"
1521
			dojoType=\"dijit.MenuItem\">".__('All')."</div>";
1522
        print "<div onclick=\"Tables.select('error-feeds-list', false)\"
1523
			dojoType=\"dijit.MenuItem\">".__('None')."</div>";
1524
        print "</div></div>";
1525
        print "</div>"; #toolbar
1526
1527
        print "<div class='panel panel-scrollable'>";
1528
        print "<table width='100%' id='error-feeds-list'>";
1529
1530
        $lnum = 1;
1531
1532
        while ($line = $sth->fetch()) {
1533
1534
            $feed_id = $line["id"];
1535
1536
            print "<tr data-row-id='$feed_id'>";
1537
1538
            print "<td width='5%' align='center'><input
1539
				onclick='Tables.onRowChecked(this);' dojoType=\"dijit.form.CheckBox\"
1540
				type=\"checkbox\"></td>";
1541
            print "<td>";
1542
1543
            print "<a class=\"visibleLink\" href=\"#\" ".
1544
                "title=\"".__("Click to edit feed")."\" ".
1545
                "onclick=\"CommonDialogs.editFeed(".$line["id"].")\">".
1546
                htmlspecialchars($line["title"])."</a>: ";
1547
1548
            print "<span class=\"text-muted\">";
1549
            print htmlspecialchars($line["last_error"]);
1550
            print "</span>";
1551
1552
            print "</td>";
1553
            print "</tr>";
1554
1555
            ++$lnum;
1556
        }
1557
1558
        print "</table>";
1559
        print "</div>";
1560
1561
        print "<footer>";
1562
        print "<button style='float : left' class=\"alt-danger\" dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').removeSelected()\">"
1563
            .__('Unsubscribe from selected feeds')."</button> ";
1564
1565
        print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('errorFeedsDlg').hide()\">".
1566
            __('Close this window')."</button>";
1567
1568
        print "</footer>";
1569
    }
1570
1571
    private function remove_feed_category($id, $owner_uid) {
1572
1573
        $sth = $this->pdo->prepare("DELETE FROM ttrss_feed_categories
1574
			WHERE id = ? AND owner_uid = ?");
1575
        $sth->execute([$id, $owner_uid]);
1576
1577
        CCache::remove($id, $owner_uid, true);
1578
    }
1579
1580
    public static function remove_feed($id, $owner_uid) {
1581
        foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_UNSUBSCRIBE_FEED) as $p) {
1582
            if (!$p->hook_unsubscribe_feed($id, $owner_uid)) {
1583
                user_error("feed $id (owner: $owner_uid) not removed due to plugin error (HOOK_UNSUBSCRIBE_FEED).", E_USER_WARNING);
1584
                return;
1585
            }
1586
        }
1587
1588
        $pdo = Db::pdo();
1589
1590
        if ($id > 0) {
1591
            $pdo->beginTransaction();
1592
1593
            /* save starred articles in Archived feed */
1594
1595
            /* prepare feed if necessary */
1596
1597
            $sth = $pdo->prepare("SELECT feed_url FROM ttrss_feeds WHERE id = ?
1598
				AND owner_uid = ?");
1599
            $sth->execute([$id, $owner_uid]);
1600
1601
            if ($row = $sth->fetch()) {
1602
                $feed_url = $row["feed_url"];
1603
1604
                $sth = $pdo->prepare("SELECT id FROM ttrss_archived_feeds
1605
					WHERE feed_url = ? AND owner_uid = ?");
1606
                $sth->execute([$feed_url, $owner_uid]);
1607
1608
                if ($row = $sth->fetch()) {
1609
                    $archive_id = $row["id"];
1610
                } else {
1611
                    $res = $pdo->query("SELECT MAX(id) AS id FROM ttrss_archived_feeds");
1612
                    $row = $res->fetch();
1613
1614
                    $new_feed_id = (int) $row['id'] + 1;
1615
1616
                    $sth = $pdo->prepare("INSERT INTO ttrss_archived_feeds
1617
						(id, owner_uid, title, feed_url, site_url, created)
1618
							SELECT ?, owner_uid, title, feed_url, site_url, NOW() from ttrss_feeds
1619
							WHERE id = ?");
1620
                    $sth->execute([$new_feed_id, $id]);
1621
1622
                    $archive_id = $new_feed_id;
1623
                }
1624
1625
                $sth = $pdo->prepare("UPDATE ttrss_user_entries SET feed_id = NULL,
1626
					orig_feed_id = ? WHERE feed_id = ? AND
1627
						marked = true AND owner_uid = ?");
1628
1629
                $sth->execute([$archive_id, $id, $owner_uid]);
1630
1631
                /* Remove access key for the feed */
1632
1633
                $sth = $pdo->prepare("DELETE FROM ttrss_access_keys WHERE
1634
					feed_id = ? AND owner_uid = ?");
1635
                $sth->execute([$id, $owner_uid]);
1636
1637
                /* remove the feed */
1638
1639
                $sth = $pdo->prepare("DELETE FROM ttrss_feeds
1640
					WHERE id = ? AND owner_uid = ?");
1641
                $sth->execute([$id, $owner_uid]);
1642
            }
1643
1644
            $pdo->commit();
1645
1646
            if (file_exists(ICONS_DIR."/$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...
1647
                unlink(ICONS_DIR."/$id.ico");
1648
            }
1649
1650
            CCache::remove($id, $owner_uid);
1651
1652
        } else {
1653
            Labels::remove(Labels::feed_to_label_id($id), $owner_uid);
1654
            //CCache::remove($id, $owner_uid); don't think labels are cached
1655
        }
1656
    }
1657
1658
    public function batchSubscribe() {
1659
        print_hidden("op", "pref-feeds");
1660
        print_hidden("method", "batchaddfeeds");
1661
1662
        print "<header class='horizontal'>".__("One valid feed per line (no detection is done)")."</header>";
1663
        print "<section>";
1664
1665
        print "<textarea
1666
			style='font-size : 12px; width : 98%; height: 200px;'
1667
			dojoType='dijit.form.SimpleTextarea' name='feeds'></textarea>";
1668
1669
        if (get_pref('ENABLE_FEED_CATS')) {
1670
            print "<fieldset>";
1671
            print "<label>".__('Place in category:')."</label> ";
1672
            print_feed_cat_select("cat", false, 'dojoType="fox.form.Select"');
1673
            print "</fieldset>";
1674
        }
1675
1676
        print "</section>";
1677
1678
        print "<div id='feedDlg_loginContainer' style='display : none'>";
1679
1680
        print "<header>".__("Authentication")."</header>";
1681
        print "<section>";
1682
1683
        print "<input dojoType='dijit.form.TextBox' name='login' placeHolder=\"".__("Login")."\">
1684
			<input placeHolder=\"".__("Password")."\" dojoType=\"dijit.form.TextBox\" type='password'
1685
				autocomplete='new-password' name='pass''></div>";
1686
1687
        print "</section>";
1688
        print "</div>";
1689
1690
        print "<fieldset class='narrow'>
1691
			<label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox'
1692
					onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'> ".
1693
                __('Feeds require authentication.')."</label></div>";
1694
        print "</fieldset>";
1695
1696
        print "<footer>
1697
			<button dojoType='dijit.form.Button' type='submit' class='alt-primary'>".__('Subscribe')."</button>
1698
			<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('batchSubDlg').hide()\">".__('Cancel')."</button>
1699
			</footer>";
1700
    }
1701
1702
    public function batchAddFeeds() {
1703
        $cat_id = clean($_REQUEST['cat']);
1704
        $feeds = explode("\n", clean($_REQUEST['feeds']));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['feeds']) 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

1704
        $feeds = explode("\n", /** @scrutinizer ignore-type */ clean($_REQUEST['feeds']));
Loading history...
1705
        $login = clean($_REQUEST['login']);
1706
        $pass = trim(clean($_REQUEST['pass']));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['pass']) can also be of type array; however, parameter $str of trim() 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

1706
        $pass = trim(/** @scrutinizer ignore-type */ clean($_REQUEST['pass']));
Loading history...
1707
1708
        $csth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
1709
						WHERE feed_url = ? AND owner_uid = ?");
1710
1711
        $isth = $this->pdo->prepare("INSERT INTO ttrss_feeds
1712
							(owner_uid,feed_url,title,cat_id,auth_login,auth_pass,update_method,auth_pass_encrypted)
1713
						VALUES (?, ?, '[Unknown]', ?, ?, ?, 0, false)");
1714
1715
        foreach ($feeds as $feed) {
1716
            $feed = trim($feed);
1717
1718
            if (Feeds::validate_feed_url($feed)) {
1719
1720
                $this->pdo->beginTransaction();
1721
1722
                $csth->execute([$feed, $_SESSION['uid']]);
1723
1724
                if (!$csth->fetch()) {
1725
                    $isth->execute([$_SESSION['uid'], $feed, $cat_id ? $cat_id : null, $login, $pass]);
1726
                }
1727
1728
                $this->pdo->commit();
1729
            }
1730
        }
1731
    }
1732
1733
    public function regenOPMLKey() {
1734
        $this->update_feed_access_key('OPML:Publish',
1735
        false, $_SESSION["uid"]);
1736
1737
        $new_link = Opml::opml_publish_url();
1738
1739
        print json_encode(array("link" => $new_link));
1740
    }
1741
1742
    public function regenFeedKey() {
1743
        $feed_id = clean($_REQUEST['id']);
1744
        $is_cat = clean($_REQUEST['is_cat']);
1745
1746
        $new_key = $this->update_feed_access_key($feed_id, $is_cat);
1747
1748
        print json_encode(["link" => $new_key]);
1749
    }
1750
1751
1752
    private function update_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
1753
        if (!$owner_uid) {
1754
            $owner_uid = $_SESSION["uid"];
1755
        }
1756
1757
        // clear old value and generate new one
1758
        $sth = $this->pdo->prepare("DELETE FROM ttrss_access_keys
1759
			WHERE feed_id = ? AND is_cat = ? AND owner_uid = ?");
1760
        $sth->execute([$feed_id, bool_to_sql_bool($is_cat), $owner_uid]);
1761
1762
        return Feeds::get_feed_access_key($feed_id, $is_cat, $owner_uid);
1763
    }
1764
1765
    // Silent
1766
    public function clearKeys() {
1767
        $sth = $this->pdo->prepare("DELETE FROM ttrss_access_keys WHERE
1768
			owner_uid = ?");
1769
        $sth->execute([$_SESSION['uid']]);
1770
    }
1771
1772
    private function calculate_children_count($cat) {
1773
        $c = 0;
1774
1775
        foreach ($cat['items'] as $child) {
1776
            if ($child['type'] == 'category') {
1777
                $c += $this->calculate_children_count($child);
1778
            } else {
1779
                $c += 1;
1780
            }
1781
        }
1782
1783
        return $c;
1784
    }
1785
1786
    public function getinactivefeeds() {
1787
        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...
1788
            $interval_qpart = "NOW() - INTERVAL '3 months'";
1789
        } else {
1790
            $interval_qpart = "DATE_SUB(NOW(), INTERVAL 3 MONTH)";
1791
        }
1792
1793
        $sth = $this->pdo->prepare("SELECT COUNT(id) AS num_inactive FROM ttrss_feeds WHERE
1794
				(SELECT MAX(updated) FROM ttrss_entries, ttrss_user_entries WHERE
1795
					ttrss_entries.id = ref_id AND
1796
						ttrss_user_entries.feed_id = ttrss_feeds.id) < $interval_qpart AND
1797
			  ttrss_feeds.owner_uid = ?");
1798
        $sth->execute([$_SESSION['uid']]);
1799
1800
        if ($row = $sth->fetch()) {
1801
            print (int) $row["num_inactive"];
1802
        }
1803
    }
1804
1805
    public static function subscribe_to_feed_url() {
1806
        $url_path = get_self_url_prefix().
1807
            "/public.php?op=subscribe&feed_url=%s";
1808
        return $url_path;
1809
    }
1810
1811
}
1812