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, '…'); |
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(); |
|
|
|
|
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
//print __("Articles matching this filter:"); |
174
|
|
|
|
175
|
|
|
print "<div><img id='prefFilterLoadingIndicator' src='images/indicator_tiny.gif'> <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); |
|
|
|
|
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)); |
|
|
|
|
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)); |
|
|
|
|
615
|
|
|
} |
616
|
|
|
|
617
|
|
|
public function editSave() { |
618
|
|
|
if (clean($_REQUEST["savemode"] && $_REQUEST["savemode"]) == "test") { |
619
|
|
|
return $this->testFilter(); |
|
|
|
|
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"])); |
|
|
|
|
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(); |
|
|
|
|
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); |
|
|
|
|
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); |
|
|
|
|
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 " "; // 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"])); |
|
|
|
|
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
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
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.