Pref_Filters::join()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 26
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 17
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 26
rs 9.7
1
<?php
2
class Pref_Filters extends Handler_Protected {
3
4
    public function csrf_ignore($method) {
5
        $csrf_ignored = array("index", "getfiltertree", "edit", "newfilter", "newrule",
6
            "newaction", "savefilterorder");
7
8
        return array_search($method, $csrf_ignored) !== false;
9
    }
10
11
    public function filtersortreset() {
12
        $sth = $this->pdo->prepare("UPDATE ttrss_filters2
13
				SET order_id = 0 WHERE owner_uid = ?");
14
        $sth->execute([$_SESSION['uid']]);
15
        return;
16
    }
17
18
    public function savefilterorder() {
19
        $data = json_decode($_POST['payload'], true);
20
21
        #file_put_contents("/tmp/saveorder.json", clean($_POST['payload']));
22
        #$data = json_decode(file_get_contents("/tmp/saveorder.json"), true);
23
24
        if (!is_array($data['items'])) {
25
                    $data['items'] = json_decode($data['items'], true);
26
        }
27
28
        $index = 0;
29
30
        if (is_array($data) && is_array($data['items'])) {
31
32
            $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET
33
						order_id = ? WHERE id = ? AND
34
						owner_uid = ?");
35
36
            foreach ($data['items'][0]['items'] as $item) {
37
                $filter_id = (int) str_replace("FILTER:", "", $item['_reference']);
38
39
                if ($filter_id > 0) {
40
                    $sth->execute([$index, $filter_id, $_SESSION['uid']]);
41
                    ++$index;
42
                }
43
            }
44
        }
45
46
        return;
47
    }
48
49
    public function testFilterDo() {
50
        $offset = (int) clean($_REQUEST["offset"]);
51
        $limit = (int) clean($_REQUEST["limit"]);
52
53
        $filter = array();
54
55
        $filter["enabled"] = true;
56
        $filter["match_any_rule"] = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"]));
57
        $filter["inverse"] = checkbox_to_sql_bool(clean($_REQUEST["inverse"]));
58
59
        $filter["rules"] = array();
60
        $filter["actions"] = array("dummy-action");
61
62
        $res = $this->pdo->query("SELECT id,name FROM ttrss_filter_types");
63
64
        $filter_types = array();
65
        while ($line = $res->fetch()) {
66
            $filter_types[$line["id"]] = $line["name"];
67
        }
68
69
        $scope_qparts = array();
70
71
        $rctr = 0;
72
        foreach (clean($_REQUEST["rule"]) as $r) {
73
            $rule = json_decode($r, true);
74
75
            if ($rule && $rctr < 5) {
76
                $rule["type"] = $filter_types[$rule["filter_type"]];
77
                unset($rule["filter_type"]);
78
79
                $scope_inner_qparts = [];
80
                foreach ($rule["feed_id"] as $feed_id) {
81
82
                    if (strpos($feed_id, "CAT:") === 0) {
83
                        $cat_id = (int) substr($feed_id, 4);
84
                        array_push($scope_inner_qparts, "cat_id = ".$this->pdo->quote($cat_id));
85
                    } else if ($feed_id > 0) {
86
                        array_push($scope_inner_qparts, "feed_id = ".$this->pdo->quote($feed_id));
87
                    }
88
                }
89
90
                if (count($scope_inner_qparts) > 0) {
91
                    array_push($scope_qparts, "(".implode(" OR ", $scope_inner_qparts).")");
92
                }
93
94
                array_push($filter["rules"], $rule);
95
96
                ++$rctr;
97
            } else {
98
                break;
99
            }
100
        }
101
102
        if (count($scope_qparts) == 0) {
103
            $scope_qparts = ["true"];
104
        }
105
106
        $glue = $filter['match_any_rule'] ? " OR " : " AND ";
107
        $scope_qpart = join($glue, $scope_qparts);
108
109
        if (!$scope_qpart) {
110
            $scope_qpart = "true";
111
        }
112
113
        $rv = array();
114
115
        //while ($found < $limit && $offset < $limit * 1000 && time() - $started < ini_get("max_execution_time") * 0.7) {
116
117
        $sth = $this->pdo->prepare("SELECT ttrss_entries.id,
118
				ttrss_entries.title,
119
				ttrss_feeds.id AS feed_id,
120
				ttrss_feeds.title AS feed_title,
121
				ttrss_feed_categories.id AS cat_id,
122
				content,
123
				date_entered,
124
				link,
125
				author,
126
				tag_cache
127
			FROM
128
				ttrss_entries, ttrss_user_entries
129
					LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)
130
					LEFT JOIN ttrss_feed_categories ON (ttrss_feeds.cat_id = ttrss_feed_categories.id)
131
			WHERE
132
				ref_id = ttrss_entries.id AND
133
				($scope_qpart) AND
134
				ttrss_user_entries.owner_uid = ?
135
			ORDER BY date_entered DESC LIMIT $limit OFFSET $offset");
136
137
        $sth->execute([$_SESSION['uid']]);
138
139
        while ($line = $sth->fetch()) {
140
141
            $rc = RSSUtils::get_article_filters(array($filter), $line['title'], $line['content'], $line['link'],
142
                $line['author'], explode(",", $line['tag_cache']));
143
144
            if (count($rc) > 0) {
145
146
                $line["content_preview"] = truncate_string(strip_tags($line["content"]), 200, '&hellip;');
147
148
                foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
149
                    $line = $p->hook_query_headlines($line, 100);
150
                }
151
152
                $content_preview = $line["content_preview"];
153
154
                $tmp = "<li><span class='title'>".$line["title"]."</span><br/>".
155
                    "<span class='feed'>".$line['feed_title']."</span>, <span class='date'>".mb_substr($line["date_entered"], 0, 16)."</span>".
156
                    "<div class='preview text-muted'>".$content_preview."</div>".
157
                    "</li>";
158
159
                array_push($rv, $tmp);
160
161
            }
162
        }
163
164
        print json_encode($rv);
165
    }
166
167
    public function testFilter() {
168
169
        if (isset($_REQUEST["offset"])) {
170
            return $this->testFilterDo();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->testFilterDo() targeting Pref_Filters::testFilterDo() 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...
171
        }
172
173
        //print __("Articles matching this filter:");
174
175
        print "<div><img id='prefFilterLoadingIndicator' src='images/indicator_tiny.gif'>&nbsp;<span id='prefFilterProgressMsg'>Looking for articles...</span></div>";
176
177
        print "<ul class='panel panel-scrollable list list-unstyled' id='prefFilterTestResultList'>";
178
        print "</ul>";
179
180
        print "<footer class='text-center'>";
181
        print "<button dojoType='dijit.form.Button' onclick=\"dijit.byId('filterTestDlg').hide()\">".
182
            __('Close this window')."</button>";
183
        print "</footer>";
184
185
    }
186
187
    private function getfilterrules_list($filter_id) {
188
        $sth = $this->pdo->prepare("SELECT reg_exp,
189
			inverse,
190
			match_on,
191
			feed_id,
192
			cat_id,
193
			cat_filter,
194
			ttrss_filter_types.description AS field
195
			FROM
196
				ttrss_filters2_rules, ttrss_filter_types
197
			WHERE
198
				filter_id = ? AND filter_type = ttrss_filter_types.id
199
			ORDER BY reg_exp");
200
        $sth->execute([$filter_id]);
201
202
        $rv = "";
203
204
        while ($line = $sth->fetch()) {
205
206
            if ($line["match_on"]) {
207
                $feeds = json_decode($line["match_on"], true);
208
                $feeds_fmt = [];
209
210
                foreach ($feeds as $feed_id) {
211
212
                    if (strpos($feed_id, "CAT:") === 0) {
213
                        $feed_id = (int) substr($feed_id, 4);
214
                        array_push($feeds_fmt, Feeds::getCategoryTitle($feed_id));
215
                    } else {
216
                        if ($feed_id) {
217
                                                    array_push($feeds_fmt, Feeds::getFeedTitle((int) $feed_id));
218
                        } else {
219
                                                    array_push($feeds_fmt, __("All feeds"));
220
                        }
221
                    }
222
                }
223
224
                $where = implode(", ", $feeds_fmt);
225
226
            } else {
227
228
                $where = $line["cat_filter"] ?
229
                    Feeds::getCategoryTitle($line["cat_id"]) : ($line["feed_id"] ?
230
                        Feeds::getFeedTitle($line["feed_id"]) : __("All feeds"));
231
            }
232
233
#			$where = $line["cat_id"] . "/" . $line["feed_id"];
234
235
            $inverse = $line["inverse"] ? "inverse" : "";
236
237
            $rv .= "<li class='$inverse'>".T_sprintf("%s on %s in %s %s",
238
                htmlspecialchars($line["reg_exp"]),
239
                $line["field"],
240
                $where,
241
                $line["inverse"] ? __("(inverse)") : "")."</li>";
242
        }
243
244
        return $rv;
245
    }
246
247
    public function getfiltertree() {
248
        $root = array();
249
        $root['id'] = 'root';
250
        $root['name'] = __('Filters');
251
        $root['items'] = array();
252
253
        $filter_search = $_SESSION["prefs_filter_search"];
254
255
        $sth = $this->pdo->prepare("SELECT *,
256
			(SELECT action_param FROM ttrss_filters2_actions
257
				WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS action_param,
258
			(SELECT action_id FROM ttrss_filters2_actions
259
				WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS action_id,
260
			(SELECT description FROM ttrss_filter_actions
261
				WHERE id = (SELECT action_id FROM ttrss_filters2_actions
262
					WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1)) AS action_name,
263
			(SELECT reg_exp FROM ttrss_filters2_rules
264
				WHERE filter_id = ttrss_filters2.id ORDER BY id LIMIT 1) AS reg_exp
265
			FROM ttrss_filters2 WHERE
266
			owner_uid = ? ORDER BY order_id, title");
267
        $sth->execute([$_SESSION['uid']]);
268
269
        $folder = array();
270
        $folder['items'] = array();
271
272
        while ($line = $sth->fetch()) {
273
274
            $name = $this->getFilterName($line["id"]);
275
276
            $match_ok = false;
277
            if ($filter_search) {
278
                if (mb_strpos($line['title'], $filter_search) !== false) {
279
                    $match_ok = true;
280
                }
281
282
                $rules_sth = $this->pdo->prepare("SELECT reg_exp
283
					FROM ttrss_filters2_rules WHERE filter_id = ?");
284
                $rules_sth->execute([$line['id']]);
285
286
                while ($rule_line = $rules_sth->fetch()) {
287
                    if (mb_strpos($rule_line['reg_exp'], $filter_search) !== false) {
288
                        $match_ok = true;
289
                        break;
290
                    }
291
                }
292
            }
293
294
            if ($line['action_id'] == 7) {
295
                $label_sth = $this->pdo->prepare("SELECT fg_color, bg_color
296
					FROM ttrss_labels2 WHERE caption = ? AND
297
						owner_uid = ?");
298
                $label_sth->execute([$line['action_param'], $_SESSION['uid']]);
299
300
                if ($label_row = $label_sth->fetch()) {
301
                    //$fg_color = $label_row["fg_color"];
302
                    $bg_color = $label_row["bg_color"];
303
304
                    $name[1] = "<i class=\"material-icons\" style='color : $bg_color; margin-right : 4px'>label</i>".$name[1];
305
                }
306
            }
307
308
            $filter = array();
309
            $filter['id'] = 'FILTER:'.$line['id'];
310
            $filter['bare_id'] = $line['id'];
311
            $filter['name'] = $name[0];
312
            $filter['param'] = $name[1];
313
            $filter['checkbox'] = false;
314
            $filter['last_triggered'] = $line["last_triggered"] ? make_local_datetime($line["last_triggered"], false) : null;
315
            $filter['enabled'] = $line["enabled"];
316
            $filter['rules'] = $this->getfilterrules_list($line['id']);
317
318
            if (!$filter_search || $match_ok) {
319
                array_push($folder['items'], $filter);
320
            }
321
        }
322
323
        $root['items'] = $folder['items'];
324
325
        $fl = array();
326
        $fl['identifier'] = 'id';
327
        $fl['label'] = 'name';
328
        $fl['items'] = array($root);
329
330
        print json_encode($fl);
331
        return;
332
    }
333
334
    public function edit() {
335
336
        $filter_id = clean($_REQUEST["id"]);
337
338
        $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2
339
			WHERE id = ? AND owner_uid = ?");
340
        $sth->execute([$filter_id, $_SESSION['uid']]);
341
342
        if ($row = $sth->fetch()) {
343
344
            $enabled = $row["enabled"];
345
            $match_any_rule = $row["match_any_rule"];
346
            $inverse = $row["inverse"];
347
            $title = htmlspecialchars($row["title"]);
348
349
            print "<form id='filter_edit_form' onsubmit='return false'>";
350
351
            print_hidden("op", "pref-filters");
352
            print_hidden("id", "$filter_id");
353
            print_hidden("method", "editSave");
354
            print_hidden("csrf_token", $_SESSION['csrf_token']);
355
356
            print "<header>".__("Caption")."</header>";
357
            print "<section>";
358
            print "<input required=\"true\" dojoType=\"dijit.form.ValidationTextBox\" style=\"width : 20em;\" name=\"title\" value=\"$title\">";
359
            print "</section>";
360
361
            print "<header class='horizontal'>".__("Match")."</header>";
362
            print "<section>";
363
364
            print "<div dojoType=\"fox.Toolbar\">";
365
366
            print "<div dojoType=\"fox.form.DropDownButton\">".
367
                "<span>".__('Select')."</span>";
368
            print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
369
            print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(true)\"
370
			dojoType=\"dijit.MenuItem\">".__('All')."</div>";
371
            print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(false)\"
372
			dojoType=\"dijit.MenuItem\">".__('None')."</div>";
373
            print "</div></div>";
374
375
            print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addRule()\">".
376
                __('Add')."</button> ";
377
378
            print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteRule()\">".
379
                __('Delete')."</button> ";
380
381
            print "</div>";
382
383
            print "<ul id='filterDlg_Matches'>";
384
385
            $rules_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules
386
				  WHERE filter_id = ? ORDER BY reg_exp, id");
387
            $rules_sth->execute([$filter_id]);
388
389
            while ($line = $rules_sth->fetch()) {
390
                if ($line["match_on"]) {
391
                    $line["feed_id"] = json_decode($line["match_on"], true);
392
                } else {
393
                    if ($line["cat_filter"]) {
394
                        $feed_id = "CAT:".(int) $line["cat_id"];
395
                    } else {
396
                        $feed_id = (int) $line["feed_id"];
397
                    }
398
399
                    $line["feed_id"] = ["".$feed_id]; // set item type to string for in_array()
400
                }
401
402
                unset($line["cat_filter"]);
403
                unset($line["cat_id"]);
404
                unset($line["filter_id"]);
405
                unset($line["id"]);
406
                if (!$line["inverse"]) {
407
                    unset($line["inverse"]);
408
                }
409
                unset($line["match_on"]);
410
411
                $data = htmlspecialchars(json_encode($line));
412
413
                print "<li><input dojoType='dijit.form.CheckBox' type='checkbox' onclick='Lists.onRowChecked(this)'>".
414
                    "<span onclick=\"dijit.byId('filterEditDlg').editRule(this)\">".$this->getRuleName($line)."</span>".
415
                    "<input type='hidden' name='rule[]' value=\"$data\"/></li>";
416
            }
417
418
            print "</ul>";
419
420
            print "</section>";
421
422
            print "<header class='horizontal'>".__("Apply actions")."</header>";
423
424
            print "<section>";
425
426
            print "<div dojoType=\"fox.Toolbar\">";
427
428
            print "<div dojoType=\"fox.form.DropDownButton\">".
429
                "<span>".__('Select')."</span>";
430
            print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
431
            print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(true)\"
432
			dojoType=\"dijit.MenuItem\">".__('All')."</div>";
433
            print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(false)\"
434
			dojoType=\"dijit.MenuItem\">".__('None')."</div>";
435
            print "</div></div>";
436
437
            print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').addAction()\">".
438
                __('Add')."</button> ";
439
440
            print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').deleteAction()\">".
441
                __('Delete')."</button> ";
442
443
            print "</div>";
444
445
            print "<ul id='filterDlg_Actions'>";
446
447
            $actions_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions
448
				WHERE filter_id = ? ORDER BY id");
449
            $actions_sth->execute([$filter_id]);
450
451
            while ($line = $actions_sth->fetch()) {
452
                $line["action_param_label"] = $line["action_param"];
453
454
                unset($line["filter_id"]);
455
                unset($line["id"]);
456
457
                $data = htmlspecialchars(json_encode($line));
458
459
                print "<li><input dojoType='dijit.form.CheckBox' type='checkbox' onclick='Lists.onRowChecked(this)'>".
460
                    "<span onclick=\"dijit.byId('filterEditDlg').editAction(this)\">".$this->getActionName($line)."</span>".
461
                    "<input type='hidden' name='action[]' value=\"$data\"/></li>";
462
            }
463
464
            print "</ul>";
465
466
            print "</section>";
467
468
            print "<header>".__("Options")."</header>";
469
            print "<section>";
470
471
            if ($enabled) {
472
                $checked = "checked=\"1\"";
473
            } else {
474
                $checked = "";
475
            }
476
477
            print "<fieldset class='narrow'>";
478
            print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"enabled\" id=\"enabled\" $checked>
479
				".__('Enabled')."</label>";
480
481
            if ($match_any_rule) {
482
                $checked = "checked=\"1\"";
483
            } else {
484
                $checked = "";
485
            }
486
487
            print "</fieldset><fieldset class='narrow'>";
488
489
            print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"match_any_rule\" id=\"match_any_rule\" $checked>
490
				".__('Match any rule')."</label>";
491
492
            print "</fieldset><fieldset class='narrow'>";
493
494
            if ($inverse) {
495
                $checked = "checked=\"1\"";
496
            } else {
497
                $checked = "";
498
            }
499
500
            print "<label class='checkbox'><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" name=\"inverse\" id=\"inverse\" $checked>
501
				".__('Inverse matching')."</label>";
502
503
            print "</fieldset>";
504
505
            print "</section>";
506
507
            print "<footer>";
508
509
            print "<div style=\"float : left\">";
510
            print "<button dojoType=\"dijit.form.Button\" class=\"alt-danger\" onclick=\"return dijit.byId('filterEditDlg').removeFilter()\">".
511
                __('Remove')."</button>";
512
            print "</div>";
513
514
            print "<button dojoType=\"dijit.form.Button\" class=\"alt-info\" onclick=\"return dijit.byId('filterEditDlg').test()\">".
515
                __('Test')."</button> ";
516
517
            print "<button dojoType=\"dijit.form.Button\" type=\"submit\" class=\"alt-primary\" onclick=\"return dijit.byId('filterEditDlg').execute()\">".
518
                __('Save')."</button> ";
519
520
            print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterEditDlg').hide()\">".
521
                __('Cancel')."</button>";
522
523
            print "</footer>";
524
            print "</form>";
525
526
        }
527
    }
528
529
    private function getRuleName($rule) {
530
        if (!$rule) {
531
            $rule = json_decode(clean($_REQUEST["rule"]), true);
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['rule']) can also be of type array; however, parameter $json of json_decode() 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

531
            $rule = json_decode(/** @scrutinizer ignore-type */ clean($_REQUEST["rule"]), true);
Loading history...
532
        }
533
534
        $feeds = $rule["feed_id"];
535
        $feeds_fmt = [];
536
537
        if (!is_array($feeds)) {
538
            $feeds = [$feeds];
539
        }
540
541
        foreach ($feeds as $feed_id) {
542
543
            if (strpos($feed_id, "CAT:") === 0) {
544
                $feed_id = (int) substr($feed_id, 4);
545
                array_push($feeds_fmt, Feeds::getCategoryTitle($feed_id));
546
            } else {
547
                if ($feed_id) {
548
                                    array_push($feeds_fmt, Feeds::getFeedTitle((int) $feed_id));
549
                } else {
550
                                    array_push($feeds_fmt, __("All feeds"));
551
                }
552
            }
553
        }
554
555
        $feed = implode(", ", $feeds_fmt);
556
557
        $sth = $this->pdo->prepare("SELECT description FROM ttrss_filter_types
558
			WHERE id = ?");
559
        $sth->execute([(int) $rule["filter_type"]]);
560
561
        if ($row = $sth->fetch()) {
562
            $filter_type = $row["description"];
563
        } else {
564
            $filter_type = "?UNKNOWN?";
565
        }
566
567
        $inverse = isset($rule["inverse"]) ? "inverse" : "";
568
569
        return "<span class='filterRule $inverse'>".
570
            T_sprintf("%s on %s in %s %s", htmlspecialchars($rule["reg_exp"]),
571
            $filter_type, $feed, isset($rule["inverse"]) ? __("(inverse)") : "")."</span>";
572
    }
573
574
    public function printRuleName() {
575
        print $this->getRuleName(json_decode(clean($_REQUEST["rule"]), true));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['rule']) can also be of type array; however, parameter $json of json_decode() 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

575
        print $this->getRuleName(json_decode(/** @scrutinizer ignore-type */ clean($_REQUEST["rule"]), true));
Loading history...
576
    }
577
578
    private function getActionName($action) {
579
        $sth = $this->pdo->prepare("SELECT description FROM
580
			ttrss_filter_actions WHERE id = ?");
581
        $sth->execute([(int) $action["action_id"]]);
582
583
        $title = "";
584
585
        if ($row = $sth->fetch()) {
586
587
            $title = __($row["description"]);
588
589
            if ($action["action_id"] == 4 || $action["action_id"] == 6 ||
590
                $action["action_id"] == 7) {
591
                            $title .= ": ".$action["action_param"];
592
            }
593
594
            if ($action["action_id"] == 9) {
595
                list ($pfclass, $pfaction) = explode(":", $action["action_param"]);
596
597
                $filter_actions = PluginHost::getInstance()->get_filter_actions();
598
599
                foreach ($filter_actions as $fclass => $factions) {
600
                    foreach ($factions as $faction) {
601
                        if ($pfaction == $faction["action"] && $pfclass == $fclass) {
602
                            $title .= ": ".$fclass.": ".$faction["description"];
603
                            break;
604
                        }
605
                    }
606
                }
607
            }
608
        }
609
610
        return $title;
611
    }
612
613
    public function printActionName() {
614
        print $this->getActionName(json_decode(clean($_REQUEST["action"]), true));
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['action']) can also be of type array; however, parameter $json of json_decode() 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

614
        print $this->getActionName(json_decode(/** @scrutinizer ignore-type */ clean($_REQUEST["action"]), true));
Loading history...
615
    }
616
617
    public function editSave() {
618
        if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") {
619
            return $this->testFilter();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->testFilter() targeting Pref_Filters::testFilter() 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...
620
        }
621
622
        $filter_id = clean($_REQUEST["id"]);
623
        $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"]));
624
        $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"]));
625
        $inverse = checkbox_to_sql_bool(clean($_REQUEST["inverse"]));
626
        $title = clean($_REQUEST["title"]);
627
628
        $this->pdo->beginTransaction();
629
630
        $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET enabled = ?,
631
			match_any_rule = ?,
632
			inverse = ?,
633
			title = ?
634
			WHERE id = ? AND owner_uid = ?");
635
636
        $sth->execute([$enabled, $match_any_rule, $inverse, $title, $filter_id, $_SESSION['uid']]);
637
638
        $this->saveRulesAndActions($filter_id);
639
640
        $this->pdo->commit();
641
    }
642
643
    public function remove() {
644
645
        $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

645
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST["ids"]));
Loading history...
646
        $ids_qmarks = arr_qmarks($ids);
647
648
        $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2 WHERE id IN ($ids_qmarks)
649
			AND owner_uid = ?");
650
        $sth->execute(array_merge($ids, [$_SESSION['uid']]));
651
    }
652
653
    private function saveRulesAndActions($filter_id)
654
    {
655
656
        $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_rules WHERE filter_id = ?");
657
        $sth->execute([$filter_id]);
658
659
        $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_actions WHERE filter_id = ?");
660
        $sth->execute([$filter_id]);
661
662
        if (!is_array(clean($_REQUEST["rule"]))) {
663
            $_REQUEST["rule"] = [];
664
        }
665
        if (!is_array(clean($_REQUEST["action"]))) {
666
            $_REQUEST["action"] = [];
667
        }
668
669
        if ($filter_id) {
670
            /* create rules */
671
672
            $rules = array();
673
            $actions = array();
674
675
            foreach (clean($_REQUEST["rule"]) as $rule) {
676
                $rule = json_decode($rule, true);
677
                unset($rule["id"]);
678
679
                if (array_search($rule, $rules) === false) {
680
                    array_push($rules, $rule);
681
                }
682
            }
683
684
            foreach (clean($_REQUEST["action"]) as $action) {
685
                $action = json_decode($action, true);
686
                unset($action["id"]);
687
688
                if (array_search($action, $actions) === false) {
689
                    array_push($actions, $action);
690
                }
691
            }
692
693
            $rsth = $this->pdo->prepare("INSERT INTO ttrss_filters2_rules
694
						(filter_id, reg_exp,filter_type,feed_id,cat_id,match_on,inverse) VALUES
695
						(?, ?, ?, NULL, NULL, ?, ?)");
696
697
            foreach ($rules as $rule) {
698
                if ($rule) {
699
700
                    $reg_exp = trim($rule["reg_exp"]);
701
                    $inverse = isset($rule["inverse"]) ? 1 : 0;
702
703
                    $filter_type = (int) trim($rule["filter_type"]);
704
                    $match_on = json_encode($rule["feed_id"]);
705
706
                    $rsth->execute([$filter_id, $reg_exp, $filter_type, $match_on, $inverse]);
707
                }
708
            }
709
710
            $asth = $this->pdo->prepare("INSERT INTO ttrss_filters2_actions
711
						(filter_id, action_id, action_param) VALUES
712
						(?, ?, ?)");
713
714
            foreach ($actions as $action) {
715
                if ($action) {
716
717
                    $action_id = (int) $action["action_id"];
718
                    $action_param = $action["action_param"];
719
                    $action_param_label = $action["action_param_label"];
720
721
                    if ($action_id == 7) {
722
                        $action_param = $action_param_label;
723
                    }
724
725
                    if ($action_id == 6) {
726
                        $action_param = (int) str_replace("+", "", $action_param);
727
                    }
728
729
                    $asth->execute([$filter_id, $action_id, $action_param]);
730
                }
731
            }
732
        }
733
    }
734
735
    public function add() {
736
        if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") {
737
            return $this->testFilter();
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->testFilter() targeting Pref_Filters::testFilter() 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...
738
        }
739
740
        $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"]));
741
        $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"]));
742
        $title = clean($_REQUEST["title"]);
743
        $inverse = checkbox_to_sql_bool(clean($_REQUEST["inverse"]));
744
745
        $this->pdo->beginTransaction();
746
747
        /* create base filter */
748
749
        $sth = $this->pdo->prepare("INSERT INTO ttrss_filters2
750
			(owner_uid, match_any_rule, enabled, title, inverse) VALUES
751
			(?, ?, ?, ?, ?)");
752
753
        $sth->execute([$_SESSION['uid'], $match_any_rule, $enabled, $title, $inverse]);
754
755
        $sth = $this->pdo->prepare("SELECT MAX(id) AS id FROM ttrss_filters2
756
			WHERE owner_uid = ?");
757
        $sth->execute([$_SESSION['uid']]);
758
759
        if ($row = $sth->fetch()) {
760
            $filter_id = $row['id'];
761
            $this->saveRulesAndActions($filter_id);
762
        }
763
764
        $this->pdo->commit();
765
    }
766
767
    public function index() {
768
769
        $filter_search = clean($_REQUEST["search"]);
770
771
        if (array_key_exists("search", $_REQUEST)) {
772
            $_SESSION["prefs_filter_search"] = $filter_search;
773
        } else {
774
            $filter_search = $_SESSION["prefs_filter_search"];
775
        }
776
777
        print "<div dojoType='dijit.layout.BorderContainer' gutters='false'>";
778
        print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='top'>";
779
        print "<div dojoType='fox.Toolbar'>";
780
781
        if (array_key_exists("search", $_REQUEST)) {
782
            $_SESSION["prefs_filter_search"] = $filter_search;
783
        } else {
784
            $filter_search = $_SESSION["prefs_filter_search"];
785
        }
786
787
        print "<div style='float : right; padding-right : 4px;'>
788
			<input dojoType=\"dijit.form.TextBox\" id=\"filter_search\" size=\"20\" type=\"search\"
789
				value=\"$filter_search\">
790
			<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('filterTree').reload()\">".
791
                __('Search')."</button>
792
			</div>";
793
794
        print "<div dojoType=\"fox.form.DropDownButton\">".
795
                "<span>".__('Select')."</span>";
796
        print "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
797
        print "<div onclick=\"dijit.byId('filterTree').model.setAllChecked(true)\"
798
			dojoType=\"dijit.MenuItem\">".__('All')."</div>";
799
        print "<div onclick=\"dijit.byId('filterTree').model.setAllChecked(false)\"
800
			dojoType=\"dijit.MenuItem\">".__('None')."</div>";
801
        print "</div></div>";
802
803
        print "<button dojoType=\"dijit.form.Button\" onclick=\"return Filters.quickAddFilter()\">".
804
            __('Create filter')."</button> ";
805
806
        print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterTree').joinSelectedFilters()\">".
807
            __('Combine')."</button> ";
808
809
        print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterTree').editSelectedFilter()\">".
810
            __('Edit')."</button> ";
811
812
        print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterTree').resetFilterOrder()\">".
813
            __('Reset sort order')."</button> ";
814
815
816
        print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterTree').removeSelectedFilters()\">".
817
            __('Remove')."</button> ";
818
819
        print "</div>"; # toolbar
820
        print "</div>"; # toolbar-frame
821
        print "<div style='padding : 0px' dojoType='dijit.layout.ContentPane' region='center'>";
822
823
        print "<div id='filterlistLoading'>
824
		<img src='images/indicator_tiny.gif'>".
825
            __("Loading, please wait...")."</div>";
826
827
        print "<div dojoType=\"fox.PrefFilterStore\" jsId=\"filterStore\"
828
			url=\"backend.php?op=pref-filters&method=getfiltertree\">
829
		</div>
830
		<div dojoType=\"lib.CheckBoxStoreModel\" jsId=\"filterModel\" store=\"filterStore\"
831
			query=\"{id:'root'}\" rootId=\"root\" rootLabel=\"Filters\"
832
			childrenAttrs=\"items\" checkboxStrict=\"false\" checkboxAll=\"false\">
833
		</div>
834
		<div dojoType=\"fox.PrefFilterTree\" id=\"filterTree\"
835
			dndController=\"dijit.tree.dndSource\"
836
			betweenThreshold=\"5\"
837
			model=\"filterModel\" openOnClick=\"true\">
838
		<script type=\"dojo/method\" event=\"onLoad\" args=\"item\">
839
			Element.hide(\"filterlistLoading\");
840
		</script>
841
		<script type=\"dojo/method\" event=\"onClick\" args=\"item\">
842
			var id = String(item.id);
843
			var bare_id = id.substr(id.indexOf(':')+1);
844
845
			if (id.match('FILTER:')) {
846
				dijit.byId('filterTree').editFilter(bare_id);
847
			}
848
		</script>
849
850
		</div>";
851
852
        print "</div>"; #pane
853
854
        PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_TAB,
855
            "hook_prefs_tab", "prefFilters");
856
857
        print "</div>"; #container
858
859
    }
860
861
    public function newfilter() {
862
863
        print "<form name='filter_new_form' id='filter_new_form' onsubmit='return false'>";
864
865
        print_hidden("op", "pref-filters");
866
        print_hidden("method", "add");
867
        print_hidden("csrf_token", $_SESSION['csrf_token']);
868
869
        print "<header>".__("Caption")."</header>";
870
871
        print "<section>";
872
        print "<input required='true' dojoType='dijit.form.ValidationTextBox' style='width : 20em;' name='title' value=''>";
873
        print "</section>";
874
875
        print "<header class='horizontal'>".__("Match")."</header >";
876
        print "<section>";
877
878
        print "<div dojoType='fox.Toolbar'>";
879
880
        print "<div dojoType='fox.form.DropDownButton'>".
881
                "<span>".__('Select')."</span>";
882
        print "<div dojoType='dijit.Menu' style='display: none'>";
883
        print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(true)\"
884
			dojoType='dijit.MenuItem'>".__('All')."</div>";
885
        print "<div onclick=\"dijit.byId('filterEditDlg').selectRules(false)\"
886
			dojoType='dijit.MenuItem'>".__('None')."</div>";
887
        print "</div></div>";
888
889
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').addRule()\">".
890
            __('Add')."</button> ";
891
892
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').deleteRule()\">".
893
            __('Delete')."</button> ";
894
895
        print "</div>";
896
897
        print "<ul id='filterDlg_Matches'>";
898
#		print "<li>No rules</li>";
899
        print "</ul>";
900
901
        print "</section>";
902
903
        print "<header class='horizontal'>".__("Apply actions")."</header>";
904
905
        print "<section>";
906
907
        print "<div dojoType='fox.Toolbar'>";
908
909
        print "<div dojoType='fox.form.DropDownButton'>".
910
                "<span>".__('Select')."</span>";
911
        print "<div dojoType='dijit.Menu' style='display: none'>";
912
        print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(true)\"
913
			dojoType='dijit.MenuItem'>".__('All')."</div>";
914
        print "<div onclick=\"dijit.byId('filterEditDlg').selectActions(false)\"
915
			dojoType='dijit.MenuItem'>".__('None')."</div>";
916
        print "</div></div>";
917
918
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').addAction()\">".
919
            __('Add')."</button> ";
920
921
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').deleteAction()\">".
922
            __('Delete')."</button> ";
923
924
        print "</div>";
925
926
        print "<ul id='filterDlg_Actions'>";
927
#		print "<li>No actions</li>";
928
        print "</ul>";
929
930
        print "</section>";
931
932
        print "<header>".__("Options")."</header>";
933
934
        print "<section>";
935
        print "<fieldset class='narrow'>";
936
937
        print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' name='enabled' id='enabled' checked='1'>
938
				".__('Enabled')."</label>";
939
940
        print "</fieldset><fieldset class='narrow'>";
941
942
        print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' name='match_any_rule' id='match_any_rule'>
943
				".__('Match any rule')."</label>";
944
945
        print "</fieldset><fieldset class='narrow'>";
946
947
        print "<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' name='inverse' id='inverse'>
948
				".__('Inverse matching')."</label>";
949
950
        print "</fieldset>";
951
952
        print "</section>";
953
954
        print "<footer>";
955
956
        print "<button dojoType='dijit.form.Button' class='alt-info' onclick=\"return dijit.byId('filterEditDlg').test()\">".
957
            __('Test')."</button> ";
958
        print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"return dijit.byId('filterEditDlg').execute()\">".
959
            __('Create')."</button> ";
960
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterEditDlg').hide()\">".
961
            __('Cancel')."</button>";
962
963
        print "</footer>";
964
965
    }
966
967
    public function newrule() {
968
        $rule = json_decode(clean($_REQUEST["rule"]), true);
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['rule']) can also be of type array; however, parameter $json of json_decode() 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

968
        $rule = json_decode(/** @scrutinizer ignore-type */ clean($_REQUEST["rule"]), true);
Loading history...
969
970
        if ($rule) {
971
            $reg_exp = htmlspecialchars($rule["reg_exp"]);
972
            $filter_type = $rule["filter_type"];
973
            $feed_id = $rule["feed_id"];
974
            $inverse_checked = isset($rule["inverse"]) ? "checked" : "";
975
        } else {
976
            $reg_exp = "";
977
            $filter_type = 1;
978
            $feed_id = ["0"];
979
            $inverse_checked = "";
980
        }
981
982
        print "<form name='filter_new_rule_form' id='filter_new_rule_form' onsubmit='return false;'>";
983
984
        $res = $this->pdo->query("SELECT id,description
985
			FROM ttrss_filter_types WHERE id != 5 ORDER BY description");
986
987
        $filter_types = array();
988
989
        while ($line = $res->fetch()) {
990
            $filter_types[$line["id"]] = __($line["description"]);
991
        }
992
993
        print "<header>".__("Match")."</header>";
994
995
        print "<section>";
996
997
        print "<input dojoType=\"dijit.form.ValidationTextBox\"
998
			 required=\"true\" id=\"filterDlg_regExp\"
999
			 onchange='Filters.filterDlgCheckRegExp(this)'
1000
			 onblur='Filters.filterDlgCheckRegExp(this)'
1001
			 onfocus='Filters.filterDlgCheckRegExp(this)'
1002
			 style=\"font-size : 16px; width : 500px\"
1003
			 name=\"reg_exp\" value=\"$reg_exp\"/>";
1004
1005
        print "<div dojoType='dijit.Tooltip' id='filterDlg_regExp_tip' connectId='filterDlg_regExp' position='below'></div>";
1006
1007
        print "<fieldset>";
1008
        print "<label class='checkbox'><input id=\"filterDlg_inverse\" dojoType=\"dijit.form.CheckBox\"
1009
			 name=\"inverse\" $inverse_checked/> ".
1010
                __("Inverse regular expression matching")."</label>";
1011
        print "</fieldset>";
1012
1013
        print "<fieldset>";
1014
        print "<label style='display : inline'>".__("on field")."</label> ";
1015
        print_select_hash("filter_type", $filter_type, $filter_types,
1016
            'dojoType="fox.form.Select"');
1017
        print "<label style='padding-left : 10px; display : inline'>".__("in")."</label> ";
1018
1019
        print "</fieldset>";
1020
1021
        print "<fieldset>";
1022
        print "<span id='filterDlg_feeds'>";
1023
        print_feed_multi_select("feed_id",
1024
            $feed_id,
1025
            'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"');
1026
        print "</span>";
1027
1028
        print "</fieldset>";
1029
1030
        print "</section>";
1031
1032
        print "<footer>";
1033
1034
        print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/ContentFilters\")'>
1035
			<i class='material-icons'>help</i> ".__("More info...")."</button>";
1036
1037
        print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"return dijit.byId('filterNewRuleDlg').execute()\">".
1038
            ($rule ? __("Save rule") : __('Add rule'))."</button> ";
1039
1040
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterNewRuleDlg').hide()\">".
1041
            __('Cancel')."</button>";
1042
1043
        print "</footer>";
1044
1045
        print "</form>";
1046
    }
1047
1048
    public function newaction() {
1049
        $action = json_decode(clean($_REQUEST["action"]), true);
0 ignored issues
show
Bug introduced by
It seems like clean($_REQUEST['action']) can also be of type array; however, parameter $json of json_decode() 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

1049
        $action = json_decode(/** @scrutinizer ignore-type */ clean($_REQUEST["action"]), true);
Loading history...
1050
1051
        if ($action) {
1052
            $action_param = $action["action_param"];
1053
            $action_id = (int) $action["action_id"];
1054
        } else {
1055
            $action_param = "";
1056
            $action_id = 0;
1057
        }
1058
1059
        print "<form name='filter_new_action_form' id='filter_new_action_form' onsubmit='return false;'>";
1060
1061
        print "<header>".__("Perform Action")."</header>";
1062
1063
        print "<section>";
1064
1065
        print "<select name='action_id' dojoType='fox.form.Select'
1066
			onchange='Filters.filterDlgCheckAction(this)'>";
1067
1068
        $res = $this->pdo->query("SELECT id,description FROM ttrss_filter_actions
1069
			ORDER BY name");
1070
1071
        while ($line = $res->fetch()) {
1072
            $is_selected = ($line["id"] == $action_id) ? "selected='1'" : "";
1073
            printf("<option $is_selected value='%d'>%s</option>", $line["id"], __($line["description"]));
1074
        }
1075
1076
        print "</select>";
1077
1078
        $param_box_hidden = ($action_id == 7 || $action_id == 4 || $action_id == 6 || $action_id == 9) ?
1079
            "" : "display : none";
1080
1081
        $param_hidden = ($action_id == 4 || $action_id == 6) ?
1082
            "" : "display : none";
1083
1084
        $label_param_hidden = ($action_id == 7) ? "" : "display : none";
1085
        $plugin_param_hidden = ($action_id == 9) ? "" : "display : none";
1086
1087
        print "<span id='filterDlg_paramBox' style=\"$param_box_hidden\">";
1088
        print " ";
1089
        //print " " . __("with parameters:") . " ";
1090
        print "<input dojoType='dijit.form.TextBox'
1091
			id='filterDlg_actionParam' style=\"$param_hidden\"
1092
			name='action_param' value=\"$action_param\">";
1093
1094
        print_label_select("action_param_label", $action_param,
1095
            "id='filterDlg_actionParamLabel' style=\"$label_param_hidden\"
1096
			dojoType='fox.form.Select'");
1097
1098
        $filter_actions = PluginHost::getInstance()->get_filter_actions();
1099
        $filter_action_hash = array();
1100
1101
        foreach ($filter_actions as $fclass => $factions) {
1102
            foreach ($factions as $faction) {
1103
1104
                $filter_action_hash[$fclass.":".$faction["action"]] =
1105
                    $fclass.": ".$faction["description"];
1106
            }
1107
        }
1108
1109
        if (count($filter_action_hash) == 0) {
1110
            $filter_plugin_disabled = "disabled";
1111
1112
            $filter_action_hash["no-data"] = __("No actions available");
1113
1114
        } else {
1115
            $filter_plugin_disabled = "";
1116
        }
1117
1118
        print_select_hash("filterDlg_actionParamPlugin", $action_param, $filter_action_hash,
1119
            "style=\"$plugin_param_hidden\" dojoType='fox.form.Select' $filter_plugin_disabled",
1120
            "action_param_plugin");
1121
1122
        print "</span>";
1123
1124
        print "&nbsp;"; // tiny layout hack
1125
1126
        print "</section>";
1127
1128
        print "<footer>";
1129
1130
        print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit' onclick=\"return dijit.byId('filterNewActionDlg').execute()\">".
1131
            ($action ? __("Save action") : __('Add action'))."</button> ";
1132
1133
        print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('filterNewActionDlg').hide()\">".
1134
            __('Cancel')."</button>";
1135
1136
        print "</footer>";
1137
1138
        print "</form>";
1139
    }
1140
1141
    private function getFilterName($id) {
1142
1143
        $sth = $this->pdo->prepare(
1144
            "SELECT title,match_any_rule,f.inverse AS inverse,COUNT(DISTINCT r.id) AS num_rules,COUNT(DISTINCT a.id) AS num_actions
1145
				FROM ttrss_filters2 AS f LEFT JOIN ttrss_filters2_rules AS r
1146
					ON (r.filter_id = f.id)
1147
						LEFT JOIN ttrss_filters2_actions AS a
1148
							ON (a.filter_id = f.id) WHERE f.id = ? GROUP BY f.title, f.match_any_rule, f.inverse");
1149
        $sth->execute([$id]);
1150
1151
        if ($row = $sth->fetch()) {
1152
1153
            $title = $row["title"];
1154
            $num_rules = $row["num_rules"];
1155
            $num_actions = $row["num_actions"];
1156
            $match_any_rule = $row["match_any_rule"];
1157
            $inverse = $row["inverse"];
1158
1159
            if (!$title) {
1160
                $title = __("[No caption]");
1161
            }
1162
1163
            $title = sprintf(_ngettext("%s (%d rule)", "%s (%d rules)", (int) $num_rules), $title, $num_rules);
1164
1165
            $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions
1166
				WHERE filter_id = ? ORDER BY id LIMIT 1");
1167
            $sth->execute([$id]);
1168
1169
            $actions = "";
1170
1171
            if ($line = $sth->fetch()) {
1172
                $actions = $this->getActionName($line);
1173
1174
                $num_actions -= 1;
1175
            }
1176
1177
            if ($match_any_rule) {
1178
                $title .= " (".__("matches any rule").")";
1179
            }
1180
            if ($inverse) {
1181
                $title .= " (".__("inverse").")";
1182
            }
1183
1184
            if ($num_actions > 0) {
1185
                            $actions = sprintf(_ngettext("%s (+%d action)", "%s (+%d actions)", (int) $num_actions), $actions, $num_actions);
1186
            }
1187
1188
            return [$title, $actions];
1189
        }
1190
1191
        return [];
1192
    }
1193
1194
    public function join() {
1195
        $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

1195
        $ids = explode(",", /** @scrutinizer ignore-type */ clean($_REQUEST["ids"]));
Loading history...
1196
1197
        if (count($ids) > 1) {
1198
            $base_id = array_shift($ids);
1199
            $ids_qmarks = arr_qmarks($ids);
1200
1201
            $this->pdo->beginTransaction();
1202
1203
            $sth = $this->pdo->prepare("UPDATE ttrss_filters2_rules
1204
				SET filter_id = ? WHERE filter_id IN ($ids_qmarks)");
1205
            $sth->execute(array_merge([$base_id], $ids));
1206
1207
            $sth = $this->pdo->prepare("UPDATE ttrss_filters2_actions
1208
				SET filter_id = ? WHERE filter_id IN ($ids_qmarks)");
1209
            $sth->execute(array_merge([$base_id], $ids));
1210
1211
            $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2 WHERE id IN ($ids_qmarks)");
1212
            $sth->execute($ids);
1213
1214
            $sth = $this->pdo->prepare("UPDATE ttrss_filters2 SET match_any_rule = true WHERE id = ?");
1215
            $sth->execute([$base_id]);
1216
1217
            $this->pdo->commit();
1218
1219
            $this->optimizeFilter($base_id);
1220
1221
        }
1222
    }
1223
1224
    private function optimizeFilter($id) {
1225
1226
        $this->pdo->beginTransaction();
1227
1228
        $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions
1229
			WHERE filter_id = ?");
1230
        $sth->execute([$id]);
1231
1232
        $tmp = array();
1233
        $dupe_ids = array();
1234
1235
        while ($line = $sth->fetch()) {
1236
            $id = $line["id"];
1237
            unset($line["id"]);
1238
1239
            if (array_search($line, $tmp) === false) {
1240
                array_push($tmp, $line);
1241
            } else {
1242
                array_push($dupe_ids, $id);
1243
            }
1244
        }
1245
1246
        if (count($dupe_ids) > 0) {
1247
            $ids_str = join(",", $dupe_ids);
1248
1249
            $this->pdo->query("DELETE FROM ttrss_filters2_actions WHERE id IN ($ids_str)");
1250
        }
1251
1252
        $sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules
1253
			WHERE filter_id = ?");
1254
        $sth->execute([$id]);
1255
1256
        $tmp = array();
1257
        $dupe_ids = array();
1258
1259
        while ($line = $sth->fetch()) {
1260
            $id = $line["id"];
1261
            unset($line["id"]);
1262
1263
            if (array_search($line, $tmp) === false) {
1264
                array_push($tmp, $line);
1265
            } else {
1266
                array_push($dupe_ids, $id);
1267
            }
1268
        }
1269
1270
        if (count($dupe_ids) > 0) {
1271
            $ids_str = join(",", $dupe_ids);
1272
1273
            $this->pdo->query("DELETE FROM ttrss_filters2_rules WHERE id IN ($ids_str)");
1274
        }
1275
1276
        $this->pdo->commit();
1277
    }
1278
}
1279