|
1
|
|
|
<?php |
|
2
|
|
|
require_once "colors.php"; |
|
3
|
|
|
|
|
4
|
|
|
class Feeds extends Handler_Protected { |
|
5
|
|
|
const NEVER_GROUP_FEEDS = [ -6, 0]; |
|
6
|
|
|
const NEVER_GROUP_BY_DATE = [ -2, -1, -3]; |
|
7
|
|
|
|
|
8
|
|
|
private $params; |
|
9
|
|
|
|
|
10
|
|
|
function csrf_ignore($method) { |
|
11
|
|
|
$csrf_ignored = array("index", "quickaddfeed", "search"); |
|
12
|
|
|
|
|
13
|
|
|
return array_search($method, $csrf_ignored) !== false; |
|
14
|
|
|
} |
|
15
|
|
|
|
|
16
|
|
|
private function format_headline_subtoolbar($feed_site_url, $feed_title, |
|
17
|
|
|
$feed_id, $is_cat, $search, |
|
18
|
|
|
$error, $feed_last_updated) { |
|
19
|
|
|
|
|
20
|
|
|
if ($is_cat) { |
|
21
|
|
|
$cat_q = "&is_cat=$is_cat"; |
|
22
|
|
|
} |
|
23
|
|
|
|
|
24
|
|
|
if ($search) { |
|
25
|
|
|
$search_q = "&q=$search"; |
|
26
|
|
|
} else { |
|
27
|
|
|
$search_q = ""; |
|
28
|
|
|
} |
|
29
|
|
|
|
|
30
|
|
|
$reply = ""; |
|
31
|
|
|
|
|
32
|
|
|
$rss_link = htmlspecialchars(get_self_url_prefix(). |
|
33
|
|
|
"/public.php?op=rss&id=$feed_id$cat_q$search_q"); |
|
|
|
|
|
|
34
|
|
|
|
|
35
|
|
|
$reply .= "<span class='left'>"; |
|
36
|
|
|
|
|
37
|
|
|
$reply .= "<a href=\"#\" |
|
38
|
|
|
title=\"".__("Show as feed")."\" |
|
39
|
|
|
onclick=\"App.displayDlg('".__("Show as feed")."','generatedFeed', '$feed_id:$is_cat:$rss_link')\"> |
|
40
|
|
|
<i class='icon-syndicate material-icons'>rss_feed</i></a>"; |
|
41
|
|
|
|
|
42
|
|
|
$reply .= "<span id='feed_title'>"; |
|
43
|
|
|
|
|
44
|
|
|
if ($feed_site_url) { |
|
45
|
|
|
$last_updated = T_sprintf("Last updated: %s", $feed_last_updated); |
|
46
|
|
|
|
|
47
|
|
|
$reply .= "<a title=\"$last_updated\" target='_blank' href=\"$feed_site_url\">". |
|
48
|
|
|
truncate_string(strip_tags($feed_title), 30)."</a>"; |
|
49
|
|
|
} else { |
|
50
|
|
|
$reply .= strip_tags($feed_title); |
|
51
|
|
|
} |
|
52
|
|
|
|
|
53
|
|
|
if ($error) |
|
54
|
|
|
$reply .= " <i title=\"".htmlspecialchars($error)."\" class='material-icons icon-error'>error</i>"; |
|
55
|
|
|
|
|
56
|
|
|
$reply .= "</span>"; |
|
57
|
|
|
$reply .= "<span id='feed_current_unread' style='display: none'></span>"; |
|
58
|
|
|
$reply .= "</span>"; |
|
59
|
|
|
|
|
60
|
|
|
$reply .= "<span class=\"right\">"; |
|
61
|
|
|
$reply .= "<span id='selected_prompt'></span>"; |
|
62
|
|
|
$reply .= " "; |
|
63
|
|
|
$reply .= "<select dojoType=\"fox.form.Select\" |
|
64
|
|
|
onchange=\"Headlines.onActionChanged(this)\">"; |
|
65
|
|
|
|
|
66
|
|
|
$reply .= "<option value=\"0\" disabled='1'>".__('Select...')."</option>"; |
|
67
|
|
|
|
|
68
|
|
|
$reply .= "<option value=\"Headlines.select('all')\">".__('All')."</option>"; |
|
69
|
|
|
$reply .= "<option value=\"Headlines.select('unread')\">".__('Unread')."</option>"; |
|
70
|
|
|
$reply .= "<option value=\"Headlines.select('invert')\">".__('Invert')."</option>"; |
|
71
|
|
|
$reply .= "<option value=\"Headlines.select('none')\">".__('None')."</option>"; |
|
72
|
|
|
|
|
73
|
|
|
$reply .= "<option value=\"0\" disabled=\"1\">".__('Selection toggle:')."</option>"; |
|
74
|
|
|
|
|
75
|
|
|
$reply .= "<option value=\"Headlines.selectionToggleUnread()\">".__('Unread')."</option> |
|
76
|
|
|
<option value=\"Headlines.selectionToggleMarked()\">".__('Starred')."</option> |
|
77
|
|
|
<option value=\"Headlines.selectionTogglePublished()\">".__('Published')."</option>"; |
|
78
|
|
|
|
|
79
|
|
|
$reply .= "<option value=\"0\" disabled=\"1\">".__('Selection:')."</option>"; |
|
80
|
|
|
|
|
81
|
|
|
$reply .= "<option value=\"Headlines.catchupSelection()\">".__('Mark as read')."</option>"; |
|
82
|
|
|
$reply .= "<option value=\"Article.selectionSetScore()\">".__('Set score')."</option>"; |
|
83
|
|
|
|
|
84
|
|
|
if ($feed_id == 0 && !$is_cat) { |
|
85
|
|
|
$reply .= "<option value=\"Headlines.archiveSelection()\">".__('Move back')."</option>"; |
|
86
|
|
|
$reply .= "<option value=\"Headlines.deleteSelection()\">".__('Delete')."</option>"; |
|
87
|
|
|
} else { |
|
88
|
|
|
$reply .= "<option value=\"Headlines.archiveSelection()\">".__('Archive')."</option>"; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
if (PluginHost::getInstance()->get_plugin("mail")) { |
|
92
|
|
|
$reply .= "<option value=\"Plugins.Mail.send()\">".__('Forward by email'). |
|
93
|
|
|
"</option>"; |
|
94
|
|
|
} |
|
95
|
|
|
|
|
96
|
|
|
if (PluginHost::getInstance()->get_plugin("mailto")) { |
|
97
|
|
|
$reply .= "<option value=\"Plugins.Mailto.send()\">".__('Forward by email'). |
|
98
|
|
|
"</option>"; |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
|
|
$reply .= "<option value=\"0\" disabled=\"1\">".__('Feed:')."</option>"; |
|
102
|
|
|
|
|
103
|
|
|
//$reply .= "<option value=\"catchupPage()\">".__('Mark as read')."</option>"; |
|
104
|
|
|
|
|
105
|
|
|
$reply .= "<option value=\"App.displayDlg('".__("Show as feed")."','generatedFeed', '$feed_id:$is_cat:$rss_link')\">". |
|
106
|
|
|
__('Show as feed')."</option>"; |
|
107
|
|
|
|
|
108
|
|
|
$reply .= "</select>"; |
|
109
|
|
|
|
|
110
|
|
|
//$reply .= "</h2"; |
|
111
|
|
|
|
|
112
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HEADLINE_TOOLBAR_BUTTON) as $p) { |
|
113
|
|
|
$reply .= $p->hook_headline_toolbar_button($feed_id, $is_cat); |
|
114
|
|
|
} |
|
115
|
|
|
|
|
116
|
|
|
$reply .= "</span>"; |
|
117
|
|
|
|
|
118
|
|
|
return $reply; |
|
119
|
|
|
} |
|
120
|
|
|
|
|
121
|
|
|
private function format_headlines_list($feed, $method, $view_mode, $limit, $cat_view, |
|
122
|
|
|
$offset, $override_order = false, $include_children = false, $check_first_id = false, |
|
123
|
|
|
$skip_first_id_check = false, $order_by = false) { |
|
124
|
|
|
|
|
125
|
|
|
$disable_cache = false; |
|
126
|
|
|
|
|
127
|
|
|
$reply = array(); |
|
128
|
|
|
|
|
129
|
|
|
$rgba_cache = array(); |
|
130
|
|
|
$topmost_article_ids = array(); |
|
131
|
|
|
|
|
132
|
|
|
if (!$offset) { |
|
133
|
|
|
$offset = 0; |
|
134
|
|
|
} |
|
135
|
|
|
if ($method == "undefined") { |
|
136
|
|
|
$method = ""; |
|
137
|
|
|
} |
|
138
|
|
|
|
|
139
|
|
|
$method_split = explode(":", $method); |
|
140
|
|
|
|
|
141
|
|
|
if ($method == "ForceUpdate" && $feed > 0 && is_numeric($feed)) { |
|
142
|
|
|
$sth = $this->pdo->prepare("UPDATE ttrss_feeds |
|
143
|
|
|
SET last_updated = '1970-01-01', last_update_started = '1970-01-01' |
|
144
|
|
|
WHERE id = ?"); |
|
145
|
|
|
$sth->execute([$feed]); |
|
146
|
|
|
} |
|
147
|
|
|
|
|
148
|
|
|
if ($method_split[0] == "MarkAllReadGR") { |
|
149
|
|
|
$this->catchup_feed($method_split[1], false); |
|
150
|
|
|
} |
|
151
|
|
|
|
|
152
|
|
|
// FIXME: might break tag display? |
|
153
|
|
|
|
|
154
|
|
|
if (is_numeric($feed) && $feed > 0 && !$cat_view) { |
|
155
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? LIMIT 1"); |
|
156
|
|
|
$sth->execute([$feed]); |
|
157
|
|
|
|
|
158
|
|
|
if (!$sth->fetch()) { |
|
159
|
|
|
$reply['content'] = "<div align='center'>".__('Feed not found.')."</div>"; |
|
160
|
|
|
} |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
@$search = $_REQUEST["query"]; |
|
164
|
|
|
@$search_language = $_REQUEST["search_language"]; // PGSQL only |
|
165
|
|
|
|
|
166
|
|
|
if ($search) { |
|
167
|
|
|
$disable_cache = true; |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
if (!$cat_view && is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) { |
|
171
|
|
|
$handler = PluginHost::getInstance()->get_feed_handler( |
|
172
|
|
|
PluginHost::feed_to_pfeed_id($feed)); |
|
173
|
|
|
|
|
174
|
|
|
if ($handler) { |
|
175
|
|
|
$options = array( |
|
176
|
|
|
"limit" => $limit, |
|
177
|
|
|
"view_mode" => $view_mode, |
|
178
|
|
|
"cat_view" => $cat_view, |
|
179
|
|
|
"search" => $search, |
|
180
|
|
|
"override_order" => $override_order, |
|
181
|
|
|
"offset" => $offset, |
|
182
|
|
|
"owner_uid" => $_SESSION["uid"], |
|
183
|
|
|
"filter" => false, |
|
184
|
|
|
"since_id" => 0, |
|
185
|
|
|
"include_children" => $include_children, |
|
186
|
|
|
"order_by" => $order_by); |
|
187
|
|
|
|
|
188
|
|
|
$qfh_ret = $handler->get_headlines(PluginHost::feed_to_pfeed_id($feed), |
|
189
|
|
|
$options); |
|
190
|
|
|
} |
|
191
|
|
|
|
|
192
|
|
|
} else { |
|
193
|
|
|
|
|
194
|
|
|
$params = array( |
|
195
|
|
|
"feed" => $feed, |
|
196
|
|
|
"limit" => $limit, |
|
197
|
|
|
"view_mode" => $view_mode, |
|
198
|
|
|
"cat_view" => $cat_view, |
|
199
|
|
|
"search" => $search, |
|
200
|
|
|
"search_language" => $search_language, |
|
201
|
|
|
"override_order" => $override_order, |
|
202
|
|
|
"offset" => $offset, |
|
203
|
|
|
"include_children" => $include_children, |
|
204
|
|
|
"check_first_id" => $check_first_id, |
|
205
|
|
|
"skip_first_id_check" => $skip_first_id_check, |
|
206
|
|
|
"order_by" => $order_by |
|
207
|
|
|
); |
|
208
|
|
|
|
|
209
|
|
|
$qfh_ret = $this->queryFeedHeadlines($params); |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
$vfeed_group_enabled = get_pref("VFEED_GROUP_BY_FEED") && |
|
213
|
|
|
!(in_array($feed, Feeds::NEVER_GROUP_FEEDS) && !$cat_view); |
|
214
|
|
|
|
|
215
|
|
|
$result = $qfh_ret[0]; // this could be either a PDO query result or a -1 if first id changed |
|
|
|
|
|
|
216
|
|
|
$feed_title = $qfh_ret[1]; |
|
217
|
|
|
$feed_site_url = $qfh_ret[2]; |
|
218
|
|
|
$last_error = $qfh_ret[3]; |
|
219
|
|
|
$last_updated = strpos($qfh_ret[4], '1970-') === false ? |
|
220
|
|
|
make_local_datetime($qfh_ret[4], false) : __("Never"); |
|
221
|
|
|
$highlight_words = $qfh_ret[5]; |
|
222
|
|
|
$reply['first_id'] = $qfh_ret[6]; |
|
223
|
|
|
$reply['is_vfeed'] = $qfh_ret[7]; |
|
224
|
|
|
$query_error_override = $qfh_ret[8]; |
|
225
|
|
|
|
|
226
|
|
|
$reply['search_query'] = [$search, $search_language]; |
|
227
|
|
|
$reply['vfeed_group_enabled'] = $vfeed_group_enabled; |
|
228
|
|
|
|
|
229
|
|
|
$reply['toolbar'] = $this->format_headline_subtoolbar($feed_site_url, |
|
230
|
|
|
$feed_title, |
|
231
|
|
|
$feed, $cat_view, $search, |
|
232
|
|
|
$last_error, $last_updated); |
|
233
|
|
|
|
|
234
|
|
|
if ($offset == 0) { |
|
235
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HEADLINES_BEFORE) as $p) { |
|
236
|
|
|
$reply['content'] .= $p->hook_headlines_before($feed, $cat_view, $qfh_ret); |
|
237
|
|
|
} |
|
238
|
|
|
} |
|
239
|
|
|
|
|
240
|
|
|
$reply['content'] = []; |
|
241
|
|
|
|
|
242
|
|
|
$headlines_count = 0; |
|
243
|
|
|
|
|
244
|
|
|
if (is_object($result)) { |
|
245
|
|
|
while ($line = $result->fetch(PDO::FETCH_ASSOC)) { |
|
246
|
|
|
|
|
247
|
|
|
++$headlines_count; |
|
248
|
|
|
|
|
249
|
|
|
if (!get_pref('SHOW_CONTENT_PREVIEW')) { |
|
250
|
|
|
$line["content_preview"] = ""; |
|
251
|
|
|
} else { |
|
252
|
|
|
$line["content_preview"] = "— ".truncate_string(strip_tags($line["content"]), 250); |
|
253
|
|
|
|
|
254
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) { |
|
255
|
|
|
$line = $p->hook_query_headlines($line, 250, false); |
|
256
|
|
|
} |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
$id = $line["id"]; |
|
260
|
|
|
|
|
261
|
|
|
// frontend doesn't expect pdo returning booleans as strings on mysql |
|
262
|
|
|
if (DB_TYPE == "mysql") { |
|
|
|
|
|
|
263
|
|
|
foreach (["unread", "marked", "published"] as $k) { |
|
264
|
|
|
$line[$k] = $line[$k] === "1"; |
|
265
|
|
|
} |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
// normalize archived feed |
|
269
|
|
|
if ($line['feed_id'] === null) { |
|
270
|
|
|
$line['feed_id'] = 0; |
|
271
|
|
|
$line["feed_title"] = __("Archived articles"); |
|
272
|
|
|
} |
|
273
|
|
|
|
|
274
|
|
|
$feed_id = $line["feed_id"]; |
|
275
|
|
|
|
|
276
|
|
|
$label_cache = $line["label_cache"]; |
|
277
|
|
|
$labels = false; |
|
278
|
|
|
|
|
279
|
|
|
if ($label_cache) { |
|
280
|
|
|
$label_cache = json_decode($label_cache, true); |
|
281
|
|
|
|
|
282
|
|
|
if ($label_cache) { |
|
283
|
|
|
if ($label_cache["no-labels"] == 1) { |
|
284
|
|
|
$labels = array(); |
|
285
|
|
|
} else { |
|
286
|
|
|
$labels = $label_cache; |
|
287
|
|
|
} |
|
288
|
|
|
} |
|
289
|
|
|
} |
|
290
|
|
|
|
|
291
|
|
|
if (!is_array($labels)) { |
|
292
|
|
|
$labels = Article::get_article_labels($id); |
|
293
|
|
|
} |
|
294
|
|
|
|
|
295
|
|
|
$labels_str = "<span class=\"HLLCTR-$id\">"; |
|
296
|
|
|
$labels_str .= Article::format_article_labels($labels); |
|
297
|
|
|
$labels_str .= "</span>"; |
|
298
|
|
|
|
|
299
|
|
|
$line["labels"] = $labels_str; |
|
300
|
|
|
|
|
301
|
|
|
if (count($topmost_article_ids) < 3) { |
|
302
|
|
|
array_push($topmost_article_ids, $id); |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
if (!$line["feed_title"]) { |
|
306
|
|
|
$line["feed_title"] = ""; |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
$line["buttons_left"] = ""; |
|
310
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) { |
|
311
|
|
|
$line["buttons_left"] .= $p->hook_article_left_button($line); |
|
312
|
|
|
} |
|
313
|
|
|
|
|
314
|
|
|
$line["buttons"] = ""; |
|
315
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) { |
|
316
|
|
|
$line["buttons"] .= $p->hook_article_button($line); |
|
317
|
|
|
} |
|
318
|
|
|
|
|
319
|
|
|
$line["content"] = sanitize($line["content"], |
|
320
|
|
|
$line['hide_images'], false, $line["site_url"], $highlight_words, $line["id"]); |
|
321
|
|
|
|
|
322
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) { |
|
323
|
|
|
$line = $p->hook_render_article_cdm($line); |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
$line['content'] = DiskCache::rewriteUrls($line['content']); |
|
327
|
|
|
|
|
328
|
|
|
if ($line['note']) { |
|
329
|
|
|
$line['note'] = Article::format_article_note($id, $line['note']); |
|
330
|
|
|
} else { |
|
331
|
|
|
$line['note'] = ""; |
|
332
|
|
|
} |
|
333
|
|
|
|
|
334
|
|
|
if (!get_pref("CDM_EXPANDED")) { |
|
335
|
|
|
$line["cdm_excerpt"] = "<span class='collapse'> |
|
336
|
|
|
<i class='material-icons' onclick='return Article.cdmUnsetActive(event)' |
|
337
|
|
|
title=\"" . __("Collapse article")."\">remove_circle</i></span>"; |
|
338
|
|
|
|
|
339
|
|
|
if (get_pref('SHOW_CONTENT_PREVIEW')) { |
|
340
|
|
|
$line["cdm_excerpt"] .= "<span class='excerpt'>".$line["content_preview"]."</span>"; |
|
341
|
|
|
} |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
|
|
$line["enclosures"] = Article::format_article_enclosures($id, $line["always_display_enclosures"], |
|
345
|
|
|
$line["content"], $line["hide_images"]); |
|
346
|
|
|
|
|
347
|
|
|
if ($line["orig_feed_id"]) { |
|
348
|
|
|
|
|
349
|
|
|
$ofgh = $this->pdo->prepare("SELECT * FROM ttrss_archived_feeds |
|
350
|
|
|
WHERE id = ? AND owner_uid = ?"); |
|
351
|
|
|
$ofgh->execute([$line["orig_feed_id"], $_SESSION['uid']]); |
|
352
|
|
|
|
|
353
|
|
|
if ($tmp_line = $ofgh->fetch()) { |
|
354
|
|
|
$line["orig_feed"] = [$tmp_line["title"], $tmp_line["site_url"], $tmp_line["feed_url"]]; |
|
355
|
|
|
} |
|
356
|
|
|
} |
|
357
|
|
|
|
|
358
|
|
|
$line["updated_long"] = make_local_datetime($line["updated"], true); |
|
359
|
|
|
$line["updated"] = make_local_datetime($line["updated"], false, false, false, true); |
|
360
|
|
|
|
|
361
|
|
|
|
|
362
|
|
|
$line['imported'] = T_sprintf("Imported at %s", |
|
363
|
|
|
make_local_datetime($line["date_entered"], false)); |
|
364
|
|
|
|
|
365
|
|
|
if ($line["tag_cache"]) { |
|
366
|
|
|
$tags = explode(",", $line["tag_cache"]); |
|
367
|
|
|
} else { |
|
368
|
|
|
$tags = false; |
|
369
|
|
|
} |
|
370
|
|
|
|
|
371
|
|
|
$line["tags_str"] = Article::format_tags_string($tags); |
|
372
|
|
|
|
|
373
|
|
|
if (feeds::feedHasIcon($feed_id)) { |
|
374
|
|
|
$line['feed_icon'] = "<img class=\"icon\" src=\"".ICONS_URL."/$feed_id.ico\" alt=\"\">"; |
|
|
|
|
|
|
375
|
|
|
} else { |
|
376
|
|
|
$line['feed_icon'] = "<i class='icon-no-feed material-icons'>rss_feed</i>"; |
|
377
|
|
|
} |
|
378
|
|
|
|
|
379
|
|
|
//setting feed headline background color, needs to change text color based on dark/light |
|
380
|
|
|
$fav_color = $line['favicon_avg_color']; |
|
381
|
|
|
|
|
382
|
|
|
require_once "colors.php"; |
|
383
|
|
|
|
|
384
|
|
|
if (!isset($rgba_cache[$feed_id])) { |
|
385
|
|
|
if ($fav_color && $fav_color != 'fail') { |
|
386
|
|
|
$rgba_cache[$feed_id] = _color_unpack($fav_color); |
|
387
|
|
|
} else { |
|
388
|
|
|
$rgba_cache[$feed_id] = _color_unpack($this->color_of($line['feed_title'])); |
|
389
|
|
|
} |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
if (isset($rgba_cache[$feed_id])) { |
|
393
|
|
|
$line['feed_bg_color'] = 'rgba('.implode(",", $rgba_cache[$feed_id]).',0.3)'; |
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/* we don't need those */ |
|
397
|
|
|
|
|
398
|
|
|
foreach (["date_entered", "guid", "last_published", "last_marked", "tag_cache", "favicon_avg_color", |
|
399
|
|
|
"uuid", "label_cache", "yyiw"] as $k) { |
|
400
|
|
|
unset($line[$k]); |
|
401
|
|
|
} |
|
402
|
|
|
|
|
403
|
|
|
array_push($reply['content'], $line); |
|
404
|
|
|
} |
|
405
|
|
|
} |
|
406
|
|
|
|
|
407
|
|
|
if (!$headlines_count) { |
|
408
|
|
|
|
|
409
|
|
|
if (is_object($result)) { |
|
410
|
|
|
|
|
411
|
|
|
if ($query_error_override) { |
|
412
|
|
|
$message = $query_error_override; |
|
413
|
|
|
} else { |
|
414
|
|
|
switch ($view_mode) { |
|
415
|
|
|
case "unread": |
|
416
|
|
|
$message = __("No unread articles found to display."); |
|
417
|
|
|
break; |
|
418
|
|
|
case "updated": |
|
419
|
|
|
$message = __("No updated articles found to display."); |
|
420
|
|
|
break; |
|
421
|
|
|
case "marked": |
|
422
|
|
|
$message = __("No starred articles found to display."); |
|
423
|
|
|
break; |
|
424
|
|
|
default: |
|
425
|
|
|
if ($feed < LABEL_BASE_INDEX) { |
|
426
|
|
|
$message = __("No articles found to display. You can assign articles to labels manually from article header context menu (applies to all selected articles) or use a filter."); |
|
427
|
|
|
} else { |
|
428
|
|
|
$message = __("No articles found to display."); |
|
429
|
|
|
} |
|
430
|
|
|
} |
|
431
|
|
|
} |
|
432
|
|
|
|
|
433
|
|
|
if (!$offset && $message) { |
|
434
|
|
|
$reply['content'] = "<div class='whiteBox'>$message"; |
|
435
|
|
|
|
|
436
|
|
|
$reply['content'] .= "<p><span class=\"text-muted\">"; |
|
437
|
|
|
|
|
438
|
|
|
$sth = $this->pdo->prepare("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds |
|
439
|
|
|
WHERE owner_uid = ?"); |
|
440
|
|
|
$sth->execute([$_SESSION['uid']]); |
|
441
|
|
|
$row = $sth->fetch(); |
|
442
|
|
|
|
|
443
|
|
|
$last_updated = make_local_datetime($row["last_updated"], false); |
|
444
|
|
|
|
|
445
|
|
|
$reply['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); |
|
446
|
|
|
|
|
447
|
|
|
$sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors |
|
448
|
|
|
FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); |
|
449
|
|
|
$sth->execute([$_SESSION['uid']]); |
|
450
|
|
|
$row = $sth->fetch(); |
|
451
|
|
|
|
|
452
|
|
|
$num_errors = $row["num_errors"]; |
|
453
|
|
|
|
|
454
|
|
|
if ($num_errors > 0) { |
|
455
|
|
|
$reply['content'] .= "<br/>"; |
|
456
|
|
|
$reply['content'] .= "<a class=\"text-muted\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">". |
|
457
|
|
|
__('Some feeds have update errors (click for details)')."</a>"; |
|
458
|
|
|
} |
|
459
|
|
|
$reply['content'] .= "</span></p></div>"; |
|
460
|
|
|
|
|
461
|
|
|
} |
|
462
|
|
|
} else if (is_numeric($result) && $result == -1) { |
|
463
|
|
|
$reply['first_id_changed'] = true; |
|
464
|
|
|
} |
|
465
|
|
|
} |
|
466
|
|
|
|
|
467
|
|
|
return array($topmost_article_ids, $headlines_count, $feed, $disable_cache, $reply); |
|
468
|
|
|
} |
|
469
|
|
|
|
|
470
|
|
|
public function catchupAll() { |
|
471
|
|
|
$sth = $this->pdo->prepare("UPDATE ttrss_user_entries SET |
|
472
|
|
|
last_read = NOW(), unread = false WHERE unread = true AND owner_uid = ?"); |
|
473
|
|
|
$sth->execute([$_SESSION['uid']]); |
|
474
|
|
|
|
|
475
|
|
|
CCache::zero_all($_SESSION["uid"]); |
|
476
|
|
|
} |
|
477
|
|
|
|
|
478
|
|
|
public function view() { |
|
479
|
|
|
$reply = array(); |
|
480
|
|
|
|
|
481
|
|
|
$feed = $_REQUEST["feed"]; |
|
482
|
|
|
$method = $_REQUEST["m"]; |
|
483
|
|
|
$view_mode = $_REQUEST["view_mode"]; |
|
484
|
|
|
$limit = 30; |
|
485
|
|
|
@$cat_view = $_REQUEST["cat"] == "true"; |
|
486
|
|
|
@$next_unread_feed = $_REQUEST["nuf"]; |
|
487
|
|
|
@$offset = $_REQUEST["skip"]; |
|
488
|
|
|
$order_by = $_REQUEST["order_by"]; |
|
489
|
|
|
$check_first_id = $_REQUEST["fid"]; |
|
490
|
|
|
|
|
491
|
|
|
if (is_numeric($feed)) { |
|
492
|
|
|
$feed = (int) $feed; |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
/* Feed -5 is a special case: it is used to display auxiliary information |
|
496
|
|
|
* when there's nothing to load - e.g. no stuff in fresh feed */ |
|
497
|
|
|
|
|
498
|
|
|
if ($feed == -5) { |
|
499
|
|
|
print json_encode($this->generate_dashboard_feed()); |
|
500
|
|
|
return; |
|
501
|
|
|
} |
|
502
|
|
|
|
|
503
|
|
|
$sth = false; |
|
504
|
|
|
if ($feed < LABEL_BASE_INDEX) { |
|
505
|
|
|
|
|
506
|
|
|
$label_feed = Labels::feed_to_label_id($feed); |
|
507
|
|
|
|
|
508
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_labels2 WHERE |
|
509
|
|
|
id = ? AND owner_uid = ?"); |
|
510
|
|
|
$sth->execute([$label_feed, $_SESSION['uid']]); |
|
511
|
|
|
|
|
512
|
|
|
} else if (!$cat_view && is_numeric($feed) && $feed > 0) { |
|
513
|
|
|
|
|
514
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE |
|
515
|
|
|
id = ? AND owner_uid = ?"); |
|
516
|
|
|
$sth->execute([$feed, $_SESSION['uid']]); |
|
517
|
|
|
|
|
518
|
|
|
} else if ($cat_view && is_numeric($feed) && $feed > 0) { |
|
519
|
|
|
|
|
520
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feed_categories WHERE |
|
521
|
|
|
id = ? AND owner_uid = ?"); |
|
522
|
|
|
|
|
523
|
|
|
$sth->execute([$feed, $_SESSION['uid']]); |
|
524
|
|
|
} |
|
525
|
|
|
|
|
526
|
|
|
if ($sth && !$sth->fetch()) { |
|
527
|
|
|
print json_encode($this->generate_error_feed(__("Feed not found."))); |
|
528
|
|
|
return; |
|
529
|
|
|
} |
|
530
|
|
|
|
|
531
|
|
|
/* Updating a label ccache means recalculating all of the caches |
|
532
|
|
|
* so for performance reasons we don't do that here */ |
|
533
|
|
|
|
|
534
|
|
|
if ($feed >= 0) { |
|
535
|
|
|
CCache::update($feed, $_SESSION["uid"], $cat_view); |
|
536
|
|
|
} |
|
537
|
|
|
|
|
538
|
|
|
set_pref("_DEFAULT_VIEW_MODE", $view_mode); |
|
539
|
|
|
set_pref("_DEFAULT_VIEW_ORDER_BY", $order_by); |
|
540
|
|
|
|
|
541
|
|
|
/* bump login timestamp if needed */ |
|
542
|
|
|
if (time() - $_SESSION["last_login_update"] > 3600) { |
|
543
|
|
|
$sth = $this->pdo->prepare("UPDATE ttrss_users SET last_login = NOW() WHERE id = ?"); |
|
544
|
|
|
$sth->execute([$_SESSION['uid']]); |
|
545
|
|
|
|
|
546
|
|
|
$_SESSION["last_login_update"] = time(); |
|
547
|
|
|
} |
|
548
|
|
|
|
|
549
|
|
|
if (!$cat_view && is_numeric($feed) && $feed > 0) { |
|
550
|
|
|
$sth = $this->pdo->prepare("UPDATE ttrss_feeds SET last_viewed = NOW() |
|
551
|
|
|
WHERE id = ? AND owner_uid = ?"); |
|
552
|
|
|
$sth->execute([$feed, $_SESSION['uid']]); |
|
553
|
|
|
} |
|
554
|
|
|
|
|
555
|
|
|
$reply['headlines'] = []; |
|
556
|
|
|
|
|
557
|
|
|
$override_order = false; |
|
558
|
|
|
$skip_first_id_check = false; |
|
559
|
|
|
|
|
560
|
|
|
switch ($order_by) { |
|
561
|
|
|
case "title": |
|
562
|
|
|
$override_order = "ttrss_entries.title, date_entered, updated"; |
|
563
|
|
|
break; |
|
564
|
|
|
case "date_reverse": |
|
565
|
|
|
$override_order = "score DESC, date_entered, updated"; |
|
566
|
|
|
$skip_first_id_check = true; |
|
567
|
|
|
break; |
|
568
|
|
|
case "feed_dates": |
|
569
|
|
|
$override_order = "updated DESC"; |
|
570
|
|
|
break; |
|
571
|
|
|
} |
|
572
|
|
|
|
|
573
|
|
|
$ret = $this->format_headlines_list($feed, $method, |
|
574
|
|
|
$view_mode, $limit, $cat_view, $offset, |
|
575
|
|
|
$override_order, true, $check_first_id, $skip_first_id_check, $order_by); |
|
576
|
|
|
|
|
577
|
|
|
$headlines_count = $ret[1]; |
|
578
|
|
|
$disable_cache = $ret[3]; |
|
579
|
|
|
$reply['headlines'] = $ret[4]; |
|
580
|
|
|
|
|
581
|
|
|
if (!$next_unread_feed) { |
|
582
|
|
|
$reply['headlines']['id'] = $feed; |
|
583
|
|
|
} else { |
|
584
|
|
|
$reply['headlines']['id'] = $next_unread_feed; |
|
585
|
|
|
} |
|
586
|
|
|
|
|
587
|
|
|
$reply['headlines']['is_cat'] = (bool) $cat_view; |
|
588
|
|
|
|
|
589
|
|
|
$reply['headlines-info'] = ["count" => (int) $headlines_count, |
|
590
|
|
|
"disable_cache" => (bool) $disable_cache]; |
|
591
|
|
|
|
|
592
|
|
|
// this is parsed by handleRpcJson() on first viewfeed() to set cdm expanded, etc |
|
593
|
|
|
$reply['runtime-info'] = make_runtime_info(); |
|
594
|
|
|
|
|
595
|
|
|
$reply_json = json_encode($reply); |
|
596
|
|
|
|
|
597
|
|
|
if (!$reply_json) { |
|
598
|
|
|
$reply_json = json_encode(["error" => ["code" => 15, |
|
599
|
|
|
"message" => json_last_error_msg()]]); |
|
600
|
|
|
} |
|
601
|
|
|
|
|
602
|
|
|
print $reply_json; |
|
603
|
|
|
|
|
604
|
|
|
} |
|
605
|
|
|
|
|
606
|
|
|
private function generate_dashboard_feed() { |
|
607
|
|
|
$reply = array(); |
|
608
|
|
|
|
|
609
|
|
|
$reply['headlines']['id'] = -5; |
|
610
|
|
|
$reply['headlines']['is_cat'] = false; |
|
611
|
|
|
|
|
612
|
|
|
$reply['headlines']['toolbar'] = ''; |
|
613
|
|
|
|
|
614
|
|
|
$reply['headlines']['content'] = "<div class='whiteBox'>".__('No feed selected.'); |
|
615
|
|
|
|
|
616
|
|
|
$reply['headlines']['content'] .= "<p><span class=\"text-muted\">"; |
|
617
|
|
|
|
|
618
|
|
|
$sth = $this->pdo->prepare("SELECT ".SUBSTRING_FOR_DATE."(MAX(last_updated), 1, 19) AS last_updated FROM ttrss_feeds |
|
619
|
|
|
WHERE owner_uid = ?"); |
|
620
|
|
|
$sth->execute([$_SESSION['uid']]); |
|
621
|
|
|
$row = $sth->fetch(); |
|
622
|
|
|
|
|
623
|
|
|
$last_updated = make_local_datetime($row["last_updated"], false); |
|
624
|
|
|
|
|
625
|
|
|
$reply['headlines']['content'] .= sprintf(__("Feeds last updated at %s"), $last_updated); |
|
626
|
|
|
|
|
627
|
|
|
$sth = $this->pdo->prepare("SELECT COUNT(id) AS num_errors |
|
628
|
|
|
FROM ttrss_feeds WHERE last_error != '' AND owner_uid = ?"); |
|
629
|
|
|
$sth->execute([$_SESSION['uid']]); |
|
630
|
|
|
$row = $sth->fetch(); |
|
631
|
|
|
|
|
632
|
|
|
$num_errors = $row["num_errors"]; |
|
633
|
|
|
|
|
634
|
|
|
if ($num_errors > 0) { |
|
635
|
|
|
$reply['headlines']['content'] .= "<br/>"; |
|
636
|
|
|
$reply['headlines']['content'] .= "<a class=\"text-muted\" href=\"#\" onclick=\"CommonDialogs.showFeedsWithErrors()\">". |
|
637
|
|
|
__('Some feeds have update errors (click for details)')."</a>"; |
|
638
|
|
|
} |
|
639
|
|
|
$reply['headlines']['content'] .= "</span></p>"; |
|
640
|
|
|
|
|
641
|
|
|
$reply['headlines-info'] = array("count" => 0, |
|
642
|
|
|
"unread" => 0, |
|
643
|
|
|
"disable_cache" => true); |
|
644
|
|
|
|
|
645
|
|
|
return $reply; |
|
646
|
|
|
} |
|
647
|
|
|
|
|
648
|
|
|
private function generate_error_feed($error) { |
|
649
|
|
|
$reply = array(); |
|
650
|
|
|
|
|
651
|
|
|
$reply['headlines']['id'] = -7; |
|
652
|
|
|
$reply['headlines']['is_cat'] = false; |
|
653
|
|
|
|
|
654
|
|
|
$reply['headlines']['toolbar'] = ''; |
|
655
|
|
|
$reply['headlines']['content'] = "<div class='whiteBox'>".$error."</div>"; |
|
656
|
|
|
|
|
657
|
|
|
$reply['headlines-info'] = array("count" => 0, |
|
658
|
|
|
"unread" => 0, |
|
659
|
|
|
"disable_cache" => true); |
|
660
|
|
|
|
|
661
|
|
|
return $reply; |
|
662
|
|
|
} |
|
663
|
|
|
|
|
664
|
|
|
public function quickAddFeed() { |
|
665
|
|
|
print "<form onsubmit='return false'>"; |
|
666
|
|
|
|
|
667
|
|
|
print_hidden("op", "rpc"); |
|
668
|
|
|
print_hidden("method", "addfeed"); |
|
669
|
|
|
|
|
670
|
|
|
print "<div id='fadd_error_message' style='display : none' class='alert alert-danger'></div>"; |
|
671
|
|
|
|
|
672
|
|
|
print "<div id='fadd_multiple_notify' style='display : none'>"; |
|
673
|
|
|
print_notice("Provided URL is a HTML page referencing multiple feeds, please select required feed from the dropdown menu below."); |
|
674
|
|
|
print "<p></div>"; |
|
675
|
|
|
|
|
676
|
|
|
print "<section>"; |
|
677
|
|
|
|
|
678
|
|
|
print "<fieldset>"; |
|
679
|
|
|
print "<div style='float : right'><img style='display : none' id='feed_add_spinner' src='images/indicator_white.gif'></div>"; |
|
680
|
|
|
print "<input style='font-size : 16px; width : 500px;' |
|
681
|
|
|
placeHolder=\"".__("Feed or site URL")."\" |
|
682
|
|
|
dojoType='dijit.form.ValidationTextBox' required='1' name='feed' id='feedDlg_feedUrl'>"; |
|
683
|
|
|
|
|
684
|
|
|
print "</fieldset>"; |
|
685
|
|
|
|
|
686
|
|
|
print "<fieldset>"; |
|
687
|
|
|
|
|
688
|
|
|
if (get_pref('ENABLE_FEED_CATS')) { |
|
689
|
|
|
print "<label class='inline'>".__('Place in category:')."</label> "; |
|
690
|
|
|
print_feed_cat_select("cat", false, 'dojoType="fox.form.Select"'); |
|
691
|
|
|
} |
|
692
|
|
|
|
|
693
|
|
|
print "</fieldset>"; |
|
694
|
|
|
|
|
695
|
|
|
print "</section>"; |
|
696
|
|
|
|
|
697
|
|
|
print '<div id="feedDlg_feedsContainer" style="display : none"> |
|
698
|
|
|
<header>' . __('Available feeds').'</header> |
|
699
|
|
|
<section> |
|
700
|
|
|
<fieldset> |
|
701
|
|
|
<select id="feedDlg_feedContainerSelect" |
|
702
|
|
|
dojoType="fox.form.Select" size="3"> |
|
703
|
|
|
<script type="dojo/method" event="onChange" args="value"> |
|
704
|
|
|
dijit.byId("feedDlg_feedUrl").attr("value", value); |
|
705
|
|
|
</script> |
|
706
|
|
|
</select> |
|
707
|
|
|
</fieldset> |
|
708
|
|
|
</section> |
|
709
|
|
|
</div>'; |
|
710
|
|
|
|
|
711
|
|
|
print "<div id='feedDlg_loginContainer' style='display : none'> |
|
712
|
|
|
<section> |
|
713
|
|
|
<fieldset> |
|
714
|
|
|
<input dojoType=\"dijit.form.TextBox\" name='login'\" |
|
715
|
|
|
placeHolder=\"".__("Login")."\" |
|
716
|
|
|
autocomplete=\"new-password\" |
|
717
|
|
|
style=\"width : 10em;\"> |
|
718
|
|
|
<input |
|
719
|
|
|
placeHolder=\"".__("Password")."\" |
|
720
|
|
|
dojoType=\"dijit.form.TextBox\" type='password' |
|
721
|
|
|
autocomplete=\"new-password\" |
|
722
|
|
|
style=\"width : 10em;\" name='pass'\"> |
|
723
|
|
|
</fieldset> |
|
724
|
|
|
</section> |
|
725
|
|
|
</div>"; |
|
726
|
|
|
|
|
727
|
|
|
print "<section>"; |
|
728
|
|
|
print "<label> |
|
729
|
|
|
<label class='checkbox'><input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox' id='feedDlg_loginCheck' |
|
730
|
|
|
onclick='displayIfChecked(this, \"feedDlg_loginContainer\")'> |
|
731
|
|
|
".__('This feed requires authentication.')."</label>"; |
|
732
|
|
|
print "</section>"; |
|
733
|
|
|
|
|
734
|
|
|
print "<footer>"; |
|
735
|
|
|
print "<button dojoType='dijit.form.Button' class='alt-primary' type='submit' |
|
736
|
|
|
onclick=\"return dijit.byId('feedAddDlg').execute()\">".__('Subscribe')."</button>"; |
|
737
|
|
|
|
|
738
|
|
|
print "<button dojoType='dijit.form.Button' onclick=\"return dijit.byId('feedAddDlg').hide()\">".__('Cancel')."</button>"; |
|
739
|
|
|
print "</footer>"; |
|
740
|
|
|
|
|
741
|
|
|
print "</form>"; |
|
742
|
|
|
} |
|
743
|
|
|
|
|
744
|
|
|
public function search() { |
|
745
|
|
|
$this->params = explode(":", $_REQUEST["param"], 2); |
|
746
|
|
|
|
|
747
|
|
|
$active_feed_id = sprintf("%d", $this->params[0]); |
|
748
|
|
|
$is_cat = $this->params[1] != "false"; |
|
749
|
|
|
|
|
750
|
|
|
print "<form onsubmit='return false;'>"; |
|
751
|
|
|
|
|
752
|
|
|
print "<section>"; |
|
753
|
|
|
|
|
754
|
|
|
print "<fieldset>"; |
|
755
|
|
|
print "<input dojoType='dijit.form.ValidationTextBox' id='search_query' |
|
756
|
|
|
style='font-size : 16px; width : 540px;' |
|
757
|
|
|
placeHolder=\"".T_sprintf("Search %s...", $this->getFeedTitle($active_feed_id, $is_cat))."\" |
|
758
|
|
|
name='query' type='search' value=''>"; |
|
759
|
|
|
print "</fieldset>"; |
|
760
|
|
|
|
|
761
|
|
|
if (DB_TYPE == "pgsql") { |
|
|
|
|
|
|
762
|
|
|
print "<fieldset>"; |
|
763
|
|
|
print "<label class='inline'>".__("Language:")."</label>"; |
|
764
|
|
|
print_select("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(), |
|
765
|
|
|
"dojoType='fox.form.Select' title=\"".__('Used for word stemming')."\""); |
|
766
|
|
|
print "</fieldset>"; |
|
767
|
|
|
} |
|
768
|
|
|
|
|
769
|
|
|
print "</section>"; |
|
770
|
|
|
|
|
771
|
|
|
print "<footer>"; |
|
772
|
|
|
|
|
773
|
|
|
if (count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0) { |
|
774
|
|
|
print "<button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open(\"https://tt-rss.org/wiki/SearchSyntax\")'> |
|
775
|
|
|
<i class='material-icons'>help</i> ".__("Search syntax")."</button>"; |
|
776
|
|
|
} |
|
777
|
|
|
|
|
778
|
|
|
print "<button dojoType='dijit.form.Button' type='submit' class='alt-primary' onclick=\"dijit.byId('searchDlg').execute()\">".__('Search')."</button> |
|
779
|
|
|
<button dojoType='dijit.form.Button' onclick=\"dijit.byId('searchDlg').hide()\">".__('Cancel')."</button>"; |
|
780
|
|
|
|
|
781
|
|
|
print "</footer>"; |
|
782
|
|
|
|
|
783
|
|
|
print "</form>"; |
|
784
|
|
|
} |
|
785
|
|
|
|
|
786
|
|
|
public function update_debugger() { |
|
787
|
|
|
header("Content-type: text/html"); |
|
788
|
|
|
|
|
789
|
|
|
Debug::set_enabled(true); |
|
790
|
|
|
Debug::set_loglevel($_REQUEST["xdebug"]); |
|
791
|
|
|
|
|
792
|
|
|
$feed_id = (int) $_REQUEST["feed_id"]; |
|
793
|
|
|
@$do_update = $_REQUEST["action"] == "do_update"; |
|
794
|
|
|
$csrf_token = $_REQUEST["csrf_token"]; |
|
795
|
|
|
|
|
796
|
|
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); |
|
797
|
|
|
$sth->execute([$feed_id, $_SESSION['uid']]); |
|
798
|
|
|
|
|
799
|
|
|
if (!$sth->fetch()) { |
|
800
|
|
|
print "Access denied."; |
|
801
|
|
|
return; |
|
802
|
|
|
} |
|
803
|
|
|
|
|
804
|
|
|
$refetch_checked = isset($_REQUEST["force_refetch"]) ? "checked" : ""; |
|
805
|
|
|
$rehash_checked = isset($_REQUEST["force_rehash"]) ? "checked" : ""; |
|
806
|
|
|
|
|
807
|
|
|
?> |
|
808
|
|
|
<!DOCTYPE html> |
|
809
|
|
|
<html> |
|
810
|
|
|
<head> |
|
811
|
|
|
<?php echo stylesheet_tag("css/default.css") ?> |
|
812
|
|
|
<title>Feed Debugger</title> |
|
813
|
|
|
<?php |
|
814
|
|
|
echo stylesheet_tag("css/default.css"); |
|
815
|
|
|
echo javascript_tag("lib/prototype.js"); |
|
816
|
|
|
echo javascript_tag("lib/dojo/dojo.js"); |
|
817
|
|
|
echo javascript_tag("lib/dojo/tt-rss-layer.js"); |
|
818
|
|
|
?> |
|
819
|
|
|
</head> |
|
820
|
|
|
<body class="flat ttrss_utility feed_debugger"> |
|
821
|
|
|
<script type="text/javascript"> |
|
822
|
|
|
require(['dojo/parser', "dojo/ready", 'dijit/form/Button','dijit/form/CheckBox', 'dijit/form/Form', |
|
823
|
|
|
'dijit/form/Select','dijit/form/TextBox','dijit/form/ValidationTextBox'],function(parser, ready){ |
|
824
|
|
|
ready(function() { |
|
825
|
|
|
parser.parse(); |
|
826
|
|
|
}); |
|
827
|
|
|
}); |
|
828
|
|
|
</script> |
|
829
|
|
|
|
|
830
|
|
|
<div class="container"> |
|
831
|
|
|
<h1>Feed Debugger: <?php echo "$feed_id: ".$this->getFeedTitle($feed_id) ?></h1> |
|
832
|
|
|
<div class="content"> |
|
833
|
|
|
<form method="GET" action=""> |
|
834
|
|
|
<input type="hidden" name="op" value="feeds"> |
|
835
|
|
|
<input type="hidden" name="method" value="update_debugger"> |
|
836
|
|
|
<input type="hidden" name="xdebug" value="1"> |
|
837
|
|
|
<input type="hidden" name="csrf_token" value="<?php echo $csrf_token ?>"> |
|
838
|
|
|
<input type="hidden" name="action" value="do_update"> |
|
839
|
|
|
<input type="hidden" name="feed_id" value="<?php echo $feed_id ?>"> |
|
840
|
|
|
|
|
841
|
|
|
<fieldset class="narrow"> |
|
842
|
|
|
<label class="checkbox"><input dojoType="dijit.form.CheckBox" type="checkbox" name="force_refetch" value="1" <?php echo $refetch_checked ?>> Force refetch</label> |
|
843
|
|
|
</fieldset> |
|
844
|
|
|
|
|
845
|
|
|
<fieldset class="narrow"> |
|
846
|
|
|
<label class="checkbox"><input dojoType="dijit.form.CheckBox" type="checkbox" name="force_rehash" value="1" <?php echo $rehash_checked ?>> Force rehash</label> |
|
847
|
|
|
</fieldset> |
|
848
|
|
|
|
|
849
|
|
|
<button type="submit" dojoType="dijit.form.Button" class="alt-primary">Continue</button> |
|
850
|
|
|
</form> |
|
851
|
|
|
|
|
852
|
|
|
<hr> |
|
853
|
|
|
|
|
854
|
|
|
<pre><?php |
|
855
|
|
|
|
|
856
|
|
|
if ($do_update) { |
|
857
|
|
|
RSSUtils::update_rss_feed($feed_id, true); |
|
858
|
|
|
} |
|
859
|
|
|
|
|
860
|
|
|
?></pre> |
|
861
|
|
|
</div> |
|
862
|
|
|
</div> |
|
863
|
|
|
</body> |
|
864
|
|
|
</html> |
|
865
|
|
|
<?php |
|
866
|
|
|
|
|
867
|
|
|
} |
|
868
|
|
|
|
|
869
|
|
|
public static function catchup_feed($feed, $cat_view, $owner_uid = false, $mode = 'all', $search = false) { |
|
870
|
|
|
|
|
871
|
|
|
if (!$owner_uid) { |
|
872
|
|
|
$owner_uid = $_SESSION['uid']; |
|
873
|
|
|
} |
|
874
|
|
|
|
|
875
|
|
|
$pdo = Db::pdo(); |
|
876
|
|
|
|
|
877
|
|
|
if (is_array($search) && $search[0]) { |
|
878
|
|
|
$search_qpart = ""; |
|
879
|
|
|
|
|
880
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) { |
|
881
|
|
|
list($search_qpart, $search_words) = $plugin->hook_search($search[0]); |
|
882
|
|
|
break; |
|
883
|
|
|
} |
|
884
|
|
|
|
|
885
|
|
|
// fall back in case of no plugins |
|
886
|
|
|
if (!$search_qpart) { |
|
887
|
|
|
list($search_qpart, $search_words) = Feeds::search_to_sql($search[0], $search[1]); |
|
888
|
|
|
} |
|
889
|
|
|
} else { |
|
890
|
|
|
$search_qpart = "true"; |
|
891
|
|
|
} |
|
892
|
|
|
|
|
893
|
|
|
// TODO: all this interval stuff needs some generic generator function |
|
894
|
|
|
|
|
895
|
|
|
switch ($mode) { |
|
896
|
|
|
case "1day": |
|
897
|
|
|
if (DB_TYPE == "pgsql") { |
|
|
|
|
|
|
898
|
|
|
$date_qpart = "date_entered < NOW() - INTERVAL '1 day' "; |
|
899
|
|
|
} else { |
|
900
|
|
|
$date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) "; |
|
901
|
|
|
} |
|
902
|
|
|
break; |
|
903
|
|
|
case "1week": |
|
904
|
|
|
if (DB_TYPE == "pgsql") { |
|
905
|
|
|
$date_qpart = "date_entered < NOW() - INTERVAL '1 week' "; |
|
906
|
|
|
} else { |
|
907
|
|
|
$date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) "; |
|
908
|
|
|
} |
|
909
|
|
|
break; |
|
910
|
|
|
case "2week": |
|
911
|
|
|
if (DB_TYPE == "pgsql") { |
|
912
|
|
|
$date_qpart = "date_entered < NOW() - INTERVAL '2 week' "; |
|
913
|
|
|
} else { |
|
914
|
|
|
$date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) "; |
|
915
|
|
|
} |
|
916
|
|
|
break; |
|
917
|
|
|
default: |
|
918
|
|
|
$date_qpart = "true"; |
|
919
|
|
|
} |
|
920
|
|
|
|
|
921
|
|
|
if (is_numeric($feed)) { |
|
922
|
|
|
if ($cat_view) { |
|
923
|
|
|
|
|
924
|
|
|
if ($feed >= 0) { |
|
925
|
|
|
|
|
926
|
|
|
if ($feed > 0) { |
|
927
|
|
|
$children = Feeds::getChildCategories($feed, $owner_uid); |
|
928
|
|
|
array_push($children, $feed); |
|
929
|
|
|
$children = array_map("intval", $children); |
|
930
|
|
|
|
|
931
|
|
|
$children = join(",", $children); |
|
932
|
|
|
|
|
933
|
|
|
$cat_qpart = "cat_id IN ($children)"; |
|
934
|
|
|
} else { |
|
935
|
|
|
$cat_qpart = "cat_id IS NULL"; |
|
936
|
|
|
} |
|
937
|
|
|
|
|
938
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
939
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
940
|
|
|
(SELECT id FROM |
|
941
|
|
|
(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id |
|
942
|
|
|
AND owner_uid = ? AND unread = true AND feed_id IN |
|
943
|
|
|
(SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart AND $search_qpart) as tmp)"); |
|
944
|
|
|
$sth->execute([$owner_uid]); |
|
945
|
|
|
|
|
946
|
|
|
} else if ($feed == -2) { |
|
947
|
|
|
|
|
948
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
949
|
|
|
SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*) |
|
950
|
|
|
FROM ttrss_user_labels2, ttrss_entries WHERE article_id = ref_id AND id = ref_id AND $date_qpart AND $search_qpart) > 0 |
|
951
|
|
|
AND unread = true AND owner_uid = ?"); |
|
952
|
|
|
$sth->execute([$owner_uid]); |
|
953
|
|
|
} |
|
954
|
|
|
|
|
955
|
|
|
} else if ($feed > 0) { |
|
956
|
|
|
|
|
957
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
958
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
959
|
|
|
(SELECT id FROM |
|
960
|
|
|
(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id |
|
961
|
|
|
AND owner_uid = ? AND unread = true AND feed_id = ? AND $date_qpart AND $search_qpart) as tmp)"); |
|
962
|
|
|
$sth->execute([$owner_uid, $feed]); |
|
963
|
|
|
|
|
964
|
|
|
} else if ($feed < 0 && $feed > LABEL_BASE_INDEX) { // special, like starred |
|
965
|
|
|
|
|
966
|
|
|
if ($feed == -1) { |
|
967
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
968
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
969
|
|
|
(SELECT id FROM |
|
970
|
|
|
(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id |
|
971
|
|
|
AND owner_uid = ? AND unread = true AND marked = true AND $date_qpart AND $search_qpart) as tmp)"); |
|
972
|
|
|
$sth->execute([$owner_uid]); |
|
973
|
|
|
} |
|
974
|
|
|
|
|
975
|
|
|
if ($feed == -2) { |
|
976
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
977
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
978
|
|
|
(SELECT id FROM |
|
979
|
|
|
(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id |
|
980
|
|
|
AND owner_uid = ? AND unread = true AND published = true AND $date_qpart AND $search_qpart) as tmp)"); |
|
981
|
|
|
$sth->execute([$owner_uid]); |
|
982
|
|
|
} |
|
983
|
|
|
|
|
984
|
|
|
if ($feed == -3) { |
|
985
|
|
|
|
|
986
|
|
|
$intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE"); |
|
987
|
|
|
|
|
988
|
|
|
if (DB_TYPE == "pgsql") { |
|
989
|
|
|
$match_part = "date_entered > NOW() - INTERVAL '$intl hour' "; |
|
990
|
|
|
} else { |
|
991
|
|
|
$match_part = "date_entered > DATE_SUB(NOW(), |
|
992
|
|
|
INTERVAL $intl HOUR) "; |
|
993
|
|
|
} |
|
994
|
|
|
|
|
995
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
996
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
997
|
|
|
(SELECT id FROM |
|
998
|
|
|
(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id |
|
999
|
|
|
AND owner_uid = ? AND score >= 0 AND unread = true AND $date_qpart AND $match_part AND $search_qpart) as tmp)"); |
|
1000
|
|
|
$sth->execute([$owner_uid]); |
|
1001
|
|
|
} |
|
1002
|
|
|
|
|
1003
|
|
|
if ($feed == -4) { |
|
1004
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
1005
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
1006
|
|
|
(SELECT id FROM |
|
1007
|
|
|
(SELECT DISTINCT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id |
|
1008
|
|
|
AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); |
|
1009
|
|
|
$sth->execute([$owner_uid]); |
|
1010
|
|
|
} |
|
1011
|
|
|
|
|
1012
|
|
|
} else if ($feed < LABEL_BASE_INDEX) { // label |
|
1013
|
|
|
|
|
1014
|
|
|
$label_id = Labels::feed_to_label_id($feed); |
|
1015
|
|
|
|
|
1016
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
1017
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
1018
|
|
|
(SELECT id FROM |
|
1019
|
|
|
(SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id |
|
1020
|
|
|
AND label_id = ? AND ref_id = article_id |
|
1021
|
|
|
AND owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); |
|
1022
|
|
|
$sth->execute([$label_id, $owner_uid]); |
|
1023
|
|
|
|
|
1024
|
|
|
} |
|
1025
|
|
|
|
|
1026
|
|
|
CCache::update($feed, $owner_uid, $cat_view); |
|
1027
|
|
|
|
|
1028
|
|
|
} else { // tag |
|
1029
|
|
|
$sth = $pdo->prepare("UPDATE ttrss_user_entries |
|
1030
|
|
|
SET unread = false, last_read = NOW() WHERE ref_id IN |
|
1031
|
|
|
(SELECT id FROM |
|
1032
|
|
|
(SELECT DISTINCT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id |
|
1033
|
|
|
AND post_int_id = int_id AND tag_name = ? |
|
1034
|
|
|
AND ttrss_user_entries.owner_uid = ? AND unread = true AND $date_qpart AND $search_qpart) as tmp)"); |
|
1035
|
|
|
$sth->execute([$feed, $owner_uid]); |
|
1036
|
|
|
|
|
1037
|
|
|
} |
|
1038
|
|
|
} |
|
1039
|
|
|
|
|
1040
|
|
|
public static function getFeedArticles($feed, $is_cat = false, $unread_only = false, |
|
1041
|
|
|
$owner_uid = false) { |
|
1042
|
|
|
|
|
1043
|
|
|
$n_feed = (int) $feed; |
|
1044
|
|
|
$need_entries = false; |
|
1045
|
|
|
|
|
1046
|
|
|
$pdo = Db::pdo(); |
|
1047
|
|
|
|
|
1048
|
|
|
if (!$owner_uid) { |
|
1049
|
|
|
$owner_uid = $_SESSION["uid"]; |
|
1050
|
|
|
} |
|
1051
|
|
|
|
|
1052
|
|
|
if ($unread_only) { |
|
1053
|
|
|
$unread_qpart = "unread = true"; |
|
1054
|
|
|
} else { |
|
1055
|
|
|
$unread_qpart = "true"; |
|
1056
|
|
|
} |
|
1057
|
|
|
|
|
1058
|
|
|
$match_part = ""; |
|
1059
|
|
|
|
|
1060
|
|
|
if ($is_cat) { |
|
1061
|
|
|
return Feeds::getCategoryUnread($n_feed, $owner_uid); |
|
1062
|
|
|
} else if ($n_feed == -6) { |
|
1063
|
|
|
return 0; |
|
1064
|
|
|
} else if ($feed != "0" && $n_feed == 0) { |
|
1065
|
|
|
|
|
1066
|
|
|
$sth = $pdo->prepare("SELECT SUM((SELECT COUNT(int_id) |
|
1067
|
|
|
FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id |
|
1068
|
|
|
AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags |
|
1069
|
|
|
WHERE owner_uid = ? AND tag_name = ?"); |
|
1070
|
|
|
|
|
1071
|
|
|
$sth->execute([$owner_uid, $feed]); |
|
1072
|
|
|
$row = $sth->fetch(); |
|
1073
|
|
|
|
|
1074
|
|
|
return $row["count"]; |
|
1075
|
|
|
|
|
1076
|
|
|
} else if ($n_feed == -1) { |
|
1077
|
|
|
$match_part = "marked = true"; |
|
1078
|
|
|
} else if ($n_feed == -2) { |
|
1079
|
|
|
$match_part = "published = true"; |
|
1080
|
|
|
} else if ($n_feed == -3) { |
|
1081
|
|
|
$match_part = "unread = true AND score >= 0"; |
|
1082
|
|
|
|
|
1083
|
|
|
$intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid); |
|
1084
|
|
|
|
|
1085
|
|
|
if (DB_TYPE == "pgsql") { |
|
|
|
|
|
|
1086
|
|
|
$match_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' "; |
|
1087
|
|
|
} else { |
|
1088
|
|
|
$match_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) "; |
|
1089
|
|
|
} |
|
1090
|
|
|
|
|
1091
|
|
|
$need_entries = true; |
|
1092
|
|
|
|
|
1093
|
|
|
} else if ($n_feed == -4) { |
|
1094
|
|
|
$match_part = "true"; |
|
1095
|
|
|
} else if ($n_feed >= 0) { |
|
1096
|
|
|
|
|
1097
|
|
|
if ($n_feed != 0) { |
|
1098
|
|
|
$match_part = "feed_id = ".(int) $n_feed; |
|
1099
|
|
|
} else { |
|
1100
|
|
|
$match_part = "feed_id IS NULL"; |
|
1101
|
|
|
} |
|
1102
|
|
|
|
|
1103
|
|
|
} else if ($feed < LABEL_BASE_INDEX) { |
|
1104
|
|
|
|
|
1105
|
|
|
$label_id = Labels::feed_to_label_id($feed); |
|
1106
|
|
|
|
|
1107
|
|
|
return Feeds::getLabelUnread($label_id, $owner_uid); |
|
1108
|
|
|
} |
|
1109
|
|
|
|
|
1110
|
|
|
if ($match_part) { |
|
1111
|
|
|
|
|
1112
|
|
|
if ($need_entries) { |
|
1113
|
|
|
$from_qpart = "ttrss_user_entries,ttrss_entries"; |
|
1114
|
|
|
$from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND"; |
|
1115
|
|
|
} else { |
|
1116
|
|
|
$from_qpart = "ttrss_user_entries"; |
|
1117
|
|
|
$from_where = ""; |
|
1118
|
|
|
} |
|
1119
|
|
|
|
|
1120
|
|
|
$sth = $pdo->prepare("SELECT count(int_id) AS unread |
|
1121
|
|
|
FROM $from_qpart WHERE |
|
1122
|
|
|
$unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = ?"); |
|
1123
|
|
|
$sth->execute([$owner_uid]); |
|
1124
|
|
|
$row = $sth->fetch(); |
|
1125
|
|
|
|
|
1126
|
|
|
return $row["unread"]; |
|
1127
|
|
|
|
|
1128
|
|
|
} else { |
|
1129
|
|
|
|
|
1130
|
|
|
$sth = $pdo->prepare("SELECT COUNT(post_int_id) AS unread |
|
1131
|
|
|
FROM ttrss_tags,ttrss_user_entries,ttrss_entries |
|
1132
|
|
|
WHERE tag_name = ? AND post_int_id = int_id AND ref_id = ttrss_entries.id |
|
1133
|
|
|
AND $unread_qpart AND ttrss_tags.owner_uid = ,"); |
|
1134
|
|
|
|
|
1135
|
|
|
$sth->execute([$feed, $owner_uid]); |
|
1136
|
|
|
$row = $sth->fetch(); |
|
1137
|
|
|
|
|
1138
|
|
|
return $row["unread"]; |
|
1139
|
|
|
} |
|
1140
|
|
|
} |
|
1141
|
|
|
|
|
1142
|
|
|
/** |
|
1143
|
|
|
* @return array (code => Status code, message => error message if available) |
|
1144
|
|
|
* |
|
1145
|
|
|
* 0 - OK, Feed already exists |
|
1146
|
|
|
* 1 - OK, Feed added |
|
1147
|
|
|
* 2 - Invalid URL |
|
1148
|
|
|
* 3 - URL content is HTML, no feeds available |
|
1149
|
|
|
* 4 - URL content is HTML which contains multiple feeds. |
|
1150
|
|
|
* Here you should call extractfeedurls in rpc-backend |
|
1151
|
|
|
* to get all possible feeds. |
|
1152
|
|
|
* 5 - Couldn't download the URL content. |
|
1153
|
|
|
* 6 - Content is an invalid XML. |
|
1154
|
|
|
*/ |
|
1155
|
|
|
public static function subscribe_to_feed($url, $cat_id = 0, |
|
1156
|
|
|
$auth_login = '', $auth_pass = '') { |
|
1157
|
|
|
|
|
1158
|
|
|
global $fetch_last_error; |
|
1159
|
|
|
global $fetch_last_error_content; |
|
1160
|
|
|
global $fetch_last_content_type; |
|
1161
|
|
|
|
|
1162
|
|
|
$pdo = Db::pdo(); |
|
1163
|
|
|
|
|
1164
|
|
|
$url = Feeds::fix_url($url); |
|
1165
|
|
|
|
|
1166
|
|
|
if (!$url || !Feeds::validate_feed_url($url)) { |
|
1167
|
|
|
return array("code" => 2); |
|
1168
|
|
|
} |
|
1169
|
|
|
|
|
1170
|
|
|
$contents = @fetch_file_contents($url, false, $auth_login, $auth_pass); |
|
1171
|
|
|
|
|
1172
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SUBSCRIBE_FEED) as $plugin) { |
|
1173
|
|
|
$contents = $plugin->hook_subscribe_feed($contents, $url, $auth_login, $auth_pass); |
|
1174
|
|
|
} |
|
1175
|
|
|
|
|
1176
|
|
|
if (!$contents) { |
|
1177
|
|
|
if (preg_match("/cloudflare\.com/", $fetch_last_error_content)) { |
|
1178
|
|
|
$fetch_last_error .= " (feed behind Cloudflare)"; |
|
1179
|
|
|
} |
|
1180
|
|
|
|
|
1181
|
|
|
return array("code" => 5, "message" => $fetch_last_error); |
|
1182
|
|
|
} |
|
1183
|
|
|
|
|
1184
|
|
|
if (mb_strpos($fetch_last_content_type, "html") !== false && Feeds::is_html($contents)) { |
|
1185
|
|
|
$feedUrls = Feeds::get_feeds_from_html($url, $contents); |
|
1186
|
|
|
|
|
1187
|
|
|
if (count($feedUrls) == 0) { |
|
1188
|
|
|
return array("code" => 3); |
|
1189
|
|
|
} else if (count($feedUrls) > 1) { |
|
1190
|
|
|
return array("code" => 4, "feeds" => $feedUrls); |
|
1191
|
|
|
} |
|
1192
|
|
|
//use feed url as new URL |
|
1193
|
|
|
$url = key($feedUrls); |
|
1194
|
|
|
} |
|
1195
|
|
|
|
|
1196
|
|
|
if (!$cat_id) { |
|
1197
|
|
|
$cat_id = null; |
|
1198
|
|
|
} |
|
1199
|
|
|
|
|
1200
|
|
|
$sth = $pdo->prepare("SELECT id FROM ttrss_feeds |
|
1201
|
|
|
WHERE feed_url = ? AND owner_uid = ?"); |
|
1202
|
|
|
$sth->execute([$url, $_SESSION['uid']]); |
|
1203
|
|
|
|
|
1204
|
|
|
if ($row = $sth->fetch()) { |
|
1205
|
|
|
return array("code" => 0, "feed_id" => (int) $row["id"]); |
|
1206
|
|
|
} else { |
|
1207
|
|
|
$sth = $pdo->prepare( |
|
1208
|
|
|
"INSERT INTO ttrss_feeds |
|
1209
|
|
|
(owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted) |
|
1210
|
|
|
VALUES (?, ?, ?, ?, ?, ?, 0, false)"); |
|
1211
|
|
|
|
|
1212
|
|
|
$sth->execute([$_SESSION['uid'], $url, "[Unknown]", $cat_id, (string) $auth_login, (string) $auth_pass]); |
|
1213
|
|
|
|
|
1214
|
|
|
$sth = $pdo->prepare("SELECT id FROM ttrss_feeds WHERE feed_url = ? |
|
1215
|
|
|
AND owner_uid = ?"); |
|
1216
|
|
|
$sth->execute([$url, $_SESSION['uid']]); |
|
1217
|
|
|
$row = $sth->fetch(); |
|
1218
|
|
|
|
|
1219
|
|
|
$feed_id = $row["id"]; |
|
1220
|
|
|
|
|
1221
|
|
|
if ($feed_id) { |
|
1222
|
|
|
RSSUtils::set_basic_feed_info($feed_id); |
|
1223
|
|
|
} |
|
1224
|
|
|
|
|
1225
|
|
|
return array("code" => 1, "feed_id" => (int) $feed_id); |
|
1226
|
|
|
|
|
1227
|
|
|
} |
|
1228
|
|
|
} |
|
1229
|
|
|
|
|
1230
|
|
|
public static function getIconFile($feed_id) { |
|
1231
|
|
|
return ICONS_DIR."/$feed_id.ico"; |
|
|
|
|
|
|
1232
|
|
|
} |
|
1233
|
|
|
|
|
1234
|
|
|
public static function feedHasIcon($id) { |
|
1235
|
|
|
return is_file(ICONS_DIR."/$id.ico") && filesize(ICONS_DIR."/$id.ico") > 0; |
|
|
|
|
|
|
1236
|
|
|
} |
|
1237
|
|
|
|
|
1238
|
|
|
public static function getFeedIcon($id) { |
|
1239
|
|
|
switch ($id) { |
|
1240
|
|
|
case 0: |
|
1241
|
|
|
return "archive"; |
|
1242
|
|
|
case -1: |
|
1243
|
|
|
return "star"; |
|
1244
|
|
|
case -2: |
|
1245
|
|
|
return "rss_feed"; |
|
1246
|
|
|
case -3: |
|
1247
|
|
|
return "whatshot"; |
|
1248
|
|
|
case -4: |
|
1249
|
|
|
return "inbox"; |
|
1250
|
|
|
case -6: |
|
1251
|
|
|
return "restore"; |
|
1252
|
|
|
default: |
|
1253
|
|
|
if ($id < LABEL_BASE_INDEX) { |
|
1254
|
|
|
return "label"; |
|
1255
|
|
|
} else { |
|
1256
|
|
|
$icon = self::getIconFile($id); |
|
1257
|
|
|
|
|
1258
|
|
|
if ($icon && file_exists($icon)) { |
|
1259
|
|
|
return ICONS_URL."/".basename($icon)."?".filemtime($icon); |
|
|
|
|
|
|
1260
|
|
|
} |
|
1261
|
|
|
} |
|
1262
|
|
|
} |
|
1263
|
|
|
|
|
1264
|
|
|
return false; |
|
1265
|
|
|
} |
|
1266
|
|
|
|
|
1267
|
|
|
public static function getFeedTitle($id, $cat = false) { |
|
1268
|
|
|
$pdo = Db::pdo(); |
|
1269
|
|
|
|
|
1270
|
|
|
if ($cat) { |
|
1271
|
|
|
return Feeds::getCategoryTitle($id); |
|
1272
|
|
|
} else if ($id == -1) { |
|
1273
|
|
|
return __("Starred articles"); |
|
1274
|
|
|
} else if ($id == -2) { |
|
1275
|
|
|
return __("Published articles"); |
|
1276
|
|
|
} else if ($id == -3) { |
|
1277
|
|
|
return __("Fresh articles"); |
|
1278
|
|
|
} else if ($id == -4) { |
|
1279
|
|
|
return __("All articles"); |
|
1280
|
|
|
} else if ($id === 0 || $id === "0") { |
|
1281
|
|
|
return __("Archived articles"); |
|
1282
|
|
|
} else if ($id == -6) { |
|
1283
|
|
|
return __("Recently read"); |
|
1284
|
|
|
} else if ($id < LABEL_BASE_INDEX) { |
|
1285
|
|
|
|
|
1286
|
|
|
$label_id = Labels::feed_to_label_id($id); |
|
1287
|
|
|
|
|
1288
|
|
|
$sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 WHERE id = ?"); |
|
1289
|
|
|
$sth->execute([$label_id]); |
|
1290
|
|
|
|
|
1291
|
|
|
if ($row = $sth->fetch()) { |
|
1292
|
|
|
return $row["caption"]; |
|
1293
|
|
|
} else { |
|
1294
|
|
|
return "Unknown label ($label_id)"; |
|
1295
|
|
|
} |
|
1296
|
|
|
|
|
1297
|
|
|
} else if (is_numeric($id) && $id > 0) { |
|
1298
|
|
|
|
|
1299
|
|
|
$sth = $pdo->prepare("SELECT title FROM ttrss_feeds WHERE id = ?"); |
|
1300
|
|
|
$sth->execute([$id]); |
|
1301
|
|
|
|
|
1302
|
|
|
if ($row = $sth->fetch()) { |
|
1303
|
|
|
return $row["title"]; |
|
1304
|
|
|
} else { |
|
1305
|
|
|
return "Unknown feed ($id)"; |
|
1306
|
|
|
} |
|
1307
|
|
|
|
|
1308
|
|
|
} else { |
|
1309
|
|
|
return $id; |
|
1310
|
|
|
} |
|
1311
|
|
|
} |
|
1312
|
|
|
|
|
1313
|
|
|
public static function getCategoryUnread($cat, $owner_uid = false) { |
|
1314
|
|
|
|
|
1315
|
|
|
if (!$owner_uid) { |
|
1316
|
|
|
$owner_uid = $_SESSION["uid"]; |
|
1317
|
|
|
} |
|
1318
|
|
|
|
|
1319
|
|
|
$pdo = Db::pdo(); |
|
1320
|
|
|
|
|
1321
|
|
|
if ($cat >= 0) { |
|
1322
|
|
|
|
|
1323
|
|
|
if (!$cat) { |
|
1324
|
|
|
$cat = null; |
|
1325
|
|
|
} |
|
1326
|
|
|
|
|
1327
|
|
|
$sth = $pdo->prepare("SELECT id FROM ttrss_feeds |
|
1328
|
|
|
WHERE (cat_id = :cat OR (:cat IS NULL AND cat_id IS NULL)) |
|
1329
|
|
|
AND owner_uid = :uid"); |
|
1330
|
|
|
|
|
1331
|
|
|
$sth->execute([":cat" => $cat, ":uid" => $owner_uid]); |
|
1332
|
|
|
|
|
1333
|
|
|
$cat_feeds = array(); |
|
1334
|
|
|
while ($line = $sth->fetch()) { |
|
1335
|
|
|
array_push($cat_feeds, "feed_id = ".(int) $line["id"]); |
|
1336
|
|
|
} |
|
1337
|
|
|
|
|
1338
|
|
|
if (count($cat_feeds) == 0) { |
|
1339
|
|
|
return 0; |
|
1340
|
|
|
} |
|
1341
|
|
|
|
|
1342
|
|
|
$match_part = implode(" OR ", $cat_feeds); |
|
1343
|
|
|
|
|
1344
|
|
|
$sth = $pdo->prepare("SELECT COUNT(int_id) AS unread |
|
1345
|
|
|
FROM ttrss_user_entries |
|
1346
|
|
|
WHERE unread = true AND ($match_part) |
|
1347
|
|
|
AND owner_uid = ?"); |
|
1348
|
|
|
$sth->execute([$owner_uid]); |
|
1349
|
|
|
|
|
1350
|
|
|
$unread = 0; |
|
1351
|
|
|
|
|
1352
|
|
|
# this needs to be rewritten |
|
1353
|
|
|
while ($line = $sth->fetch()) { |
|
1354
|
|
|
$unread += $line["unread"]; |
|
1355
|
|
|
} |
|
1356
|
|
|
|
|
1357
|
|
|
return $unread; |
|
1358
|
|
|
} else if ($cat == -1) { |
|
1359
|
|
|
return getFeedUnread(-1) + getFeedUnread(-2) + getFeedUnread(-3) + getFeedUnread(0); |
|
1360
|
|
|
} else if ($cat == -2) { |
|
1361
|
|
|
|
|
1362
|
|
|
$sth = $pdo->prepare("SELECT COUNT(unread) AS unread FROM |
|
1363
|
|
|
ttrss_user_entries, ttrss_user_labels2 |
|
1364
|
|
|
WHERE article_id = ref_id AND unread = true |
|
1365
|
|
|
AND ttrss_user_entries.owner_uid = ?"); |
|
1366
|
|
|
$sth->execute([$owner_uid]); |
|
1367
|
|
|
$row = $sth->fetch(); |
|
1368
|
|
|
|
|
1369
|
|
|
return $row["unread"]; |
|
1370
|
|
|
} |
|
1371
|
|
|
} |
|
1372
|
|
|
|
|
1373
|
|
|
// only accepts real cats (>= 0) |
|
1374
|
|
|
public static function getCategoryChildrenUnread($cat, $owner_uid = false) { |
|
1375
|
|
|
if (!$owner_uid) { |
|
1376
|
|
|
$owner_uid = $_SESSION["uid"]; |
|
1377
|
|
|
} |
|
1378
|
|
|
|
|
1379
|
|
|
$pdo = Db::pdo(); |
|
1380
|
|
|
|
|
1381
|
|
|
$sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories WHERE parent_cat = ? |
|
1382
|
|
|
AND owner_uid = ?"); |
|
1383
|
|
|
$sth->execute([$cat, $owner_uid]); |
|
1384
|
|
|
|
|
1385
|
|
|
$unread = 0; |
|
1386
|
|
|
|
|
1387
|
|
|
while ($line = $sth->fetch()) { |
|
1388
|
|
|
$unread += Feeds::getCategoryUnread($line["id"], $owner_uid); |
|
1389
|
|
|
$unread += Feeds::getCategoryChildrenUnread($line["id"], $owner_uid); |
|
1390
|
|
|
} |
|
1391
|
|
|
|
|
1392
|
|
|
return $unread; |
|
1393
|
|
|
} |
|
1394
|
|
|
|
|
1395
|
|
|
public static function getGlobalUnread($user_id = false) { |
|
1396
|
|
|
|
|
1397
|
|
|
if (!$user_id) { |
|
1398
|
|
|
$user_id = $_SESSION["uid"]; |
|
1399
|
|
|
} |
|
1400
|
|
|
|
|
1401
|
|
|
$pdo = Db::pdo(); |
|
1402
|
|
|
|
|
1403
|
|
|
$sth = $pdo->prepare("SELECT SUM(value) AS c_id FROM ttrss_counters_cache |
|
1404
|
|
|
WHERE owner_uid = ? AND feed_id > 0"); |
|
1405
|
|
|
$sth->execute([$user_id]); |
|
1406
|
|
|
$row = $sth->fetch(); |
|
1407
|
|
|
|
|
1408
|
|
|
return $row["c_id"]; |
|
1409
|
|
|
} |
|
1410
|
|
|
|
|
1411
|
|
|
public static function getCategoryTitle($cat_id) { |
|
1412
|
|
|
|
|
1413
|
|
|
if ($cat_id == -1) { |
|
1414
|
|
|
return __("Special"); |
|
1415
|
|
|
} else if ($cat_id == -2) { |
|
1416
|
|
|
return __("Labels"); |
|
1417
|
|
|
} else { |
|
1418
|
|
|
|
|
1419
|
|
|
$pdo = Db::pdo(); |
|
1420
|
|
|
|
|
1421
|
|
|
$sth = $pdo->prepare("SELECT title FROM ttrss_feed_categories WHERE |
|
1422
|
|
|
id = ?"); |
|
1423
|
|
|
$sth->execute([$cat_id]); |
|
1424
|
|
|
|
|
1425
|
|
|
if ($row = $sth->fetch()) { |
|
1426
|
|
|
return $row["title"]; |
|
1427
|
|
|
} else { |
|
1428
|
|
|
return __("Uncategorized"); |
|
1429
|
|
|
} |
|
1430
|
|
|
} |
|
1431
|
|
|
} |
|
1432
|
|
|
|
|
1433
|
|
|
public static function getLabelUnread($label_id, $owner_uid = false) { |
|
1434
|
|
|
if (!$owner_uid) { |
|
1435
|
|
|
$owner_uid = $_SESSION["uid"]; |
|
1436
|
|
|
} |
|
1437
|
|
|
|
|
1438
|
|
|
$pdo = Db::pdo(); |
|
1439
|
|
|
|
|
1440
|
|
|
$sth = $pdo->prepare("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2 |
|
1441
|
|
|
WHERE owner_uid = ? AND unread = true AND label_id = ? AND article_id = ref_id"); |
|
1442
|
|
|
|
|
1443
|
|
|
$sth->execute([$owner_uid, $label_id]); |
|
1444
|
|
|
|
|
1445
|
|
|
if ($row = $sth->fetch()) { |
|
1446
|
|
|
return $row["unread"]; |
|
1447
|
|
|
} else { |
|
1448
|
|
|
return 0; |
|
1449
|
|
|
} |
|
1450
|
|
|
} |
|
1451
|
|
|
|
|
1452
|
|
|
public static function queryFeedHeadlines($params) { |
|
1453
|
|
|
|
|
1454
|
|
|
$pdo = Db::pdo(); |
|
1455
|
|
|
|
|
1456
|
|
|
// WARNING: due to highly dynamic nature of this query its going to quote parameters |
|
1457
|
|
|
// right before adding them to SQL part |
|
1458
|
|
|
|
|
1459
|
|
|
$feed = $params["feed"]; |
|
1460
|
|
|
$limit = isset($params["limit"]) ? $params["limit"] : 30; |
|
1461
|
|
|
$view_mode = $params["view_mode"]; |
|
1462
|
|
|
$cat_view = isset($params["cat_view"]) ? $params["cat_view"] : false; |
|
1463
|
|
|
$search = isset($params["search"]) ? $params["search"] : false; |
|
1464
|
|
|
$search_language = isset($params["search_language"]) ? $params["search_language"] : ""; |
|
1465
|
|
|
$override_order = isset($params["override_order"]) ? $params["override_order"] : false; |
|
1466
|
|
|
$offset = isset($params["offset"]) ? $params["offset"] : 0; |
|
1467
|
|
|
$owner_uid = isset($params["owner_uid"]) ? $params["owner_uid"] : $_SESSION["uid"]; |
|
1468
|
|
|
$since_id = isset($params["since_id"]) ? $params["since_id"] : 0; |
|
1469
|
|
|
$include_children = isset($params["include_children"]) ? $params["include_children"] : false; |
|
1470
|
|
|
$ignore_vfeed_group = isset($params["ignore_vfeed_group"]) ? $params["ignore_vfeed_group"] : false; |
|
1471
|
|
|
$override_strategy = isset($params["override_strategy"]) ? $params["override_strategy"] : false; |
|
1472
|
|
|
$override_vfeed = isset($params["override_vfeed"]) ? $params["override_vfeed"] : false; |
|
1473
|
|
|
$start_ts = isset($params["start_ts"]) ? $params["start_ts"] : false; |
|
1474
|
|
|
$check_first_id = isset($params["check_first_id"]) ? $params["check_first_id"] : false; |
|
1475
|
|
|
$skip_first_id_check = isset($params["skip_first_id_check"]) ? $params["skip_first_id_check"] : false; |
|
1476
|
|
|
//$order_by = isset($params["order_by"]) ? $params["order_by"] : false; |
|
1477
|
|
|
|
|
1478
|
|
|
$ext_tables_part = ""; |
|
1479
|
|
|
$limit_query_part = ""; |
|
1480
|
|
|
$query_error_override = ""; |
|
1481
|
|
|
|
|
1482
|
|
|
$search_words = []; |
|
1483
|
|
|
|
|
1484
|
|
|
if ($search) { |
|
1485
|
|
|
$search_query_part = ""; |
|
1486
|
|
|
|
|
1487
|
|
|
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) { |
|
1488
|
|
|
list($search_query_part, $search_words) = $plugin->hook_search($search); |
|
1489
|
|
|
break; |
|
1490
|
|
|
} |
|
1491
|
|
|
|
|
1492
|
|
|
// fall back in case of no plugins |
|
1493
|
|
|
if (!$search_query_part) { |
|
1494
|
|
|
list($search_query_part, $search_words) = Feeds::search_to_sql($search, $search_language); |
|
1495
|
|
|
} |
|
1496
|
|
|
|
|
1497
|
|
|
if (DB_TYPE == "pgsql") { |
|
|
|
|
|
|
1498
|
|
|
$test_sth = $pdo->prepare("select $search_query_part |
|
1499
|
|
|
FROM ttrss_entries, ttrss_user_entries WHERE id = ref_id limit 1"); |
|
1500
|
|
|
|
|
1501
|
|
|
try { |
|
1502
|
|
|
$test_sth->execute(); |
|
1503
|
|
|
} catch (PDOException $e) { |
|
1504
|
|
|
// looks like tsquery syntax is invalid |
|
1505
|
|
|
$search_query_part = "false"; |
|
1506
|
|
|
|
|
1507
|
|
|
$query_error_override = T_sprintf("Incorrect search syntax: %s.", implode(" ", $search_words)); |
|
1508
|
|
|
} |
|
1509
|
|
|
} |
|
1510
|
|
|
|
|
1511
|
|
|
$search_query_part .= " AND "; |
|
1512
|
|
|
} else { |
|
1513
|
|
|
$search_query_part = ""; |
|
1514
|
|
|
} |
|
1515
|
|
|
|
|
1516
|
|
|
if ($since_id) { |
|
1517
|
|
|
$since_id_part = "ttrss_entries.id > ".$pdo->quote($since_id)." AND "; |
|
1518
|
|
|
} else { |
|
1519
|
|
|
$since_id_part = ""; |
|
1520
|
|
|
} |
|
1521
|
|
|
|
|
1522
|
|
|
$view_query_part = ""; |
|
1523
|
|
|
|
|
1524
|
|
|
if ($view_mode == "adaptive") { |
|
1525
|
|
|
if ($search) { |
|
1526
|
|
|
$view_query_part = " "; |
|
1527
|
|
|
} else if ($feed != -1) { |
|
1528
|
|
|
|
|
1529
|
|
|
$unread = getFeedUnread($feed, $cat_view); |
|
1530
|
|
|
|
|
1531
|
|
|
if ($cat_view && $feed > 0 && $include_children) { |
|
1532
|
|
|
$unread += Feeds::getCategoryChildrenUnread($feed); |
|
1533
|
|
|
} |
|
1534
|
|
|
|
|
1535
|
|
|
if ($unread > 0) { |
|
1536
|
|
|
$view_query_part = " unread = true AND "; |
|
1537
|
|
|
} |
|
1538
|
|
|
} |
|
1539
|
|
|
} |
|
1540
|
|
|
|
|
1541
|
|
|
if ($view_mode == "marked") { |
|
1542
|
|
|
$view_query_part = " marked = true AND "; |
|
1543
|
|
|
} |
|
1544
|
|
|
|
|
1545
|
|
|
if ($view_mode == "has_note") { |
|
1546
|
|
|
$view_query_part = " (note IS NOT NULL AND note != '') AND "; |
|
1547
|
|
|
} |
|
1548
|
|
|
|
|
1549
|
|
|
if ($view_mode == "published") { |
|
1550
|
|
|
$view_query_part = " published = true AND "; |
|
1551
|
|
|
} |
|
1552
|
|
|
|
|
1553
|
|
|
if ($view_mode == "unread" && $feed != -6) { |
|
1554
|
|
|
$view_query_part = " unread = true AND "; |
|
1555
|
|
|
} |
|
1556
|
|
|
|
|
1557
|
|
|
if ($limit > 0) { |
|
1558
|
|
|
$limit_query_part = "LIMIT ".(int) $limit; |
|
1559
|
|
|
} |
|
1560
|
|
|
|
|
1561
|
|
|
$allow_archived = false; |
|
1562
|
|
|
|
|
1563
|
|
|
$vfeed_query_part = ""; |
|
1564
|
|
|
|
|
1565
|
|
|
/* tags */ |
|
1566
|
|
|
if (!is_numeric($feed)) { |
|
1567
|
|
|
$query_strategy_part = "true"; |
|
1568
|
|
|
$vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE |
|
1569
|
|
|
id = feed_id) as feed_title,"; |
|
1570
|
|
|
} else if ($feed > 0) { |
|
1571
|
|
|
|
|
1572
|
|
|
if ($cat_view) { |
|
1573
|
|
|
|
|
1574
|
|
|
if ($feed > 0) { |
|
1575
|
|
|
if ($include_children) { |
|
1576
|
|
|
# sub-cats |
|
1577
|
|
|
$subcats = Feeds::getChildCategories($feed, $owner_uid); |
|
1578
|
|
|
array_push($subcats, $feed); |
|
1579
|
|
|
$subcats = array_map("intval", $subcats); |
|
1580
|
|
|
|
|
1581
|
|
|
$query_strategy_part = "cat_id IN (". |
|
1582
|
|
|
implode(",", $subcats).")"; |
|
1583
|
|
|
|
|
1584
|
|
|
} else { |
|
1585
|
|
|
$query_strategy_part = "cat_id = ".$pdo->quote($feed); |
|
1586
|
|
|
} |
|
1587
|
|
|
|
|
1588
|
|
|
} else { |
|
1589
|
|
|
$query_strategy_part = "cat_id IS NULL"; |
|
1590
|
|
|
} |
|
1591
|
|
|
|
|
1592
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1593
|
|
|
|
|
1594
|
|
|
} else { |
|
1595
|
|
|
$query_strategy_part = "feed_id = ".$pdo->quote($feed); |
|
1596
|
|
|
} |
|
1597
|
|
|
} else if ($feed == 0 && !$cat_view) { // archive virtual feed |
|
1598
|
|
|
$query_strategy_part = "feed_id IS NULL"; |
|
1599
|
|
|
$allow_archived = true; |
|
1600
|
|
|
} else if ($feed == 0 && $cat_view) { // uncategorized |
|
1601
|
|
|
$query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL"; |
|
1602
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1603
|
|
|
} else if ($feed == -1) { // starred virtual feed |
|
1604
|
|
|
$query_strategy_part = "marked = true"; |
|
1605
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1606
|
|
|
$allow_archived = true; |
|
1607
|
|
|
|
|
1608
|
|
|
if (!$override_order) { |
|
1609
|
|
|
$override_order = "last_marked DESC, date_entered DESC, updated DESC"; |
|
1610
|
|
|
} |
|
1611
|
|
|
|
|
1612
|
|
|
} else if ($feed == -2) { // published virtual feed OR labels category |
|
1613
|
|
|
|
|
1614
|
|
|
if (!$cat_view) { |
|
1615
|
|
|
$query_strategy_part = "published = true"; |
|
1616
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1617
|
|
|
$allow_archived = true; |
|
1618
|
|
|
|
|
1619
|
|
|
if (!$override_order) { |
|
1620
|
|
|
$override_order = "last_published DESC, date_entered DESC, updated DESC"; |
|
1621
|
|
|
} |
|
1622
|
|
|
|
|
1623
|
|
|
} else { |
|
1624
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1625
|
|
|
|
|
1626
|
|
|
$ext_tables_part = "ttrss_labels2,ttrss_user_labels2,"; |
|
1627
|
|
|
|
|
1628
|
|
|
$query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND |
|
1629
|
|
|
ttrss_user_labels2.article_id = ref_id"; |
|
1630
|
|
|
|
|
1631
|
|
|
} |
|
1632
|
|
|
} else if ($feed == -6) { // recently read |
|
1633
|
|
|
$query_strategy_part = "unread = false AND last_read IS NOT NULL"; |
|
1634
|
|
|
|
|
1635
|
|
|
if (DB_TYPE == "pgsql") { |
|
1636
|
|
|
$query_strategy_part .= " AND last_read > NOW() - INTERVAL '1 DAY' "; |
|
1637
|
|
|
} else { |
|
1638
|
|
|
$query_strategy_part .= " AND last_read > DATE_SUB(NOW(), INTERVAL 1 DAY) "; |
|
1639
|
|
|
} |
|
1640
|
|
|
|
|
1641
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1642
|
|
|
$allow_archived = true; |
|
1643
|
|
|
$ignore_vfeed_group = true; |
|
1644
|
|
|
|
|
1645
|
|
|
if (!$override_order) { |
|
1646
|
|
|
$override_order = "last_read DESC"; |
|
1647
|
|
|
} |
|
1648
|
|
|
|
|
1649
|
|
|
} else if ($feed == -3) { // fresh virtual feed |
|
1650
|
|
|
$query_strategy_part = "unread = true AND score >= 0"; |
|
1651
|
|
|
|
|
1652
|
|
|
$intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid); |
|
1653
|
|
|
|
|
1654
|
|
|
if (DB_TYPE == "pgsql") { |
|
1655
|
|
|
$query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' "; |
|
1656
|
|
|
} else { |
|
1657
|
|
|
$query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) "; |
|
1658
|
|
|
} |
|
1659
|
|
|
|
|
1660
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1661
|
|
|
} else if ($feed == -4) { // all articles virtual feed |
|
1662
|
|
|
$allow_archived = true; |
|
1663
|
|
|
$query_strategy_part = "true"; |
|
1664
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1665
|
|
|
} else if ($feed <= LABEL_BASE_INDEX) { // labels |
|
1666
|
|
|
$label_id = Labels::feed_to_label_id($feed); |
|
1667
|
|
|
|
|
1668
|
|
|
$query_strategy_part = "label_id = ".$pdo->quote($label_id)." AND |
|
1669
|
|
|
ttrss_labels2.id = ttrss_user_labels2.label_id AND |
|
1670
|
|
|
ttrss_user_labels2.article_id = ref_id"; |
|
1671
|
|
|
|
|
1672
|
|
|
$vfeed_query_part = "ttrss_feeds.title AS feed_title,"; |
|
1673
|
|
|
$ext_tables_part = "ttrss_labels2,ttrss_user_labels2,"; |
|
1674
|
|
|
$allow_archived = true; |
|
1675
|
|
|
|
|
1676
|
|
|
} else { |
|
1677
|
|
|
$query_strategy_part = "true"; |
|
1678
|
|
|
} |
|
1679
|
|
|
|
|
1680
|
|
|
$order_by = "score DESC, date_entered DESC, updated DESC"; |
|
1681
|
|
|
|
|
1682
|
|
|
if ($override_order) { |
|
1683
|
|
|
$order_by = $override_order; |
|
1684
|
|
|
} |
|
1685
|
|
|
|
|
1686
|
|
|
if ($override_strategy) { |
|
1687
|
|
|
$query_strategy_part = $override_strategy; |
|
1688
|
|
|
} |
|
1689
|
|
|
|
|
1690
|
|
|
if ($override_vfeed) { |
|
1691
|
|
|
$vfeed_query_part = $override_vfeed; |
|
1692
|
|
|
} |
|
1693
|
|
|
|
|
1694
|
|
|
if ($search) { |
|
1695
|
|
|
$feed_title = T_sprintf("Search results: %s", $search); |
|
1696
|
|
|
} else { |
|
1697
|
|
|
if ($cat_view) { |
|
1698
|
|
|
$feed_title = Feeds::getCategoryTitle($feed); |
|
1699
|
|
|
} else { |
|
1700
|
|
|
if (is_numeric($feed) && $feed > 0) { |
|
1701
|
|
|
$ssth = $pdo->prepare("SELECT title,site_url,last_error,last_updated |
|
1702
|
|
|
FROM ttrss_feeds WHERE id = ? AND owner_uid = ?"); |
|
1703
|
|
|
$ssth->execute([$feed, $owner_uid]); |
|
1704
|
|
|
$row = $ssth->fetch(); |
|
1705
|
|
|
|
|
1706
|
|
|
$feed_title = $row["title"]; |
|
1707
|
|
|
$feed_site_url = $row["site_url"]; |
|
1708
|
|
|
$last_error = $row["last_error"]; |
|
1709
|
|
|
$last_updated = $row["last_updated"]; |
|
1710
|
|
|
} else { |
|
1711
|
|
|
$feed_title = Feeds::getFeedTitle($feed); |
|
1712
|
|
|
} |
|
1713
|
|
|
} |
|
1714
|
|
|
} |
|
1715
|
|
|
|
|
1716
|
|
|
$content_query_part = "content, "; |
|
1717
|
|
|
|
|
1718
|
|
|
if ($limit_query_part) { |
|
1719
|
|
|
$offset_query_part = "OFFSET ".(int) $offset; |
|
1720
|
|
|
} else { |
|
1721
|
|
|
$offset_query_part = ""; |
|
1722
|
|
|
} |
|
1723
|
|
|
|
|
1724
|
|
|
if ($start_ts) { |
|
1725
|
|
|
$start_ts_formatted = date("Y/m/d H:i:s", strtotime($start_ts)); |
|
1726
|
|
|
$start_ts_query_part = "date_entered >= '$start_ts_formatted' AND"; |
|
1727
|
|
|
} else { |
|
1728
|
|
|
$start_ts_query_part = ""; |
|
1729
|
|
|
} |
|
1730
|
|
|
|
|
1731
|
|
|
if (is_numeric($feed)) { |
|
1732
|
|
|
// proper override_order applied above |
|
1733
|
|
|
if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) { |
|
1734
|
|
|
|
|
1735
|
|
|
if (!(in_array($feed, Feeds::NEVER_GROUP_BY_DATE) && !$cat_view)) { |
|
1736
|
|
|
$yyiw_desc = $order_by == "date_reverse" ? "" : "desc"; |
|
1737
|
|
|
$yyiw_order_qpart = "yyiw $yyiw_desc, "; |
|
1738
|
|
|
} else { |
|
1739
|
|
|
$yyiw_order_qpart = ""; |
|
1740
|
|
|
} |
|
1741
|
|
|
|
|
1742
|
|
|
if (!$override_order) { |
|
1743
|
|
|
$order_by = "$yyiw_order_qpart ttrss_feeds.title, $order_by"; |
|
1744
|
|
|
} else { |
|
1745
|
|
|
$order_by = "$yyiw_order_qpart ttrss_feeds.title, $override_order"; |
|
1746
|
|
|
} |
|
1747
|
|
|
} |
|
1748
|
|
|
|
|
1749
|
|
|
if (!$allow_archived) { |
|
1750
|
|
|
$from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id),ttrss_feeds"; |
|
1751
|
|
|
$feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND"; |
|
1752
|
|
|
|
|
1753
|
|
|
} else { |
|
1754
|
|
|
$from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id) |
|
1755
|
|
|
LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)"; |
|
1756
|
|
|
} |
|
1757
|
|
|
|
|
1758
|
|
|
if ($vfeed_query_part) { |
|
1759
|
|
|
$vfeed_query_part .= "favicon_avg_color,"; |
|
1760
|
|
|
} |
|
1761
|
|
|
|
|
1762
|
|
|
$first_id = 0; |
|
1763
|
|
|
$first_id_query_strategy_part = $query_strategy_part; |
|
1764
|
|
|
|
|
1765
|
|
|
if ($feed == -3) { |
|
1766
|
|
|
$first_id_query_strategy_part = "true"; |
|
1767
|
|
|
} |
|
1768
|
|
|
|
|
1769
|
|
|
if (DB_TYPE == "pgsql") { |
|
1770
|
|
|
$sanity_interval_qpart = "date_entered >= NOW() - INTERVAL '1 hour' AND"; |
|
1771
|
|
|
$yyiw_qpart = "to_char(date_entered, 'IYYY-IW') AS yyiw"; |
|
1772
|
|
|
} else { |
|
1773
|
|
|
$sanity_interval_qpart = "date_entered >= DATE_SUB(NOW(), INTERVAL 1 hour) AND"; |
|
1774
|
|
|
$yyiw_qpart = "date_format(date_entered, '%Y-%u') AS yyiw"; |
|
1775
|
|
|
} |
|
1776
|
|
|
|
|
1777
|
|
|
if (!$search && !$skip_first_id_check) { |
|
1778
|
|
|
// if previous topmost article id changed that means our current pagination is no longer valid |
|
1779
|
|
|
$query = "SELECT DISTINCT |
|
1780
|
|
|
ttrss_feeds.title, |
|
1781
|
|
|
date_entered, |
|
1782
|
|
|
$yyiw_qpart, |
|
1783
|
|
|
guid, |
|
1784
|
|
|
ttrss_entries.id, |
|
1785
|
|
|
ttrss_entries.title, |
|
1786
|
|
|
updated, |
|
1787
|
|
|
score, |
|
1788
|
|
|
marked, |
|
1789
|
|
|
published, |
|
1790
|
|
|
last_marked, |
|
1791
|
|
|
last_published, |
|
1792
|
|
|
last_read |
|
1793
|
|
|
FROM |
|
1794
|
|
|
$from_qpart |
|
1795
|
|
|
WHERE |
|
1796
|
|
|
$feed_check_qpart |
|
|
|
|
|
|
1797
|
|
|
ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND |
|
1798
|
|
|
$search_query_part |
|
1799
|
|
|
$start_ts_query_part |
|
1800
|
|
|
$since_id_part |
|
1801
|
|
|
$sanity_interval_qpart |
|
1802
|
|
|
$first_id_query_strategy_part ORDER BY $order_by LIMIT 1"; |
|
1803
|
|
|
|
|
1804
|
|
|
/*if ($_REQUEST["debug"]) { |
|
1805
|
|
|
print $query; |
|
1806
|
|
|
}*/ |
|
1807
|
|
|
|
|
1808
|
|
|
$res = $pdo->query($query); |
|
1809
|
|
|
|
|
1810
|
|
|
if ($row = $res->fetch()) { |
|
1811
|
|
|
$first_id = (int) $row["id"]; |
|
1812
|
|
|
|
|
1813
|
|
|
if ($offset > 0 && $first_id && $check_first_id && $first_id != $check_first_id) { |
|
1814
|
|
|
return array(-1, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override); |
|
|
|
|
|
|
1815
|
|
|
} |
|
1816
|
|
|
} |
|
1817
|
|
|
} |
|
1818
|
|
|
|
|
1819
|
|
|
$query = "SELECT DISTINCT |
|
1820
|
|
|
date_entered, |
|
1821
|
|
|
$yyiw_qpart, |
|
1822
|
|
|
guid, |
|
1823
|
|
|
ttrss_entries.id,ttrss_entries.title, |
|
1824
|
|
|
updated, |
|
1825
|
|
|
label_cache, |
|
1826
|
|
|
tag_cache, |
|
1827
|
|
|
always_display_enclosures, |
|
1828
|
|
|
site_url, |
|
1829
|
|
|
note, |
|
1830
|
|
|
num_comments, |
|
1831
|
|
|
comments, |
|
1832
|
|
|
int_id, |
|
1833
|
|
|
uuid, |
|
1834
|
|
|
lang, |
|
1835
|
|
|
hide_images, |
|
1836
|
|
|
unread,feed_id,marked,published,link,last_read,orig_feed_id, |
|
1837
|
|
|
last_marked, last_published, |
|
1838
|
|
|
$vfeed_query_part |
|
1839
|
|
|
$content_query_part |
|
1840
|
|
|
author,score |
|
1841
|
|
|
FROM |
|
1842
|
|
|
$from_qpart |
|
1843
|
|
|
WHERE |
|
1844
|
|
|
$feed_check_qpart |
|
1845
|
|
|
ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND |
|
1846
|
|
|
$search_query_part |
|
1847
|
|
|
$start_ts_query_part |
|
1848
|
|
|
$view_query_part |
|
1849
|
|
|
$since_id_part |
|
1850
|
|
|
$query_strategy_part ORDER BY $order_by |
|
1851
|
|
|
$limit_query_part $offset_query_part"; |
|
1852
|
|
|
|
|
1853
|
|
|
//if ($_REQUEST["debug"]) print $query; |
|
1854
|
|
|
|
|
1855
|
|
|
$res = $pdo->query($query); |
|
1856
|
|
|
|
|
1857
|
|
|
} else { |
|
1858
|
|
|
// browsing by tag |
|
1859
|
|
|
|
|
1860
|
|
|
$query = "SELECT DISTINCT |
|
1861
|
|
|
date_entered, |
|
1862
|
|
|
guid, |
|
1863
|
|
|
note, |
|
1864
|
|
|
ttrss_entries.id as id, |
|
1865
|
|
|
title, |
|
1866
|
|
|
updated, |
|
1867
|
|
|
unread, |
|
1868
|
|
|
feed_id, |
|
1869
|
|
|
orig_feed_id, |
|
1870
|
|
|
marked, |
|
1871
|
|
|
published, |
|
1872
|
|
|
num_comments, |
|
1873
|
|
|
comments, |
|
1874
|
|
|
int_id, |
|
1875
|
|
|
tag_cache, |
|
1876
|
|
|
label_cache, |
|
1877
|
|
|
link, |
|
1878
|
|
|
lang, |
|
1879
|
|
|
uuid, |
|
1880
|
|
|
last_read, |
|
1881
|
|
|
(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images, |
|
1882
|
|
|
last_marked, last_published, |
|
1883
|
|
|
$since_id_part |
|
1884
|
|
|
$vfeed_query_part |
|
1885
|
|
|
$content_query_part |
|
1886
|
|
|
author, score |
|
1887
|
|
|
FROM ttrss_entries, ttrss_user_entries, ttrss_tags |
|
1888
|
|
|
WHERE |
|
1889
|
|
|
ref_id = ttrss_entries.id AND |
|
1890
|
|
|
ttrss_user_entries.owner_uid = ".$pdo->quote($owner_uid)." AND |
|
1891
|
|
|
post_int_id = int_id AND |
|
1892
|
|
|
tag_name = ".$pdo->quote($feed)." AND |
|
1893
|
|
|
$view_query_part |
|
1894
|
|
|
$search_query_part |
|
1895
|
|
|
$start_ts_query_part |
|
1896
|
|
|
$query_strategy_part ORDER BY $order_by |
|
1897
|
|
|
$limit_query_part $offset_query_part"; |
|
1898
|
|
|
|
|
1899
|
|
|
if ($_REQUEST["debug"]) { |
|
1900
|
|
|
print $query; |
|
1901
|
|
|
} |
|
1902
|
|
|
|
|
1903
|
|
|
$res = $pdo->query($query); |
|
1904
|
|
|
} |
|
1905
|
|
|
|
|
1906
|
|
|
return array($res, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words, $first_id, $vfeed_query_part != "", $query_error_override); |
|
|
|
|
|
|
1907
|
|
|
|
|
1908
|
|
|
} |
|
1909
|
|
|
|
|
1910
|
|
|
public static function getParentCategories($cat, $owner_uid) { |
|
1911
|
|
|
$rv = array(); |
|
1912
|
|
|
|
|
1913
|
|
|
$pdo = Db::pdo(); |
|
1914
|
|
|
|
|
1915
|
|
|
$sth = $pdo->prepare("SELECT parent_cat FROM ttrss_feed_categories |
|
1916
|
|
|
WHERE id = ? AND parent_cat IS NOT NULL AND owner_uid = ?"); |
|
1917
|
|
|
$sth->execute([$cat, $owner_uid]); |
|
1918
|
|
|
|
|
1919
|
|
|
while ($line = $sth->fetch()) { |
|
1920
|
|
|
array_push($rv, $line["parent_cat"]); |
|
1921
|
|
|
$rv = array_merge($rv, Feeds::getParentCategories($line["parent_cat"], $owner_uid)); |
|
1922
|
|
|
} |
|
1923
|
|
|
|
|
1924
|
|
|
return $rv; |
|
1925
|
|
|
} |
|
1926
|
|
|
|
|
1927
|
|
|
public static function getChildCategories($cat, $owner_uid) { |
|
1928
|
|
|
$rv = array(); |
|
1929
|
|
|
|
|
1930
|
|
|
$pdo = Db::pdo(); |
|
1931
|
|
|
|
|
1932
|
|
|
$sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories |
|
1933
|
|
|
WHERE parent_cat = ? AND owner_uid = ?"); |
|
1934
|
|
|
$sth->execute([$cat, $owner_uid]); |
|
1935
|
|
|
|
|
1936
|
|
|
while ($line = $sth->fetch()) { |
|
1937
|
|
|
array_push($rv, $line["id"]); |
|
1938
|
|
|
$rv = array_merge($rv, Feeds::getChildCategories($line["id"], $owner_uid)); |
|
1939
|
|
|
} |
|
1940
|
|
|
|
|
1941
|
|
|
return $rv; |
|
1942
|
|
|
} |
|
1943
|
|
|
|
|
1944
|
|
|
public static function getFeedCategory($feed) { |
|
1945
|
|
|
$pdo = Db::pdo(); |
|
1946
|
|
|
|
|
1947
|
|
|
$sth = $pdo->prepare("SELECT cat_id FROM ttrss_feeds |
|
1948
|
|
|
WHERE id = ?"); |
|
1949
|
|
|
$sth->execute([$feed]); |
|
1950
|
|
|
|
|
1951
|
|
|
if ($row = $sth->fetch()) { |
|
1952
|
|
|
return $row["cat_id"]; |
|
1953
|
|
|
} else { |
|
1954
|
|
|
return false; |
|
1955
|
|
|
} |
|
1956
|
|
|
|
|
1957
|
|
|
} |
|
1958
|
|
|
|
|
1959
|
|
|
function color_of($name) { |
|
1960
|
|
|
$colormap = ["#1cd7d7", "#d91111", "#1212d7", "#8e16e5", "#7b7b7b", |
|
1961
|
|
|
"#39f110", "#0bbea6", "#ec0e0e", "#1534f2", "#b9e416", |
|
1962
|
|
|
"#479af2", "#f36b14", "#10c7e9", "#1e8fe7", "#e22727"]; |
|
1963
|
|
|
|
|
1964
|
|
|
$sum = 0; |
|
1965
|
|
|
|
|
1966
|
|
|
for ($i = 0; $i < strlen($name); $i++) { |
|
1967
|
|
|
$sum += ord($name[$i]); |
|
1968
|
|
|
} |
|
1969
|
|
|
|
|
1970
|
|
|
$sum %= count($colormap); |
|
1971
|
|
|
|
|
1972
|
|
|
return $colormap[$sum]; |
|
1973
|
|
|
} |
|
1974
|
|
|
|
|
1975
|
|
|
public static function get_feeds_from_html($url, $content) { |
|
1976
|
|
|
$url = Feeds::fix_url($url); |
|
1977
|
|
|
$baseUrl = substr($url, 0, strrpos($url, '/') + 1); |
|
1978
|
|
|
|
|
1979
|
|
|
$feedUrls = []; |
|
1980
|
|
|
|
|
1981
|
|
|
$doc = new DOMDocument(); |
|
1982
|
|
|
if ($doc->loadHTML($content)) { |
|
1983
|
|
|
$xpath = new DOMXPath($doc); |
|
1984
|
|
|
$entries = $xpath->query('/html/head/link[@rel="alternate" and '. |
|
1985
|
|
|
'(contains(@type,"rss") or contains(@type,"atom"))]|/html/head/link[@rel="feed"]'); |
|
1986
|
|
|
|
|
1987
|
|
|
foreach ($entries as $entry) { |
|
1988
|
|
|
if ($entry->hasAttribute('href')) { |
|
1989
|
|
|
$title = $entry->getAttribute('title'); |
|
1990
|
|
|
if ($title == '') { |
|
1991
|
|
|
$title = $entry->getAttribute('type'); |
|
1992
|
|
|
} |
|
1993
|
|
|
$feedUrl = rewrite_relative_url( |
|
1994
|
|
|
$baseUrl, $entry->getAttribute('href') |
|
1995
|
|
|
); |
|
1996
|
|
|
$feedUrls[$feedUrl] = $title; |
|
1997
|
|
|
} |
|
1998
|
|
|
} |
|
1999
|
|
|
} |
|
2000
|
|
|
return $feedUrls; |
|
2001
|
|
|
} |
|
2002
|
|
|
|
|
2003
|
|
|
public static function is_html($content) { |
|
2004
|
|
|
return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 8192)) !== 0; |
|
2005
|
|
|
} |
|
2006
|
|
|
|
|
2007
|
|
|
public static function validate_feed_url($url) { |
|
2008
|
|
|
$parts = parse_url($url); |
|
2009
|
|
|
|
|
2010
|
|
|
return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https'); |
|
2011
|
|
|
} |
|
2012
|
|
|
|
|
2013
|
|
|
/** |
|
2014
|
|
|
* Fixes incomplete URLs by prepending "http://". |
|
2015
|
|
|
* Also replaces feed:// with http://, and |
|
2016
|
|
|
* prepends a trailing slash if the url is a domain name only. |
|
2017
|
|
|
* |
|
2018
|
|
|
* @param string $url Possibly incomplete URL |
|
2019
|
|
|
* |
|
2020
|
|
|
* @return string Fixed URL. |
|
2021
|
|
|
*/ |
|
2022
|
|
|
public static function fix_url($url) { |
|
2023
|
|
|
|
|
2024
|
|
|
// support schema-less urls |
|
2025
|
|
|
if (strpos($url, '//') === 0) { |
|
2026
|
|
|
$url = 'https:'.$url; |
|
2027
|
|
|
} |
|
2028
|
|
|
|
|
2029
|
|
|
if (strpos($url, '://') === false) { |
|
2030
|
|
|
$url = 'http://'.$url; |
|
2031
|
|
|
} else if (substr($url, 0, 5) == 'feed:') { |
|
2032
|
|
|
$url = 'http:'.substr($url, 5); |
|
2033
|
|
|
} |
|
2034
|
|
|
|
|
2035
|
|
|
//prepend slash if the URL has no slash in it |
|
2036
|
|
|
// "http://www.example" -> "http://www.example/" |
|
2037
|
|
|
if (strpos($url, '/', strpos($url, ':') + 3) === false) { |
|
2038
|
|
|
$url .= '/'; |
|
2039
|
|
|
} |
|
2040
|
|
|
|
|
2041
|
|
|
//convert IDNA hostname to punycode if possible |
|
2042
|
|
|
if (function_exists("idn_to_ascii")) { |
|
2043
|
|
|
$parts = parse_url($url); |
|
2044
|
|
|
if (mb_detect_encoding($parts['host']) != 'ASCII') |
|
2045
|
|
|
{ |
|
2046
|
|
|
$parts['host'] = idn_to_ascii($parts['host']); |
|
2047
|
|
|
$url = build_url($parts); |
|
2048
|
|
|
} |
|
2049
|
|
|
} |
|
2050
|
|
|
|
|
2051
|
|
|
if ($url != "http:///") { |
|
2052
|
|
|
return $url; |
|
2053
|
|
|
} else { |
|
2054
|
|
|
return ''; |
|
2055
|
|
|
} |
|
2056
|
|
|
} |
|
2057
|
|
|
|
|
2058
|
|
|
public static function add_feed_category($feed_cat, $parent_cat_id = false, $order_id = 0) { |
|
2059
|
|
|
|
|
2060
|
|
|
if (!$feed_cat) { |
|
2061
|
|
|
return false; |
|
2062
|
|
|
} |
|
2063
|
|
|
|
|
2064
|
|
|
$feed_cat = mb_substr($feed_cat, 0, 250); |
|
2065
|
|
|
if (!$parent_cat_id) { |
|
2066
|
|
|
$parent_cat_id = null; |
|
2067
|
|
|
} |
|
2068
|
|
|
|
|
2069
|
|
|
$pdo = Db::pdo(); |
|
2070
|
|
|
$tr_in_progress = false; |
|
2071
|
|
|
|
|
2072
|
|
|
try { |
|
2073
|
|
|
$pdo->beginTransaction(); |
|
2074
|
|
|
} catch (Exception $e) { |
|
2075
|
|
|
$tr_in_progress = true; |
|
2076
|
|
|
} |
|
2077
|
|
|
|
|
2078
|
|
|
$sth = $pdo->prepare("SELECT id FROM ttrss_feed_categories |
|
2079
|
|
|
WHERE (parent_cat = :parent OR (:parent IS NULL AND parent_cat IS NULL)) |
|
2080
|
|
|
AND title = :title AND owner_uid = :uid"); |
|
2081
|
|
|
$sth->execute([':parent' => $parent_cat_id, ':title' => $feed_cat, ':uid' => $_SESSION['uid']]); |
|
2082
|
|
|
|
|
2083
|
|
|
if (!$sth->fetch()) { |
|
2084
|
|
|
|
|
2085
|
|
|
$sth = $pdo->prepare("INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat,order_id) |
|
2086
|
|
|
VALUES (?, ?, ?, ?)"); |
|
2087
|
|
|
$sth->execute([$_SESSION['uid'], $feed_cat, $parent_cat_id, (int) $order_id]); |
|
2088
|
|
|
|
|
2089
|
|
|
if (!$tr_in_progress) { |
|
2090
|
|
|
$pdo->commit(); |
|
2091
|
|
|
} |
|
2092
|
|
|
|
|
2093
|
|
|
return true; |
|
2094
|
|
|
} |
|
2095
|
|
|
|
|
2096
|
|
|
$pdo->commit(); |
|
2097
|
|
|
|
|
2098
|
|
|
return false; |
|
2099
|
|
|
} |
|
2100
|
|
|
|
|
2101
|
|
|
public static function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) { |
|
2102
|
|
|
|
|
2103
|
|
|
if (!$owner_uid) { |
|
2104
|
|
|
$owner_uid = $_SESSION["uid"]; |
|
2105
|
|
|
} |
|
2106
|
|
|
|
|
2107
|
|
|
$is_cat = bool_to_sql_bool($is_cat); |
|
2108
|
|
|
|
|
2109
|
|
|
$pdo = Db::pdo(); |
|
2110
|
|
|
|
|
2111
|
|
|
$sth = $pdo->prepare("SELECT access_key FROM ttrss_access_keys |
|
2112
|
|
|
WHERE feed_id = ? AND is_cat = ? |
|
2113
|
|
|
AND owner_uid = ?"); |
|
2114
|
|
|
$sth->execute([$feed_id, $is_cat, $owner_uid]); |
|
2115
|
|
|
|
|
2116
|
|
|
if ($row = $sth->fetch()) { |
|
2117
|
|
|
return $row["access_key"]; |
|
2118
|
|
|
} else { |
|
2119
|
|
|
$key = uniqid_short(); |
|
2120
|
|
|
|
|
2121
|
|
|
$sth = $pdo->prepare("INSERT INTO ttrss_access_keys |
|
2122
|
|
|
(access_key, feed_id, is_cat, owner_uid) |
|
2123
|
|
|
VALUES (?, ?, ?, ?)"); |
|
2124
|
|
|
|
|
2125
|
|
|
$sth->execute([$key, $feed_id, $is_cat, $owner_uid]); |
|
2126
|
|
|
|
|
2127
|
|
|
return $key; |
|
2128
|
|
|
} |
|
2129
|
|
|
} |
|
2130
|
|
|
|
|
2131
|
|
|
/** |
|
2132
|
|
|
* Purge a feed old posts. |
|
2133
|
|
|
* |
|
2134
|
|
|
* @param mixed $link A database connection. |
|
2135
|
|
|
* @param mixed $feed_id The id of the purged feed. |
|
2136
|
|
|
* @param mixed $purge_interval Olderness of purged posts. |
|
2137
|
|
|
* @param boolean $debug Set to True to enable the debug. False by default. |
|
2138
|
|
|
* @access public |
|
2139
|
|
|
* @return void |
|
2140
|
|
|
*/ |
|
2141
|
|
|
public static function purge_feed($feed_id, $purge_interval) { |
|
2142
|
|
|
|
|
2143
|
|
|
if (!$purge_interval) { |
|
2144
|
|
|
$purge_interval = Feeds::feed_purge_interval($feed_id); |
|
2145
|
|
|
} |
|
2146
|
|
|
|
|
2147
|
|
|
$pdo = Db::pdo(); |
|
2148
|
|
|
|
|
2149
|
|
|
$sth = $pdo->prepare("SELECT owner_uid FROM ttrss_feeds WHERE id = ?"); |
|
2150
|
|
|
$sth->execute([$feed_id]); |
|
2151
|
|
|
|
|
2152
|
|
|
$owner_uid = false; |
|
2153
|
|
|
|
|
2154
|
|
|
if ($row = $sth->fetch()) { |
|
2155
|
|
|
$owner_uid = $row["owner_uid"]; |
|
2156
|
|
|
} |
|
2157
|
|
|
|
|
2158
|
|
|
if ($purge_interval == -1 || !$purge_interval) { |
|
2159
|
|
|
if ($owner_uid) { |
|
2160
|
|
|
CCache::update($feed_id, $owner_uid); |
|
2161
|
|
|
} |
|
2162
|
|
|
return; |
|
2163
|
|
|
} |
|
2164
|
|
|
|
|
2165
|
|
|
if (!$owner_uid) { |
|
2166
|
|
|
return; |
|
2167
|
|
|
} |
|
2168
|
|
|
|
|
2169
|
|
|
if (FORCE_ARTICLE_PURGE == 0) { |
|
|
|
|
|
|
2170
|
|
|
$purge_unread = get_pref("PURGE_UNREAD_ARTICLES", |
|
2171
|
|
|
$owner_uid, false); |
|
2172
|
|
|
} else { |
|
2173
|
|
|
$purge_unread = true; |
|
2174
|
|
|
$purge_interval = FORCE_ARTICLE_PURGE; |
|
2175
|
|
|
} |
|
2176
|
|
|
|
|
2177
|
|
|
if (!$purge_unread) { |
|
2178
|
|
|
$query_limit = " unread = false AND "; |
|
2179
|
|
|
} else { |
|
2180
|
|
|
$query_limit = ""; |
|
2181
|
|
|
} |
|
2182
|
|
|
|
|
2183
|
|
|
$purge_interval = (int) $purge_interval; |
|
2184
|
|
|
|
|
2185
|
|
|
if (DB_TYPE == "pgsql") { |
|
|
|
|
|
|
2186
|
|
|
$sth = $pdo->prepare("DELETE FROM ttrss_user_entries |
|
2187
|
|
|
USING ttrss_entries |
|
2188
|
|
|
WHERE ttrss_entries.id = ref_id AND |
|
2189
|
|
|
marked = false AND |
|
2190
|
|
|
feed_id = ? AND |
|
2191
|
|
|
$query_limit |
|
2192
|
|
|
ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'"); |
|
2193
|
|
|
$sth->execute([$feed_id]); |
|
2194
|
|
|
|
|
2195
|
|
|
} else { |
|
2196
|
|
|
$sth = $pdo->prepare("DELETE FROM ttrss_user_entries |
|
2197
|
|
|
USING ttrss_user_entries, ttrss_entries |
|
2198
|
|
|
WHERE ttrss_entries.id = ref_id AND |
|
2199
|
|
|
marked = false AND |
|
2200
|
|
|
feed_id = ? AND |
|
2201
|
|
|
$query_limit |
|
2202
|
|
|
ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); |
|
2203
|
|
|
$sth->execute([$feed_id]); |
|
2204
|
|
|
|
|
2205
|
|
|
} |
|
2206
|
|
|
|
|
2207
|
|
|
$rows = $sth->rowCount(); |
|
2208
|
|
|
|
|
2209
|
|
|
CCache::update($feed_id, $owner_uid); |
|
2210
|
|
|
|
|
2211
|
|
|
Debug::log("Purged feed $feed_id ($purge_interval): deleted $rows articles"); |
|
2212
|
|
|
|
|
2213
|
|
|
return $rows; |
|
2214
|
|
|
} |
|
2215
|
|
|
|
|
2216
|
|
|
public static function feed_purge_interval($feed_id) { |
|
2217
|
|
|
|
|
2218
|
|
|
$pdo = DB::pdo(); |
|
2219
|
|
|
|
|
2220
|
|
|
$sth = $pdo->prepare("SELECT purge_interval, owner_uid FROM ttrss_feeds |
|
2221
|
|
|
WHERE id = ?"); |
|
2222
|
|
|
$sth->execute([$feed_id]); |
|
2223
|
|
|
|
|
2224
|
|
|
if ($row = $sth->fetch()) { |
|
2225
|
|
|
$purge_interval = $row["purge_interval"]; |
|
2226
|
|
|
$owner_uid = $row["owner_uid"]; |
|
2227
|
|
|
|
|
2228
|
|
|
if ($purge_interval == 0) { |
|
2229
|
|
|
$purge_interval = get_pref( |
|
2230
|
|
|
'PURGE_OLD_DAYS', $owner_uid); |
|
2231
|
|
|
} |
|
2232
|
|
|
|
|
2233
|
|
|
return $purge_interval; |
|
2234
|
|
|
|
|
2235
|
|
|
} else { |
|
2236
|
|
|
return -1; |
|
2237
|
|
|
} |
|
2238
|
|
|
} |
|
2239
|
|
|
|
|
2240
|
|
|
public static function search_to_sql($search, $search_language) { |
|
2241
|
|
|
|
|
2242
|
|
|
$keywords = str_getcsv(trim($search), " "); |
|
2243
|
|
|
$query_keywords = array(); |
|
2244
|
|
|
$search_words = array(); |
|
2245
|
|
|
$search_query_leftover = array(); |
|
2246
|
|
|
|
|
2247
|
|
|
$pdo = Db::pdo(); |
|
2248
|
|
|
|
|
2249
|
|
|
if ($search_language) { |
|
2250
|
|
|
$search_language = $pdo->quote(mb_strtolower($search_language)); |
|
2251
|
|
|
} else { |
|
2252
|
|
|
$search_language = $pdo->quote("english"); |
|
2253
|
|
|
} |
|
2254
|
|
|
|
|
2255
|
|
|
foreach ($keywords as $k) { |
|
2256
|
|
|
if (strpos($k, "-") === 0) { |
|
2257
|
|
|
$k = substr($k, 1); |
|
2258
|
|
|
$not = "NOT"; |
|
2259
|
|
|
} else { |
|
2260
|
|
|
$not = ""; |
|
2261
|
|
|
} |
|
2262
|
|
|
|
|
2263
|
|
|
$commandpair = explode(":", mb_strtolower($k), 2); |
|
2264
|
|
|
|
|
2265
|
|
|
switch ($commandpair[0]) { |
|
2266
|
|
|
case "title": |
|
2267
|
|
|
if ($commandpair[1]) { |
|
2268
|
|
|
array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE ". |
|
2269
|
|
|
$pdo->quote('%'.mb_strtolower($commandpair[1]).'%')."))"); |
|
2270
|
|
|
} else { |
|
2271
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%') |
|
2272
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2273
|
|
|
array_push($search_words, $k); |
|
2274
|
|
|
} |
|
2275
|
|
|
break; |
|
2276
|
|
|
case "author": |
|
2277
|
|
|
if ($commandpair[1]) { |
|
2278
|
|
|
array_push($query_keywords, "($not (LOWER(author) LIKE ". |
|
2279
|
|
|
$pdo->quote('%'.mb_strtolower($commandpair[1]).'%')."))"); |
|
2280
|
|
|
} else { |
|
2281
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%') |
|
2282
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2283
|
|
|
array_push($search_words, $k); |
|
2284
|
|
|
} |
|
2285
|
|
|
break; |
|
2286
|
|
|
case "note": |
|
2287
|
|
|
if ($commandpair[1]) { |
|
2288
|
|
|
if ($commandpair[1] == "true") |
|
2289
|
|
|
array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))"); |
|
2290
|
|
|
else if ($commandpair[1] == "false") |
|
2291
|
|
|
array_push($query_keywords, "($not (note IS NULL OR note = ''))"); |
|
2292
|
|
|
else |
|
2293
|
|
|
array_push($query_keywords, "($not (LOWER(note) LIKE ". |
|
2294
|
|
|
$pdo->quote('%'.mb_strtolower($commandpair[1]).'%')."))"); |
|
2295
|
|
|
} else { |
|
2296
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").") |
|
2297
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2298
|
|
|
if (!$not) array_push($search_words, $k); |
|
2299
|
|
|
} |
|
2300
|
|
|
break; |
|
2301
|
|
|
case "star": |
|
2302
|
|
|
|
|
2303
|
|
|
if ($commandpair[1]) { |
|
2304
|
|
|
if ($commandpair[1] == "true") |
|
2305
|
|
|
array_push($query_keywords, "($not (marked = true))"); |
|
2306
|
|
|
else |
|
2307
|
|
|
array_push($query_keywords, "($not (marked = false))"); |
|
2308
|
|
|
} else { |
|
2309
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").") |
|
2310
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2311
|
|
|
if (!$not) array_push($search_words, $k); |
|
2312
|
|
|
} |
|
2313
|
|
|
break; |
|
2314
|
|
|
case "pub": |
|
2315
|
|
|
if ($commandpair[1]) { |
|
2316
|
|
|
if ($commandpair[1] == "true") |
|
2317
|
|
|
array_push($query_keywords, "($not (published = true))"); |
|
2318
|
|
|
else |
|
2319
|
|
|
array_push($query_keywords, "($not (published = false))"); |
|
2320
|
|
|
|
|
2321
|
|
|
} else { |
|
2322
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%') |
|
2323
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2324
|
|
|
if (!$not) array_push($search_words, $k); |
|
2325
|
|
|
} |
|
2326
|
|
|
break; |
|
2327
|
|
|
case "unread": |
|
2328
|
|
|
if ($commandpair[1]) { |
|
2329
|
|
|
if ($commandpair[1] == "true") |
|
2330
|
|
|
array_push($query_keywords, "($not (unread = true))"); |
|
2331
|
|
|
else |
|
2332
|
|
|
array_push($query_keywords, "($not (unread = false))"); |
|
2333
|
|
|
|
|
2334
|
|
|
} else { |
|
2335
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").") |
|
2336
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2337
|
|
|
if (!$not) array_push($search_words, $k); |
|
2338
|
|
|
} |
|
2339
|
|
|
break; |
|
2340
|
|
|
default: |
|
2341
|
|
|
if (strpos($k, "@") === 0) { |
|
2342
|
|
|
|
|
2343
|
|
|
$user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']); |
|
2344
|
|
|
$orig_ts = strtotime(substr($k, 1)); |
|
2345
|
|
|
$k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC')); |
|
2346
|
|
|
|
|
2347
|
|
|
//$k = date("Y-m-d", strtotime(substr($k, 1))); |
|
2348
|
|
|
|
|
2349
|
|
|
array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')"); |
|
2350
|
|
|
} else { |
|
2351
|
|
|
|
|
2352
|
|
|
if (DB_TYPE == "pgsql") { |
|
|
|
|
|
|
2353
|
|
|
$k = mb_strtolower($k); |
|
2354
|
|
|
array_push($search_query_leftover, $not ? "!$k" : $k); |
|
2355
|
|
|
} else { |
|
2356
|
|
|
array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER(".$pdo->quote("%$k%").") |
|
2357
|
|
|
OR UPPER(ttrss_entries.content) $not LIKE UPPER(".$pdo->quote("%$k%")."))"); |
|
2358
|
|
|
} |
|
2359
|
|
|
|
|
2360
|
|
|
if (!$not) { |
|
2361
|
|
|
array_push($search_words, $k); |
|
2362
|
|
|
} |
|
2363
|
|
|
} |
|
2364
|
|
|
} |
|
2365
|
|
|
} |
|
2366
|
|
|
|
|
2367
|
|
|
if (count($search_query_leftover) > 0) { |
|
2368
|
|
|
|
|
2369
|
|
|
if (DB_TYPE == "pgsql") { |
|
2370
|
|
|
|
|
2371
|
|
|
// if there's no joiners consider this a "simple" search and |
|
2372
|
|
|
// concatenate everything with &, otherwise don't try to mess with tsquery syntax |
|
2373
|
|
|
if (preg_match("/[&|]/", implode(" ", $search_query_leftover))) { |
|
2374
|
|
|
$tsquery = $pdo->quote(implode(" ", $search_query_leftover)); |
|
2375
|
|
|
} else { |
|
2376
|
|
|
$tsquery = $pdo->quote(implode(" & ", $search_query_leftover)); |
|
2377
|
|
|
} |
|
2378
|
|
|
|
|
2379
|
|
|
array_push($query_keywords, |
|
2380
|
|
|
"(tsvector_combined @@ to_tsquery($search_language, $tsquery))"); |
|
2381
|
|
|
} |
|
2382
|
|
|
|
|
2383
|
|
|
} |
|
2384
|
|
|
|
|
2385
|
|
|
$search_query_part = implode("AND", $query_keywords); |
|
2386
|
|
|
|
|
2387
|
|
|
return array($search_query_part, $search_words); |
|
2388
|
|
|
} |
|
2389
|
|
|
} |
|
2390
|
|
|
|
|
2391
|
|
|
|