]> git.wh0rd.org - tt-rss.git/blame - include/functions2.php
af_redditimgur: add support for bullshit ajaxified imgur albums
[tt-rss.git] / include / functions2.php
CommitLineData
97b7d5c0
AD
1<?php
2 function make_init_params() {
3 $params = array();
4
5 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
6 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
7 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
8 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
9
10 $params[strtolower($param)] = (int) get_pref($param);
11 }
12
13 $params["icons_url"] = ICONS_URL;
14 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME;
15 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
16 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
17 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
18 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
19 $params["label_base_index"] = (int) LABEL_BASE_INDEX;
f6cbe9a5
AD
20
21 $theme = get_pref( "USER_CSS_THEME", false, false);
22 $params["theme"] = theme_valid("$theme") ? $theme : "";
23
84e36b61 24 $params["plugins"] = implode(", ", PluginHost::getInstance()->get_plugin_names());
97b7d5c0 25
31460f84
AD
26 $params["php_platform"] = PHP_OS;
27 $params["php_version"] = PHP_VERSION;
28
29 $params["sanity_checksum"] = sha1(file_get_contents("include/sanity_check.php"));
30
97b7d5c0
AD
31 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
32 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
33
34 $max_feed_id = db_fetch_result($result, 0, "mid");
35 $num_feeds = db_fetch_result($result, 0, "nf");
36
37 $params["max_feed_id"] = (int) $max_feed_id;
38 $params["num_feeds"] = (int) $num_feeds;
39
40 $params["hotkeys"] = get_hotkeys_map();
41
42 $params["csrf_token"] = $_SESSION["csrf_token"];
43 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
44
45 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE;
46
47 return $params;
48 }
49
50 function get_hotkeys_info() {
51 $hotkeys = array(
52 __("Navigation") => array(
53 "next_feed" => __("Open next feed"),
54 "prev_feed" => __("Open previous feed"),
55 "next_article" => __("Open next article"),
56 "prev_article" => __("Open previous article"),
57 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
58 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
59 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
60 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
61 "search_dialog" => __("Show search dialog")),
62 __("Article") => array(
63 "toggle_mark" => __("Toggle starred"),
64 "toggle_publ" => __("Toggle published"),
65 "toggle_unread" => __("Toggle unread"),
66 "edit_tags" => __("Edit tags"),
67 "dismiss_selected" => __("Dismiss selected"),
68 "dismiss_read" => __("Dismiss read"),
69 "open_in_new_window" => __("Open in new window"),
70 "catchup_below" => __("Mark below as read"),
71 "catchup_above" => __("Mark above as read"),
72 "article_scroll_down" => __("Scroll down"),
73 "article_scroll_up" => __("Scroll up"),
74 "select_article_cursor" => __("Select article under cursor"),
75 "email_article" => __("Email article"),
76 "close_article" => __("Close/collapse article"),
77 "toggle_expand" => __("Toggle article expansion (combined mode)"),
78 "toggle_widescreen" => __("Toggle widescreen mode"),
79 "toggle_embed_original" => __("Toggle embed original")),
80 __("Article selection") => array(
81 "select_all" => __("Select all articles"),
82 "select_unread" => __("Select unread"),
83 "select_marked" => __("Select starred"),
84 "select_published" => __("Select published"),
85 "select_invert" => __("Invert selection"),
86 "select_none" => __("Deselect everything")),
87 __("Feed") => array(
88 "feed_refresh" => __("Refresh current feed"),
89 "feed_unhide_read" => __("Un/hide read feeds"),
90 "feed_subscribe" => __("Subscribe to feed"),
91 "feed_edit" => __("Edit feed"),
92 "feed_catchup" => __("Mark as read"),
93 "feed_reverse" => __("Reverse headlines"),
94 "feed_debug_update" => __("Debug feed update"),
95 "catchup_all" => __("Mark all feeds as read"),
96 "cat_toggle_collapse" => __("Un/collapse current category"),
97 "toggle_combined_mode" => __("Toggle combined mode"),
98 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
99 __("Go to") => array(
100 "goto_all" => __("All articles"),
101 "goto_fresh" => __("Fresh"),
102 "goto_marked" => __("Starred"),
103 "goto_published" => __("Published"),
104 "goto_tagcloud" => __("Tag cloud"),
105 "goto_prefs" => __("Preferences")),
106 __("Other") => array(
107 "create_label" => __("Create label"),
108 "create_filter" => __("Create filter"),
109 "collapse_sidebar" => __("Un/collapse sidebar"),
110 "help_dialog" => __("Show help dialog"))
111 );
112
113 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HOTKEY_INFO) as $plugin) {
114 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
115 }
116
117 return $hotkeys;
118 }
119
120 function get_hotkeys_map() {
121 $hotkeys = array(
122// "navigation" => array(
123 "k" => "next_feed",
124 "j" => "prev_feed",
125 "n" => "next_article",
126 "p" => "prev_article",
127 "(38)|up" => "prev_article",
128 "(40)|down" => "next_article",
129// "^(38)|Ctrl-up" => "prev_article_noscroll",
130// "^(40)|Ctrl-down" => "next_article_noscroll",
131 "(191)|/" => "search_dialog",
132// "article" => array(
133 "s" => "toggle_mark",
134 "*s" => "toggle_publ",
135 "u" => "toggle_unread",
136 "*t" => "edit_tags",
137 "*d" => "dismiss_selected",
138 "*x" => "dismiss_read",
139 "o" => "open_in_new_window",
140 "c p" => "catchup_below",
141 "c n" => "catchup_above",
142 "*n" => "article_scroll_down",
143 "*p" => "article_scroll_up",
144 "*(38)|Shift+up" => "article_scroll_up",
145 "*(40)|Shift+down" => "article_scroll_down",
146 "a *w" => "toggle_widescreen",
147 "a e" => "toggle_embed_original",
148 "e" => "email_article",
149 "a q" => "close_article",
150// "article_selection" => array(
151 "a a" => "select_all",
152 "a u" => "select_unread",
153 "a *u" => "select_marked",
154 "a p" => "select_published",
155 "a i" => "select_invert",
156 "a n" => "select_none",
157// "feed" => array(
158 "f r" => "feed_refresh",
159 "f a" => "feed_unhide_read",
160 "f s" => "feed_subscribe",
161 "f e" => "feed_edit",
162 "f q" => "feed_catchup",
163 "f x" => "feed_reverse",
164 "f *d" => "feed_debug_update",
165 "f *c" => "toggle_combined_mode",
166 "f c" => "toggle_cdm_expanded",
167 "*q" => "catchup_all",
168 "x" => "cat_toggle_collapse",
169// "goto" => array(
170 "g a" => "goto_all",
171 "g f" => "goto_fresh",
172 "g s" => "goto_marked",
173 "g p" => "goto_published",
174 "g t" => "goto_tagcloud",
175 "g *p" => "goto_prefs",
176// "other" => array(
177 "(9)|Tab" => "select_article_cursor", // tab
178 "c l" => "create_label",
179 "c f" => "create_filter",
180 "c s" => "collapse_sidebar",
181 "^(191)|Ctrl+/" => "help_dialog",
182 );
183
184 if (get_pref('COMBINED_DISPLAY_MODE')) {
185 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
186 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
187 }
188
189 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_HOTKEY_MAP) as $plugin) {
190 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
191 }
192
193 $prefixes = array();
194
195 foreach (array_keys($hotkeys) as $hotkey) {
196 $pair = explode(" ", $hotkey, 2);
197
198 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
199 array_push($prefixes, $pair[0]);
200 }
201 }
202
203 return array($prefixes, $hotkeys);
204 }
205
206 function make_runtime_info() {
207 $data = array();
208
209 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
210 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
211
212 $max_feed_id = db_fetch_result($result, 0, "mid");
213 $num_feeds = db_fetch_result($result, 0, "nf");
214
215 $data["max_feed_id"] = (int) $max_feed_id;
216 $data["num_feeds"] = (int) $num_feeds;
217
218 $data['last_article_id'] = getLastArticleId();
219 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
220
221 $data['dep_ts'] = calculate_dep_timestamp();
222 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
223
224 if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
225
226 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
227
228 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
229
230 $stamp = (int) @file_get_contents(LOCK_DIRECTORY . "/update_daemon.stamp");
231
232 if ($stamp) {
233 $stamp_delta = time() - $stamp;
234
235 if ($stamp_delta > 1800) {
236 $stamp_check = 0;
237 } else {
238 $stamp_check = 1;
239 $_SESSION["daemon_stamp_check"] = time();
240 }
241
242 $data['daemon_stamp_ok'] = $stamp_check;
243
244 $stamp_fmt = date("Y.m.d, G:i", $stamp);
245
246 $data['daemon_stamp'] = $stamp_fmt;
247 }
248 }
249 }
250
97b7d5c0
AD
251 return $data;
252 }
253
254 function search_to_sql($search) {
255
256 $search_query_part = "";
257
258 $keywords = str_getcsv($search, " ");
259 $query_keywords = array();
260 $search_words = array();
261
262 foreach ($keywords as $k) {
263 if (strpos($k, "-") === 0) {
264 $k = substr($k, 1);
265 $not = "NOT";
266 } else {
267 $not = "";
268 }
269
270 $commandpair = explode(":", mb_strtolower($k), 2);
271
272 switch ($commandpair[0]) {
273 case "title":
274 if ($commandpair[1]) {
275 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
276 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
277 } else {
278 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
279 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
280 array_push($search_words, $k);
281 }
282 break;
283 case "author":
284 if ($commandpair[1]) {
285 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
286 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
287 } else {
288 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
289 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
290 array_push($search_words, $k);
291 }
292 break;
293 case "note":
294 if ($commandpair[1]) {
295 if ($commandpair[1] == "true")
296 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
297 else if ($commandpair[1] == "false")
298 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
299 else
300 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
301 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
302 } else {
303 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
304 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
305 if (!$not) array_push($search_words, $k);
306 }
307 break;
308 case "star":
309
310 if ($commandpair[1]) {
311 if ($commandpair[1] == "true")
312 array_push($query_keywords, "($not (marked = true))");
313 else
314 array_push($query_keywords, "($not (marked = false))");
315 } else {
316 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
317 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
318 if (!$not) array_push($search_words, $k);
319 }
320 break;
321 case "pub":
322 if ($commandpair[1]) {
323 if ($commandpair[1] == "true")
324 array_push($query_keywords, "($not (published = true))");
325 else
326 array_push($query_keywords, "($not (published = false))");
327
328 } else {
329 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
330 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
331 if (!$not) array_push($search_words, $k);
332 }
333 break;
334 default:
335 if (strpos($k, "@") === 0) {
336
337 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
338 $orig_ts = strtotime(substr($k, 1));
339 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
340
341 //$k = date("Y-m-d", strtotime(substr($k, 1)));
342
343 array_push($query_keywords, "(".SUBSTRING_FOR_DATE."(updated,1,LENGTH('$k')) $not = '$k')");
344 } else {
345 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
346 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
347
348 if (!$not) array_push($search_words, $k);
349 }
350 }
351 }
352
353 $search_query_part = implode("AND", $query_keywords);
354
355 return array($search_query_part, $search_words);
356 }
357
358 function getParentCategories($cat, $owner_uid) {
359 $rv = array();
360
361 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
362 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
363
364 while ($line = db_fetch_assoc($result)) {
365 array_push($rv, $line["parent_cat"]);
366 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
367 }
368
369 return $rv;
370 }
371
372 function getChildCategories($cat, $owner_uid) {
373 $rv = array();
374
375 $result = db_query("SELECT id FROM ttrss_feed_categories
376 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
377
378 while ($line = db_fetch_assoc($result)) {
379 array_push($rv, $line["id"]);
380 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
381 }
382
383 return $rv;
384 }
385
d1e631f3 386 function queryFeedHeadlines($feed, $limit, $view_mode, $cat_view, $search, $search_mode, $override_order = false, $offset = 0, $owner_uid = 0, $filter = false, $since_id = 0, $include_children = false, $ignore_vfeed_group = false, $override_strategy = false, $override_vfeed = false, $start_ts = false) {
97b7d5c0
AD
387
388 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
389
390 $ext_tables_part = "";
391 $search_words = array();
392
393 if ($search) {
baaf4c30
RH
394 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH) as $plugin) {
395 list($search_query_part, $search_words) = $plugin->hook_search($search);
9a6456fa 396 break;
baaf4c30 397 }
97b7d5c0 398
baaf4c30
RH
399 // fall back in case of no plugins
400 if (!$search_query_part) {
97b7d5c0 401 list($search_query_part, $search_words) = search_to_sql($search);
97b7d5c0 402 }
baaf4c30 403 $search_query_part .= " AND ";
97b7d5c0
AD
404 } else {
405 $search_query_part = "";
406 }
407
408 if ($filter) {
409
410 if (DB_TYPE == "pgsql") {
411 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
412 } else {
413 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
414 }
415
416 $override_order = "updated DESC";
417
418 $filter_query_part = filter_to_sql($filter, $owner_uid);
419
420 // Try to check if SQL regexp implementation chokes on a valid regexp
421
422
7b3110c9
BS
423 $result = db_query("SELECT true AS true_val
424 FROM ttrss_entries
425 JOIN ttrss_user_entries ON ttrss_entries.id = ttrss_user_entries.ref_id
426 JOIN ttrss_feeds ON ttrss_feeds.id = ttrss_user_entries.feed_id
97b7d5c0
AD
427 WHERE $filter_query_part LIMIT 1", false);
428
429 if ($result) {
430 $test = db_fetch_result($result, 0, "true_val");
431
432 if (!$test) {
433 $filter_query_part = "false AND";
434 } else {
435 $filter_query_part .= " AND";
436 }
437 } else {
438 $filter_query_part = "false AND";
439 }
440
441 } else {
442 $filter_query_part = "";
443 }
444
445 if ($since_id) {
446 $since_id_part = "ttrss_entries.id > $since_id AND ";
447 } else {
448 $since_id_part = "";
449 }
450
451 $view_query_part = "";
452
453 if ($view_mode == "adaptive") {
454 if ($search) {
455 $view_query_part = " ";
456 } else if ($feed != -1) {
457
458 $unread = getFeedUnread($feed, $cat_view);
459
460 if ($cat_view && $feed > 0 && $include_children)
461 $unread += getCategoryChildrenUnread($feed);
462
463 if ($unread > 0)
464 $view_query_part = " unread = true AND ";
465
466 }
467 }
468
469 if ($view_mode == "marked") {
470 $view_query_part = " marked = true AND ";
471 }
472
473 if ($view_mode == "has_note") {
474 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
475 }
476
477 if ($view_mode == "published") {
478 $view_query_part = " published = true AND ";
479 }
480
481 if ($view_mode == "unread" && $feed != -6) {
482 $view_query_part = " unread = true AND ";
483 }
484
485 if ($limit > 0) {
486 $limit_query_part = "LIMIT " . $limit;
487 }
488
489 $allow_archived = false;
490
491 $vfeed_query_part = "";
492
493 // override query strategy and enable feed display when searching globally
494 if ($search && $search_mode == "all_feeds") {
495 $query_strategy_part = "true";
496 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
497 /* tags */
498 } else if (!is_numeric($feed)) {
499 $query_strategy_part = "true";
500 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
501 id = feed_id) as feed_title,";
502 } else if ($search && $search_mode == "this_cat") {
503 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
504
505 if ($feed > 0) {
506 if ($include_children) {
507 $subcats = getChildCategories($feed, $owner_uid);
508 array_push($subcats, $feed);
509 $cats_qpart = join(",", $subcats);
510 } else {
511 $cats_qpart = $feed;
512 }
513
514 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
515
516 } else {
517 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
518 }
519
520 } else if ($feed > 0) {
521
522 if ($cat_view) {
523
524 if ($feed > 0) {
525 if ($include_children) {
526 # sub-cats
527 $subcats = getChildCategories($feed, $owner_uid);
528
529 array_push($subcats, $feed);
530 $query_strategy_part = "cat_id IN (".
531 implode(",", $subcats).")";
532
533 } else {
534 $query_strategy_part = "cat_id = '$feed'";
535 }
536
537 } else {
538 $query_strategy_part = "cat_id IS NULL";
539 }
540
541 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
542
543 } else {
544 $query_strategy_part = "feed_id = '$feed'";
545 }
546 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
547 $query_strategy_part = "feed_id IS NULL";
548 $allow_archived = true;
549 } else if ($feed == 0 && $cat_view) { // uncategorized
550 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
551 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
552 } else if ($feed == -1) { // starred virtual feed
553 $query_strategy_part = "marked = true";
554 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
555 $allow_archived = true;
556
557 if (!$override_order) {
558 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
559 }
560
561 } else if ($feed == -2) { // published virtual feed OR labels category
562
563 if (!$cat_view) {
564 $query_strategy_part = "published = true";
565 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
566 $allow_archived = true;
567
568 if (!$override_order) {
569 $override_order = "last_published DESC, date_entered DESC, updated DESC";
570 }
571
572 } else {
573 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
574
575 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
576
577 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
578 ttrss_user_labels2.article_id = ref_id";
579
580 }
581 } else if ($feed == -6) { // recently read
582 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
583 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
584 $allow_archived = true;
ad593e43 585 $ignore_vfeed_group = true;
97b7d5c0
AD
586
587 if (!$override_order) $override_order = "last_read DESC";
588
589/* } else if ($feed == -7) { // shared
590 $query_strategy_part = "uuid != ''";
591 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
592 $allow_archived = true; */
593 } else if ($feed == -3) { // fresh virtual feed
594 $query_strategy_part = "unread = true AND score >= 0";
595
596 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
597
598 if (DB_TYPE == "pgsql") {
599 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
600 } else {
601 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
602 }
603
604 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
605 } else if ($feed == -4) { // all articles virtual feed
606 $allow_archived = true;
607 $query_strategy_part = "true";
608 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
609 } else if ($feed <= LABEL_BASE_INDEX) { // labels
610 $label_id = feed_to_label_id($feed);
611
612 $query_strategy_part = "label_id = '$label_id' AND
613 ttrss_labels2.id = ttrss_user_labels2.label_id AND
614 ttrss_user_labels2.article_id = ref_id";
615
616 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
617 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
618 $allow_archived = true;
619
620 } else {
621 $query_strategy_part = "true";
622 }
623
624 $order_by = "score DESC, date_entered DESC, updated DESC";
625
626 if ($view_mode == "unread_first") {
627 $order_by = "unread DESC, $order_by";
628 }
629
630 if ($override_order) {
631 $order_by = $override_order;
632 }
633
634 if ($override_strategy) {
635 $query_strategy_part = $override_strategy;
636 }
637
638 if ($override_vfeed) {
639 $vfeed_query_part = $override_vfeed;
640 }
641
642 $feed_title = "";
643
644 if ($search) {
645 $feed_title = T_sprintf("Search results: %s", $search);
646 } else {
647 if ($cat_view) {
648 $feed_title = getCategoryTitle($feed);
649 } else {
650 if (is_numeric($feed) && $feed > 0) {
651 $result = db_query("SELECT title,site_url,last_error,last_updated
652 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
653
654 $feed_title = db_fetch_result($result, 0, "title");
655 $feed_site_url = db_fetch_result($result, 0, "site_url");
656 $last_error = db_fetch_result($result, 0, "last_error");
657 $last_updated = db_fetch_result($result, 0, "last_updated");
658 } else {
659 $feed_title = getFeedTitle($feed);
660 }
661 }
662 }
663
664
665 $content_query_part = "content, ";
666
667
668 if (is_numeric($feed)) {
669
670 if ($feed >= 0) {
671 $feed_kind = "Feeds";
672 } else {
673 $feed_kind = "Labels";
674 }
675
676 if ($limit_query_part) {
677 $offset_query_part = "OFFSET $offset";
678 }
679
680 // proper override_order applied above
681 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
682 if (!$override_order) {
683 $order_by = "ttrss_feeds.title, $order_by";
684 } else {
685 $order_by = "ttrss_feeds.title, $override_order";
686 }
687 }
688
689 if (!$allow_archived) {
690 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
691 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
692
693 } else {
694 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
695 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
696 }
697
698 if ($vfeed_query_part)
699 $vfeed_query_part .= "favicon_avg_color,";
700
d1e631f3
AD
701 if ($start_ts) {
702 $start_ts_formatted = date("Y/m/d H:i:s", strtotime($start_ts));
99c19e1d 703 $start_ts_query_part = "date_entered >= '$start_ts_formatted' AND";
d1e631f3
AD
704 } else {
705 $start_ts_query_part = "";
706 }
707
97b7d5c0
AD
708 $query = "SELECT DISTINCT
709 date_entered,
710 guid,
711 ttrss_entries.id,ttrss_entries.title,
712 updated,
713 label_cache,
714 tag_cache,
715 always_display_enclosures,
716 site_url,
717 note,
718 num_comments,
719 comments,
720 int_id,
721 uuid,
722 lang,
723 hide_images,
724 unread,feed_id,marked,published,link,last_read,orig_feed_id,
725 last_marked, last_published,
726 $vfeed_query_part
727 $content_query_part
728 author,score
729 FROM
730 $from_qpart
731 WHERE
732 $feed_check_qpart
733 ttrss_user_entries.ref_id = ttrss_entries.id AND
734 ttrss_user_entries.owner_uid = '$owner_uid' AND
735 $search_query_part
d1e631f3 736 $start_ts_query_part
97b7d5c0
AD
737 $filter_query_part
738 $view_query_part
739 $since_id_part
740 $query_strategy_part ORDER BY $order_by
741 $limit_query_part $offset_query_part";
742
743 if ($_REQUEST["debug"]) print $query;
744
745 $result = db_query($query);
746
747 } else {
748 // browsing by tag
749
750 $select_qpart = "SELECT DISTINCT " .
751 "date_entered," .
752 "guid," .
753 "note," .
754 "ttrss_entries.id as id," .
755 "title," .
756 "updated," .
757 "unread," .
758 "feed_id," .
759 "orig_feed_id," .
760 "marked," .
761 "num_comments, " .
762 "comments, " .
763 "tag_cache," .
764 "label_cache," .
765 "link," .
766 "lang," .
767 "uuid," .
768 "last_read," .
769 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
770 "last_marked, last_published, " .
771 $since_id_part .
772 $vfeed_query_part .
773 $content_query_part .
774 "score ";
775
776 $feed_kind = "Tags";
777 $all_tags = explode(",", $feed);
778 if ($search_mode == 'any') {
779 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
780 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
781 $where_qpart = " WHERE " .
782 "ref_id = ttrss_entries.id AND " .
783 "ttrss_user_entries.owner_uid = $owner_uid AND " .
784 "post_int_id = int_id AND $tag_sql AND " .
785 $view_query_part .
786 $search_query_part .
787 $query_strategy_part . " ORDER BY $order_by " .
788 $limit_query_part;
789
790 } else {
791 $i = 1;
792 $sub_selects = array();
793 $sub_ands = array();
794 foreach ($all_tags as $term) {
795 array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i");
796 $i++;
797 }
798 if ($i > 2) {
799 $x = 1;
800 $y = 2;
801 do {
802 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
803 $x++;
804 $y++;
805 } while ($y < $i);
806 }
807 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
808 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
809 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
810 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
811 }
812 // error_log("TAG SQL: " . $tag_sql);
813 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
814
815 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
816 $result = db_query($select_qpart . $from_qpart . $where_qpart);
817 }
818
819 return array($result, $feed_title, $feed_site_url, $last_error, $last_updated, $search_words);
820
821 }
822
59b5d5f3
AD
823 function iframe_whitelisted($entry) {
824 $whitelist = array("youtube.com", "youtu.be", "vimeo.com");
825
826 @$src = parse_url($entry->getAttribute("src"), PHP_URL_HOST);
827
828 if ($src) {
829 foreach ($whitelist as $w) {
830 if ($src == $w || $src == "www.$w")
831 return true;
832 }
833 }
834
835 return false;
836 }
837
97b7d5c0
AD
838 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
839 if (!$owner) $owner = $_SESSION["uid"];
840
841 $res = trim($str); if (!$res) return '';
842
843 $charset_hack = '<head>
844 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
845 </head>';
846
847 $res = trim($res); if (!$res) return '';
848
849 libxml_use_internal_errors(true);
850
851 $doc = new DOMDocument();
852 $doc->loadHTML($charset_hack . $res);
853 $xpath = new DOMXPath($doc);
854
855 $entries = $xpath->query('(//a[@href]|//img[@src])');
856
857 foreach ($entries as $entry) {
858
859 if ($site_url) {
860
861 if ($entry->hasAttribute('href')) {
862 $entry->setAttribute('href',
863 rewrite_relative_url($site_url, $entry->getAttribute('href')));
864
865 $entry->setAttribute('rel', 'noreferrer');
866 }
867
868 if ($entry->hasAttribute('src')) {
869 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
870
871 $cached_filename = CACHE_DIR . '/images/' . sha1($src) . '.png';
872
873 if (file_exists($cached_filename)) {
874 $src = SELF_URL_PATH . '/image.php?hash=' . sha1($src);
875 }
876
877 $entry->setAttribute('src', $src);
878 }
879
880 if ($entry->nodeName == 'img') {
881 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
882 $force_remove_images || $_SESSION["bw_limit"]) {
883
884 $p = $doc->createElement('p');
885
886 $a = $doc->createElement('a');
887 $a->setAttribute('href', $entry->getAttribute('src'));
888
889 $a->appendChild(new DOMText($entry->getAttribute('src')));
890 $a->setAttribute('target', '_blank');
891
892 $p->appendChild($a);
893
894 $entry->parentNode->replaceChild($p, $entry);
895 }
896 }
897 }
898
899 if (strtolower($entry->nodeName) == "a") {
900 $entry->setAttribute("target", "_blank");
901 }
902 }
903
904 $entries = $xpath->query('//iframe');
905 foreach ($entries as $entry) {
59b5d5f3
AD
906 if (!iframe_whitelisted($entry)) {
907 $entry->setAttribute('sandbox', 'allow-scripts');
c63850fa
AD
908 } else {
909 if ($_SERVER['HTTPS'] == "on") {
910 $entry->setAttribute("src",
911 str_replace("http://", "https://",
912 $entry->getAttribute("src")));
913 }
59b5d5f3 914 }
97b7d5c0
AD
915 }
916
917 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
918 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
919 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
920 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
921 'dt', 'em', 'footer', 'figure', 'figcaption',
922 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
923 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
924 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
925 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
926 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
927 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
928
929 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
930
931 $disallowed_attributes = array('id', 'style', 'class');
932
933 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SANITIZE) as $plugin) {
934 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id);
935 if (is_array($retval)) {
936 $doc = $retval[0];
937 $allowed_elements = $retval[1];
938 $disallowed_attributes = $retval[2];
939 } else {
940 $doc = $retval;
941 }
942 }
943
944 $doc->removeChild($doc->firstChild); //remove doctype
945 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
946
947 if ($highlight_words) {
948 foreach ($highlight_words as $word) {
949
950 // http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph
951
952 $elements = $xpath->query("//*/text()");
953
954 foreach ($elements as $child) {
955
956 $fragment = $doc->createDocumentFragment();
957 $text = $child->textContent;
958
959 while (($pos = mb_stripos($text, $word)) !== false) {
960 $fragment->appendChild(new DomText(mb_substr($text, 0, $pos)));
961 $word = mb_substr($text, $pos, mb_strlen($word));
962 $highlight = $doc->createElement('span');
963 $highlight->appendChild(new DomText($word));
964 $highlight->setAttribute('class', 'highlight');
965 $fragment->appendChild($highlight);
966 $text = mb_substr($text, $pos + mb_strlen($word));
967 }
968
969 if (!empty($text)) $fragment->appendChild(new DomText($text));
970
971 $child->parentNode->replaceChild($fragment, $child);
972 }
973 }
974 }
975
976 $res = $doc->saveHTML();
977
978 return $res;
979 }
980
981 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
982 $xpath = new DOMXPath($doc);
983 $entries = $xpath->query('//*');
984
985 foreach ($entries as $entry) {
986 if (!in_array($entry->nodeName, $allowed_elements)) {
987 $entry->parentNode->removeChild($entry);
988 }
989
990 if ($entry->hasAttributes()) {
991 $attrs_to_remove = array();
992
993 foreach ($entry->attributes as $attr) {
994
995 if (strpos($attr->nodeName, 'on') === 0) {
996 array_push($attrs_to_remove, $attr);
997 }
998
999 if (in_array($attr->nodeName, $disallowed_attributes)) {
1000 array_push($attrs_to_remove, $attr);
1001 }
1002 }
1003
1004 foreach ($attrs_to_remove as $attr) {
1005 $entry->removeAttributeNode($attr);
1006 }
1007 }
1008 }
1009
1010 return $doc;
1011 }
1012
97b7d5c0
AD
1013 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
1014
1015 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1016 if (count($ids) == 0) return;
1017
1018 $tmp_ids = array();
1019
1020 foreach ($ids as $id) {
1021 array_push($tmp_ids, "ref_id = '$id'");
1022 }
1023
1024 $ids_qpart = join(" OR ", $tmp_ids);
1025
1026 if ($cmode == 0) {
1027 db_query("UPDATE ttrss_user_entries SET
1028 unread = false,last_read = NOW()
1029 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1030 } else if ($cmode == 1) {
1031 db_query("UPDATE ttrss_user_entries SET
1032 unread = true
1033 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1034 } else {
1035 db_query("UPDATE ttrss_user_entries SET
1036 unread = NOT unread,last_read = NOW()
1037 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1038 }
1039
1040 /* update ccache */
1041
1042 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
1043 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
1044
1045 while ($line = db_fetch_assoc($result)) {
1046 ccache_update($line["feed_id"], $owner_uid);
1047 }
1048 }
1049
1050 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
1051
1052 $a_id = db_escape_string($id);
1053
1054 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1055
1056 $query = "SELECT DISTINCT tag_name,
1057 owner_uid as owner FROM
1058 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
1059 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
1060
1061 $tags = array();
1062
1063 /* check cache first */
1064
1065 if ($tag_cache === false) {
1066 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
1067 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
1068
1069 $tag_cache = db_fetch_result($result, 0, "tag_cache");
1070 }
1071
1072 if ($tag_cache) {
1073 $tags = explode(",", $tag_cache);
1074 } else {
1075
1076 /* do it the hard way */
1077
1078 $tmp_result = db_query($query);
1079
1080 while ($tmp_line = db_fetch_assoc($tmp_result)) {
1081 array_push($tags, $tmp_line["tag_name"]);
1082 }
1083
1084 /* update the cache */
1085
1086 $tags_str = db_escape_string(join(",", $tags));
1087
1088 db_query("UPDATE ttrss_user_entries
1089 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
1090 AND owner_uid = $owner_uid");
1091 }
1092
1093 return $tags;
1094 }
1095
1096 function trim_array($array) {
1097 $tmp = $array;
1098 array_walk($tmp, 'trim');
1099 return $tmp;
1100 }
1101
1102 function tag_is_valid($tag) {
1103 if ($tag == '') return false;
1104 if (preg_match("/^[0-9]*$/", $tag)) return false;
1105 if (mb_strlen($tag) > 250) return false;
1106
1107 if (!$tag) return false;
1108
1109 return true;
1110 }
1111
1112 function render_login_form() {
1113 header('Cache-Control: public');
1114
1115 require_once "login_form.php";
1116 exit;
1117 }
1118
1119 function format_warning($msg, $id = "") {
1120 return "<div class=\"warning\" id=\"$id\">
1121 <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
1122 }
1123
1124 function format_notice($msg, $id = "") {
1125 return "<div class=\"notice\" id=\"$id\">
1126 <span><img src=\"images/information.png\"></span><span>$msg</span></div>";
1127 }
1128
1129 function format_error($msg, $id = "") {
1130 return "<div class=\"error\" id=\"$id\">
1131 <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
1132 }
1133
1134 function print_notice($msg) {
1135 return print format_notice($msg);
1136 }
1137
1138 function print_warning($msg) {
1139 return print format_warning($msg);
1140 }
1141
1142 function print_error($msg) {
1143 return print format_error($msg);
1144 }
1145
1146
1147 function T_sprintf() {
1148 $args = func_get_args();
1149 return vsprintf(__(array_shift($args)), $args);
1150 }
1151
1152 function format_inline_player($url, $ctype) {
1153
1154 $entry = "";
1155
1156 $url = htmlspecialchars($url);
1157
1158 if (strpos($ctype, "audio/") === 0) {
1159
1160 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
1161 $_SESSION["hasMp3"])) {
1162
1163 $entry .= "<audio preload=\"none\" controls>
1164 <source type=\"$ctype\" src=\"$url\"></source>
1165 </audio>";
1166
1167 } else {
1168
1169 $entry .= "<object type=\"application/x-shockwave-flash\"
1170 data=\"lib/button/musicplayer.swf?song_url=$url\"
1171 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
1172 <param name=\"movie\"
1173 value=\"lib/button/musicplayer.swf?song_url=$url\" />
1174 </object>";
1175 }
1176
1177 if ($entry) $entry .= "&nbsp; <a target=\"_blank\"
1178 href=\"$url\">" . basename($url) . "</a>";
1179
1180 return $entry;
1181
1182 }
1183
1184 return "";
1185
1186/* $filename = substr($url, strrpos($url, "/")+1);
1187
1188 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
1189 $filename . " (" . $ctype . ")" . "</a>"; */
1190
1191 }
1192
1193 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
1194 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1195
1196 $rv = array();
1197
1198 $rv['id'] = $id;
1199
1200 /* we can figure out feed_id from article id anyway, why do we
1201 * pass feed_id here? let's ignore the argument :(*/
1202
1203 $result = db_query("SELECT feed_id FROM ttrss_user_entries
1204 WHERE ref_id = '$id'");
1205
1206 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
1207
1208 $rv['feed_id'] = $feed_id;
1209
1210 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
1211
1212 if ($mark_as_read) {
1213 $result = db_query("UPDATE ttrss_user_entries
1214 SET unread = false,last_read = NOW()
1215 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
1216
1217 ccache_update($feed_id, $owner_uid);
1218 }
1219
1220 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,lang,
1221 ".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
1222 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
1223 (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,
1224 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
1225 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
1226 num_comments,
1227 tag_cache,
1228 author,
1229 orig_feed_id,
1230 note
1231 FROM ttrss_entries,ttrss_user_entries
1232 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
1233
1234 if ($result) {
1235
1236 $line = db_fetch_assoc($result);
1237
1238 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
1239 unset($line["tag_cache"]);
1240
1241 $line["content"] = sanitize($line["content"],
1242 sql_bool_to_bool($line['hide_images']),
1243 $owner_uid, $line["site_url"], false, $line["id"]);
1244
1245 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) {
1246 $line = $p->hook_render_article($line);
1247 }
1248
1249 $num_comments = $line["num_comments"];
1250 $entry_comments = "";
1251
1252 if ($num_comments > 0) {
1253 if ($line["comments"]) {
1254 $comments_url = htmlspecialchars($line["comments"]);
1255 } else {
1256 $comments_url = htmlspecialchars($line["link"]);
1257 }
1258 $entry_comments = "<a class=\"postComments\"
1259 target='_blank' href=\"$comments_url\">$num_comments ".
1260 _ngettext("comment", "comments", $num_comments)."</a>";
1261
1262 } else {
1263 if ($line["comments"] && $line["link"] != $line["comments"]) {
1264 $entry_comments = "<a class=\"postComments\" target='_blank' href=\"".htmlspecialchars($line["comments"])."\">".__("comments")."</a>";
1265 }
1266 }
1267
1268 if ($zoom_mode) {
1269 header("Content-Type: text/html");
1270 $rv['content'] .= "<html><head>
1271 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
1272 <title>Tiny Tiny RSS - ".$line["title"]."</title>".
1273 stylesheet_tag("css/tt-rss.css").
1274 stylesheet_tag("css/zoom.css").
1275 stylesheet_tag("css/dijit.css")."
1276
1277 <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
1278 <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">
1279
1280 <script type=\"text/javascript\">
1281 function openSelectedAttachment(elem) {
1282 try {
1283 var url = elem[elem.selectedIndex].value;
1284
1285 if (url) {
1286 window.open(url);
1287 elem.selectedIndex = 0;
1288 }
1289
1290 } catch (e) {
1291 exception_error(\"openSelectedAttachment\", e);
1292 }
1293 }
1294 </script>
1295 </head><body id=\"ttrssZoom\">";
1296 }
1297
1298 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
1299
1300 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
1301
1302 $entry_author = $line["author"];
1303
1304 if ($entry_author) {
1305 $entry_author = __(" - ") . $entry_author;
1306 }
1307
1308 $parsed_updated = make_local_datetime($line["updated"], true,
1309 $owner_uid, true);
1310
1311 if (!$zoom_mode)
1312 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
1313
1314 if ($line["link"]) {
1315 $rv['content'] .= "<div class='postTitle'><a target='_blank'
1316 title=\"".htmlspecialchars($line['title'])."\"
1317 href=\"" .
1318 htmlspecialchars($line["link"]) . "\">" .
1319 $line["title"] . "</a>" .
1320 "<span class='author'>$entry_author</span></div>";
1321 } else {
1322 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
1323 }
1324
1325 if ($zoom_mode) {
1326 $feed_title = "<a href=\"".htmlspecialchars($line["site_url"]).
1327 "\" target=\"_blank\">".
1328 htmlspecialchars($line["feed_title"])."</a>";
1329
1330 $rv['content'] .= "<div class=\"postFeedTitle\">$feed_title</div>";
1331
1332 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
1333 }
1334
1335 $tags_str = format_tags_string($line["tags"], $id);
1336 $tags_str_full = join(", ", $line["tags"]);
1337
1338 if (!$tags_str_full) $tags_str_full = __("no tags");
1339
1340 if (!$entry_comments) $entry_comments = "&nbsp;"; # placeholder
1341
1342 $rv['content'] .= "<div class='postTags' style='float : right'>
1343 <img src='images/tag.png'
1344 class='tagsPic' alt='Tags' title='Tags'>&nbsp;";
1345
1346 if (!$zoom_mode) {
1347 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
1348 <a title=\"".__('Edit tags for this article')."\"
1349 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
1350
1351 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
1352 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
1353 position=\"below\">$tags_str_full</div>";
1354
1355 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_BUTTON) as $p) {
1356 $rv['content'] .= $p->hook_article_button($line);
1357 }
1358
1359 } else {
1360 $tags_str = strip_tags($tags_str);
1361 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
1362 }
1363 $rv['content'] .= "</div>";
1364 $rv['content'] .= "<div clear='both'>";
1365
1366 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_ARTICLE_LEFT_BUTTON) as $p) {
1367 $rv['content'] .= $p->hook_article_left_button($line);
1368 }
1369
1370 $rv['content'] .= "$entry_comments</div>";
1371
1372 if ($line["orig_feed_id"]) {
1373
1374 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
1375 WHERE id = ".$line["orig_feed_id"]);
1376
1377 if (db_num_rows($tmp_result) != 0) {
1378
1379 $rv['content'] .= "<div clear='both'>";
1380 $rv['content'] .= __("Originally from:");
1381
1382 $rv['content'] .= "&nbsp;";
1383
1384 $tmp_line = db_fetch_assoc($tmp_result);
1385
1386 $rv['content'] .= "<a target='_blank'
1387 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
1388 $tmp_line['title'] . "</a>";
1389
1390 $rv['content'] .= "&nbsp;";
1391
1392 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
1393 $rv['content'] .= "<img title='".__('Feed URL')."' class='tinyFeedIcon' src='images/pub_set.png'></a>";
1394
1395 $rv['content'] .= "</div>";
1396 }
1397 }
1398
1399 $rv['content'] .= "</div>";
1400
1401 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
1402 if ($line['note']) {
1403 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
1404 }
1405 $rv['content'] .= "</div>";
1406
1407 if (!$line['lang']) $line['lang'] = 'en';
1408
1409 $rv['content'] .= "<div class=\"postContent\" lang=\"".$line['lang']."\">";
1410
1411 $rv['content'] .= $line["content"];
1412 $rv['content'] .= format_article_enclosures($id,
1413 sql_bool_to_bool($line["always_display_enclosures"]),
1414 $line["content"],
1415 sql_bool_to_bool($line["hide_images"]));
1416
1417 $rv['content'] .= "</div>";
1418
1419 $rv['content'] .= "</div>";
1420
1421 }
1422
1423 if ($zoom_mode) {
1424 $rv['content'] .= "
1425 <div class='footer'>
1426 <button onclick=\"return window.close()\">".
1427 __("Close this window")."</button></div>";
1428 $rv['content'] .= "</body></html>";
1429 }
1430
1431 return $rv;
1432
1433 }
1434
1435 function print_checkpoint($n, $s) {
1436 $ts = microtime(true);
1437 echo sprintf("<!-- CP[$n] %.4f seconds -->\n", $ts - $s);
1438 return $ts;
1439 }
1440
1441 function sanitize_tag($tag) {
1442 $tag = trim($tag);
1443
1444 $tag = mb_strtolower($tag, 'utf-8');
1445
1446 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
1447
1448// $tag = str_replace('"', "", $tag);
1449// $tag = str_replace("+", " ", $tag);
1450 $tag = str_replace("technorati tag: ", "", $tag);
1451
1452 return $tag;
1453 }
1454
1455 function get_self_url_prefix() {
1456 if (strrpos(SELF_URL_PATH, "/") === strlen(SELF_URL_PATH)-1) {
1457 return substr(SELF_URL_PATH, 0, strlen(SELF_URL_PATH)-1);
1458 } else {
1459 return SELF_URL_PATH;
1460 }
1461 }
1462
1463 /**
1464 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
1465 *
1466 * @return string The Mozilla Firefox feed adding URL.
1467 */
1468 function add_feed_url() {
1469 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
1470
1471 $url_path = get_self_url_prefix() .
1472 "/public.php?op=subscribe&feed_url=%s";
1473 return $url_path;
1474 } // function add_feed_url
1475
1476 function encrypt_password($pass, $salt = '', $mode2 = false) {
1477 if ($salt && $mode2) {
1478 return "MODE2:" . hash('sha256', $salt . $pass);
1479 } else if ($salt) {
1480 return "SHA1X:" . sha1("$salt:$pass");
1481 } else {
1482 return "SHA1:" . sha1($pass);
1483 }
1484 } // function encrypt_password
1485
1486 function load_filters($feed_id, $owner_uid, $action_id = false) {
1487 $filters = array();
1488
1489 $cat_id = (int)getFeedCategory($feed_id);
1490
1491 if ($cat_id == 0)
1492 $null_cat_qpart = "cat_id IS NULL OR";
1493 else
1494 $null_cat_qpart = "";
1495
1496 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
1497 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
1498
1499 $check_cats = join(",", array_merge(
1500 getParentCategories($cat_id, $owner_uid),
1501 array($cat_id)));
1502
1503 while ($line = db_fetch_assoc($result)) {
1504 $filter_id = $line["id"];
1505
1506 $result2 = db_query("SELECT
1507 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
1508 FROM ttrss_filters2_rules AS r,
1509 ttrss_filter_types AS t
1510 WHERE
1511 ($null_cat_qpart (cat_id IS NULL AND cat_filter = false) OR cat_id IN ($check_cats)) AND
1512 (feed_id IS NULL OR feed_id = '$feed_id') AND
1513 filter_type = t.id AND filter_id = '$filter_id'");
1514
1515 $rules = array();
1516 $actions = array();
1517
1518 while ($rule_line = db_fetch_assoc($result2)) {
1519# print_r($rule_line);
1520
1521 $rule = array();
1522 $rule["reg_exp"] = $rule_line["reg_exp"];
1523 $rule["type"] = $rule_line["type_name"];
1524 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
1525
1526 array_push($rules, $rule);
1527 }
1528
1529 $result2 = db_query("SELECT a.action_param,t.name AS type_name
1530 FROM ttrss_filters2_actions AS a,
1531 ttrss_filter_actions AS t
1532 WHERE
1533 action_id = t.id AND filter_id = '$filter_id'");
1534
1535 while ($action_line = db_fetch_assoc($result2)) {
1536# print_r($action_line);
1537
1538 $action = array();
1539 $action["type"] = $action_line["type_name"];
1540 $action["param"] = $action_line["action_param"];
1541
1542 array_push($actions, $action);
1543 }
1544
1545
1546 $filter = array();
1547 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
1548 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
1549 $filter["rules"] = $rules;
1550 $filter["actions"] = $actions;
1551
1552 if (count($rules) > 0 && count($actions) > 0) {
1553 array_push($filters, $filter);
1554 }
1555 }
1556
1557 return $filters;
1558 }
1559
1560 function get_score_pic($score) {
1561 if ($score > 100) {
1562 return "score_high.png";
1563 } else if ($score > 0) {
1564 return "score_half_high.png";
1565 } else if ($score < -100) {
1566 return "score_low.png";
1567 } else if ($score < 0) {
1568 return "score_half_low.png";
1569 } else {
1570 return "score_neutral.png";
1571 }
1572 }
1573
1574 function feed_has_icon($id) {
1575 return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0;
1576 }
1577
1578 function init_plugins() {
1579 PluginHost::getInstance()->load(PLUGINS, PluginHost::KIND_ALL);
1580
1581 return true;
1582 }
1583
1584 function format_tags_string($tags, $id) {
1585 if (!is_array($tags) || count($tags) == 0) {
1586 return __("no tags");
1587 } else {
1588 $maxtags = min(5, count($tags));
1589
1590 for ($i = 0; $i < $maxtags; $i++) {
1591 $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"viewfeed('".$tags[$i]."')\">" . $tags[$i] . "</a>, ";
1592 }
1593
1594 $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str)-2);
1595
1596 if (count($tags) > $maxtags)
1597 $tags_str .= ", &hellip;";
1598
1599 return $tags_str;
1600 }
1601 }
1602
1603 function format_article_labels($labels, $id) {
1604
1605 if (!is_array($labels)) return '';
1606
1607 $labels_str = "";
1608
1609 foreach ($labels as $l) {
1610 $labels_str .= sprintf("<span class='hlLabelRef'
1611 style='color : %s; background-color : %s'>%s</span>",
1612 $l[2], $l[3], $l[1]);
1613 }
1614
1615 return $labels_str;
1616
1617 }
1618
1619 function format_article_note($id, $note, $allow_edit = true) {
1620
1621 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
1622 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
1623 ($allow_edit ? __('(edit note)') : "")."</div>$note</div>";
1624
1625 return $str;
1626 }
1627
1628
1629 function get_feed_category($feed_cat, $parent_cat_id = false) {
1630 if ($parent_cat_id) {
1631 $parent_qpart = "parent_cat = '$parent_cat_id'";
1632 $parent_insert = "'$parent_cat_id'";
1633 } else {
1634 $parent_qpart = "parent_cat IS NULL";
1635 $parent_insert = "NULL";
1636 }
1637
1638 $result = db_query(
1639 "SELECT id FROM ttrss_feed_categories
1640 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
1641
1642 if (db_num_rows($result) == 0) {
1643 return false;
1644 } else {
1645 return db_fetch_result($result, 0, "id");
1646 }
1647 }
1648
1649 function add_feed_category($feed_cat, $parent_cat_id = false) {
1650
1651 if (!$feed_cat) return false;
1652
1653 db_query("BEGIN");
1654
1655 if ($parent_cat_id) {
1656 $parent_qpart = "parent_cat = '$parent_cat_id'";
1657 $parent_insert = "'$parent_cat_id'";
1658 } else {
1659 $parent_qpart = "parent_cat IS NULL";
1660 $parent_insert = "NULL";
1661 }
1662
1663 $feed_cat = mb_substr($feed_cat, 0, 250);
1664
1665 $result = db_query(
1666 "SELECT id FROM ttrss_feed_categories
1667 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
1668
1669 if (db_num_rows($result) == 0) {
1670
1671 $result = db_query(
1672 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
1673 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
1674
1675 db_query("COMMIT");
1676
1677 return true;
1678 }
1679
1680 return false;
1681 }
1682
1683 function getArticleFeed($id) {
1684 $result = db_query("SELECT feed_id FROM ttrss_user_entries
1685 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
1686
1687 if (db_num_rows($result) != 0) {
1688 return db_fetch_result($result, 0, "feed_id");
1689 } else {
1690 return 0;
1691 }
1692 }
1693
1694 /**
1695 * Fixes incomplete URLs by prepending "http://".
1696 * Also replaces feed:// with http://, and
1697 * prepends a trailing slash if the url is a domain name only.
1698 *
1699 * @param string $url Possibly incomplete URL
1700 *
1701 * @return string Fixed URL.
1702 */
1703 function fix_url($url) {
1704 if (strpos($url, '://') === false) {
1705 $url = 'http://' . $url;
1706 } else if (substr($url, 0, 5) == 'feed:') {
1707 $url = 'http:' . substr($url, 5);
1708 }
1709
1710 //prepend slash if the URL has no slash in it
1711 // "http://www.example" -> "http://www.example/"
1712 if (strpos($url, '/', strpos($url, ':') + 3) === false) {
1713 $url .= '/';
1714 }
1715
1716 if ($url != "http:///")
1717 return $url;
1718 else
1719 return '';
1720 }
1721
1722 function validate_feed_url($url) {
1723 $parts = parse_url($url);
1724
1725 return ($parts['scheme'] == 'http' || $parts['scheme'] == 'feed' || $parts['scheme'] == 'https');
1726
1727 }
1728
1729 function get_article_enclosures($id) {
1730
1731 $query = "SELECT * FROM ttrss_enclosures
1732 WHERE post_id = '$id' AND content_url != ''";
1733
1734 $rv = array();
1735
1736 $result = db_query($query);
1737
1738 if (db_num_rows($result) > 0) {
1739 while ($line = db_fetch_assoc($result)) {
1740 array_push($rv, $line);
1741 }
1742 }
1743
1744 return $rv;
1745 }
1746
3e0f2090 1747 /* function save_email_address($email) {
97b7d5c0
AD
1748 // FIXME: implement persistent storage of emails
1749
1750 if (!$_SESSION['stored_emails'])
1751 $_SESSION['stored_emails'] = array();
1752
1753 if (!in_array($email, $_SESSION['stored_emails']))
1754 array_push($_SESSION['stored_emails'], $email);
3e0f2090 1755 } */
97b7d5c0
AD
1756
1757
1758 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
1759
1760 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1761
1762 $sql_is_cat = bool_to_sql_bool($is_cat);
1763
1764 $result = db_query("SELECT access_key FROM ttrss_access_keys
1765 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
1766 AND owner_uid = " . $owner_uid);
1767
1768 if (db_num_rows($result) == 1) {
1769 return db_fetch_result($result, 0, "access_key");
1770 } else {
1771 $key = db_escape_string(uniqid(base_convert(rand(), 10, 36)));
1772
1773 $result = db_query("INSERT INTO ttrss_access_keys
1774 (access_key, feed_id, is_cat, owner_uid)
1775 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
1776
1777 return $key;
1778 }
1779 return false;
1780 }
1781
1782 function get_feeds_from_html($url, $content)
1783 {
1784 $url = fix_url($url);
1785 $baseUrl = substr($url, 0, strrpos($url, '/') + 1);
1786
1787 libxml_use_internal_errors(true);
1788
1789 $doc = new DOMDocument();
1790 $doc->loadHTML($content);
1791 $xpath = new DOMXPath($doc);
1792 $entries = $xpath->query('/html/head/link[@rel="alternate" and '.
16c48032 1793 '(contains(@type,"rss") or contains(@type,"atom"))]|/html/head/link[@rel="feed"]');
97b7d5c0
AD
1794 $feedUrls = array();
1795 foreach ($entries as $entry) {
1796 if ($entry->hasAttribute('href')) {
1797 $title = $entry->getAttribute('title');
1798 if ($title == '') {
1799 $title = $entry->getAttribute('type');
1800 }
1801 $feedUrl = rewrite_relative_url(
1802 $baseUrl, $entry->getAttribute('href')
1803 );
1804 $feedUrls[$feedUrl] = $title;
1805 }
1806 }
1807 return $feedUrls;
1808 }
1809
1810 function is_html($content) {
1811 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
1812 }
1813
1814 function url_is_html($url, $login = false, $pass = false) {
1815 return is_html(fetch_file_contents($url, false, $login, $pass));
1816 }
1817
1818 function print_label_select($name, $value, $attributes = "") {
1819
1820 $result = db_query("SELECT caption FROM ttrss_labels2
1821 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
1822
1823 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
1824 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
1825
1826 while ($line = db_fetch_assoc($result)) {
1827
1828 $issel = ($line["caption"] == $value) ? "selected=\"1\"" : "";
1829
1830 print "<option value=\"".htmlspecialchars($line["caption"])."\"
1831 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
1832
1833 }
1834
1835# print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
1836
1837 print "</select>";
1838
1839
1840 }
1841
1842 function format_article_enclosures($id, $always_display_enclosures,
1843 $article_content, $hide_images = false) {
1844
1845 $result = get_article_enclosures($id);
1846 $rv = '';
1847
2bb11658
DZ
1848 foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_FORMAT_ENCLOSURES) as $plugin) {
1849 $retval = $plugin->hook_format_enclosures($rv, $result, $id, $always_display_enclosures, $article_content, $hide_images);
1850 if (is_array($retval)) {
1851 $rv = $retval[0];
1852 $result = $retval[1];
1853 } else {
1854 $rv = $retval;
1855 }
1856 }
97b7d5c0 1857
c6ce584d 1858 if ($rv === '' && !empty($result)) {
97b7d5c0
AD
1859 $entries_html = array();
1860 $entries = array();
1861 $entries_inline = array();
1862
1863 foreach ($result as $line) {
1864
1865 $url = $line["content_url"];
1866 $ctype = $line["content_type"];
1867 $title = $line["title"];
1e871938
FE
1868 $width = $line["width"];
1869 $height = $line["height"];
97b7d5c0
AD
1870
1871 if (!$ctype) $ctype = __("unknown type");
1872
1873 $filename = substr($url, strrpos($url, "/")+1);
1874
1875 $player = format_inline_player($url, $ctype);
1876
1877 if ($player) array_push($entries_inline, $player);
1878
1879# $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
1880# $filename . " (" . $ctype . ")" . "</a>";
1881
1882 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
1883 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
1884
1885 array_push($entries_html, $entry);
1886
1887 $entry = array();
1888
1889 $entry["type"] = $ctype;
1890 $entry["filename"] = $filename;
1891 $entry["url"] = $url;
1892 $entry["title"] = $title;
1e871938
FE
1893 $entry["width"] = $width;
1894 $entry["height"] = $height;
97b7d5c0
AD
1895
1896 array_push($entries, $entry);
1897 }
1898
1899 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
1900 if ($always_display_enclosures ||
1901 !preg_match("/<img/i", $article_content)) {
1902
1903 foreach ($entries as $entry) {
1904
1905 if (preg_match("/image/", $entry["type"]) ||
1906 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
1907
1908 if (!$hide_images) {
1e871938
FE
1909 $encsize = '';
1910 if ($entry['height'] > 0)
1911 $encsize .= ' height="' . intval($entry['width']) . '"';
1912 if ($entry['width'] > 0)
1913 $encsize .= ' width="' . intval($entry['height']) . '"';
97b7d5c0
AD
1914 $rv .= "<p><img
1915 alt=\"".htmlspecialchars($entry["filename"])."\"
1e871938
FE
1916 src=\"" .htmlspecialchars($entry["url"]) . "\"
1917 " . $encsize . " /></p>";
97b7d5c0
AD
1918 } else {
1919 $rv .= "<p><a target=\"_blank\"
1920 href=\"".htmlspecialchars($entry["url"])."\"
1921 >" .htmlspecialchars($entry["url"]) . "</a></p>";
1922 }
1923
1924 if ($entry['title']) {
1925 $rv.= "<div class=\"enclosure_title\">${entry['title']}</div>";
1926 }
1927 }
1928 }
1929 }
1930 }
1931
1932 if (count($entries_inline) > 0) {
1933 $rv .= "<hr clear='both'/>";
1934 foreach ($entries_inline as $entry) { $rv .= $entry; };
1935 $rv .= "<hr clear='both'/>";
1936 }
1937
1938 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
1939 "<option value=''>" . __('Attachments')."</option>";
1940
1941 foreach ($entries as $entry) {
1942 if ($entry["title"])
1943 $title = "&mdash; " . truncate_string($entry["title"], 30);
1944 else
1945 $title = "";
1946
1947 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "$title</option>";
1948
1949 };
1950
1951 $rv .= "</select>";
1952 }
1953
1954 return $rv;
1955 }
1956
1957 function getLastArticleId() {
8458a312 1958 $result = db_query("SELECT ref_id AS id FROM ttrss_user_entries
1959 WHERE owner_uid = " . $_SESSION["uid"] . " ORDER BY ref_id DESC LIMIT 1");
97b7d5c0
AD
1960
1961 if (db_num_rows($result) == 1) {
1962 return db_fetch_result($result, 0, "id");
1963 } else {
1964 return -1;
1965 }
1966 }
1967
1968 function build_url($parts) {
1969 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
1970 }
1971
1972 /**
1973 * Converts a (possibly) relative URL to a absolute one.
1974 *
1975 * @param string $url Base URL (i.e. from where the document is)
1976 * @param string $rel_url Possibly relative URL in the document
1977 *
1978 * @return string Absolute URL
1979 */
1980 function rewrite_relative_url($url, $rel_url) {
1981 if (strpos($rel_url, ":") !== false) {
1982 return $rel_url;
1983 } else if (strpos($rel_url, "://") !== false) {
1984 return $rel_url;
1985 } else if (strpos($rel_url, "//") === 0) {
1986 # protocol-relative URL (rare but they exist)
1987 return $rel_url;
1988 } else if (strpos($rel_url, "/") === 0)
1989 {
1990 $parts = parse_url($url);
1991 $parts['path'] = $rel_url;
1992
1993 return build_url($parts);
1994
1995 } else {
1996 $parts = parse_url($url);
1997 if (!isset($parts['path'])) {
1998 $parts['path'] = '/';
1999 }
2000 $dir = $parts['path'];
2001 if (substr($dir, -1) !== '/') {
2002 $dir = dirname($parts['path']);
2003 $dir !== '/' && $dir .= '/';
2004 }
2005 $parts['path'] = $dir . $rel_url;
2006
2007 return build_url($parts);
2008 }
2009 }
2010
97b7d5c0
AD
2011 function cleanup_tags($days = 14, $limit = 1000) {
2012
2013 if (DB_TYPE == "pgsql") {
2014 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
2015 } else if (DB_TYPE == "mysql") {
2016 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
2017 }
2018
2019 $tags_deleted = 0;
2020
2021 while ($limit > 0) {
2022 $limit_part = 500;
2023
2024 $query = "SELECT ttrss_tags.id AS id
2025 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
2026 WHERE post_int_id = int_id AND $interval_query AND
2027 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
2028
2029 $result = db_query($query);
2030
2031 $ids = array();
2032
2033 while ($line = db_fetch_assoc($result)) {
2034 array_push($ids, $line['id']);
2035 }
2036
2037 if (count($ids) > 0) {
2038 $ids = join(",", $ids);
2039
2040 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
2041 $tags_deleted += db_affected_rows($tmp_result);
2042 } else {
2043 break;
2044 }
2045
2046 $limit -= $limit_part;
2047 }
2048
2049 return $tags_deleted;
2050 }
2051
2052 function print_user_stylesheet() {
2053 $value = get_pref('USER_STYLESHEET');
2054
2055 if ($value) {
2056 print "<style type=\"text/css\">";
2057 print str_replace("<br/>", "\n", $value);
2058 print "</style>";
2059 }
2060
2061 }
2062
2063 function filter_to_sql($filter, $owner_uid) {
2064 $query = array();
2065
2066 if (DB_TYPE == "pgsql")
2067 $reg_qpart = "~";
2068 else
2069 $reg_qpart = "REGEXP";
2070
2071 foreach ($filter["rules"] AS $rule) {
2072 $rule['reg_exp'] = str_replace('/', '\/', $rule["reg_exp"]);
2073 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
2074 $rule['reg_exp']) !== FALSE;
2075
2076 if ($regexp_valid) {
2077
2078 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
2079
2080 switch ($rule["type"]) {
2081 case "title":
2082 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
2083 $rule['reg_exp'] . "')";
2084 break;
2085 case "content":
2086 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
2087 $rule['reg_exp'] . "')";
2088 break;
2089 case "both":
2090 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
2091 $rule['reg_exp'] . "') OR LOWER(" .
2092 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
2093 break;
2094 case "tag":
2095 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
2096 $rule['reg_exp'] . "')";
2097 break;
2098 case "link":
2099 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
2100 $rule['reg_exp'] . "')";
2101 break;
2102 case "author":
2103 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
2104 $rule['reg_exp'] . "')";
2105 break;
2106 }
2107
2108 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
2109
2110 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
2111 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
2112 }
2113
2114 if (isset($rule["cat_id"])) {
2115
2116 if ($rule["cat_id"] > 0) {
2117 $children = getChildCategories($rule["cat_id"], $owner_uid);
2118 array_push($children, $rule["cat_id"]);
2119
2120 $children = join(",", $children);
2121
2122 $cat_qpart = "cat_id IN ($children)";
2123 } else {
2124 $cat_qpart = "cat_id IS NULL";
2125 }
2126
2127 $qpart .= " AND $cat_qpart";
2128 }
2129
2130 $qpart .= " AND feed_id IS NOT NULL";
2131
2132 array_push($query, "($qpart)");
2133
2134 }
2135 }
2136
2137 if (count($query) > 0) {
2138 $fullquery = "(" . join($filter["match_any_rule"] ? "OR" : "AND", $query) . ")";
2139 } else {
2140 $fullquery = "(false)";
2141 }
2142
2143 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
2144
2145 return $fullquery;
2146 }
2147
2148 if (!function_exists('gzdecode')) {
2149 function gzdecode($string) { // no support for 2nd argument
2150 return file_get_contents('compress.zlib://data:who/cares;base64,'.
2151 base64_encode($string));
2152 }
2153 }
2154
2155 function get_random_bytes($length) {
2156 if (function_exists('openssl_random_pseudo_bytes')) {
2157 return openssl_random_pseudo_bytes($length);
2158 } else {
2159 $output = "";
2160
2161 for ($i = 0; $i < $length; $i++)
2162 $output .= chr(mt_rand(0, 255));
2163
2164 return $output;
2165 }
2166 }
2167
2168 function read_stdin() {
2169 $fp = fopen("php://stdin", "r");
2170
2171 if ($fp) {
2172 $line = trim(fgets($fp));
2173 fclose($fp);
2174 return $line;
2175 }
2176
2177 return null;
2178 }
2179
2180 function tmpdirname($path, $prefix) {
2181 // Use PHP's tmpfile function to create a temporary
2182 // directory name. Delete the file and keep the name.
2183 $tempname = tempnam($path,$prefix);
2184 if (!$tempname)
2185 return false;
2186
2187 if (!unlink($tempname))
2188 return false;
2189
2190 return $tempname;
2191 }
2192
2193 function getFeedCategory($feed) {
2194 $result = db_query("SELECT cat_id FROM ttrss_feeds
2195 WHERE id = '$feed'");
2196
2197 if (db_num_rows($result) > 0) {
2198 return db_fetch_result($result, 0, "cat_id");
2199 } else {
2200 return false;
2201 }
2202
2203 }
2204
2205 function implements_interface($class, $interface) {
2206 return in_array($interface, class_implements($class));
2207 }
2208
fafac207 2209 function geturl($url, $depth = 0, $nobody = true){
97b7d5c0
AD
2210
2211 if ($depth == 20) return $url;
2212
2213 if (!function_exists('curl_init'))
2214 return user_error('CURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini', E_USER_ERROR);
2215
2216 $curl = curl_init();
2217 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
2218 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
2219 $header[] = "Cache-Control: max-age=0";
2220 $header[] = "Connection: keep-alive";
2221 $header[] = "Keep-Alive: 300";
2222 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
2223 $header[] = "Accept-Language: en-us,en;q=0.5";
2224 $header[] = "Pragma: ";
2225
2226 curl_setopt($curl, CURLOPT_URL, $url);
2227 curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
2228 curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
2229 curl_setopt($curl, CURLOPT_HEADER, true);
fafac207 2230 curl_setopt($curl, CURLOPT_NOBODY, $nobody);
97b7d5c0
AD
2231 curl_setopt($curl, CURLOPT_REFERER, $url);
2232 curl_setopt($curl, CURLOPT_ENCODING, 'gzip,deflate');
2233 curl_setopt($curl, CURLOPT_AUTOREFERER, true);
2234 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
2235 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
2236 curl_setopt($curl, CURLOPT_TIMEOUT, 60);
2237 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
2238
2239 if (defined('_CURL_HTTP_PROXY')) {
2240 curl_setopt($curl, CURLOPT_PROXY, _CURL_HTTP_PROXY);
2241 }
2242
97b7d5c0
AD
2243 $html = curl_exec($curl);
2244
2245 $status = curl_getinfo($curl);
2246
2247 if($status['http_code']!=200){
fafac207 2248
ae962a96 2249 // idiot site not allowing http head
fafac207
AD
2250 if($status['http_code'] == 405) {
2251 curl_close($curl);
2252 return geturl($url, $depth +1, false);
2253 }
2254
97b7d5c0
AD
2255 if($status['http_code'] == 301 || $status['http_code'] == 302) {
2256 curl_close($curl);
2257 list($header) = explode("\r\n\r\n", $html, 2);
2258 $matches = array();
2259 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
2260 $url = trim(str_replace($matches[1],"",$matches[0]));
2261 $url_parsed = parse_url($url);
2262 return (isset($url_parsed))? geturl($url, $depth + 1):'';
2263 }
2264
2265 global $fetch_last_error;
2266
2267 $fetch_last_error = curl_errno($curl) . " " . curl_error($curl);
2268 curl_close($curl);
2269
2270# $oline='';
2271# foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
2272# $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
2273# $handle = @fopen('./curl.error.log', 'a');
2274# fwrite($handle, $line);
2275 return FALSE;
2276 }
2277 curl_close($curl);
2278 return $url;
2279 }
2280
2281 function get_minified_js($files) {
2282 require_once 'lib/jshrink/Minifier.php';
2283
2284 $rv = '';
2285
2286 foreach ($files as $js) {
2287 if (!isset($_GET['debug'])) {
2288 $cached_file = CACHE_DIR . "/js/".basename($js).".js";
2289
aa9f7d44 2290 if (file_exists($cached_file) && is_readable($cached_file) && filemtime($cached_file) >= filemtime("js/$js.js")) {
97b7d5c0 2291
aa9f7d44 2292 list($header, $contents) = explode("\n", file_get_contents($cached_file), 2);
97b7d5c0 2293
aa9f7d44
AD
2294 if ($header && $contents) {
2295 list($htag, $hversion) = explode(":", $header);
2296
2297 if ($htag == "tt-rss" && $hversion == VERSION) {
2298 $rv .= $contents;
2299 continue;
2300 }
2301 }
97b7d5c0 2302 }
aa9f7d44
AD
2303
2304 $minified = JShrink\Minifier::minify(file_get_contents("js/$js.js"));
2305 file_put_contents($cached_file, "tt-rss:" . VERSION . "\n" . $minified);
2306 $rv .= $minified;
2307
97b7d5c0 2308 } else {
aa9f7d44 2309 $rv .= file_get_contents("js/$js.js"); // no cache in debug mode
97b7d5c0
AD
2310 }
2311 }
2312
2313 return $rv;
2314 }
2315
2316 function stylesheet_tag($filename) {
2317 $timestamp = filemtime($filename);
2318
2319 return "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
2320 }
2321
2322 function javascript_tag($filename) {
2323 $query = "";
2324
2325 if (!(strpos($filename, "?") === FALSE)) {
2326 $query = substr($filename, strpos($filename, "?")+1);
2327 $filename = substr($filename, 0, strpos($filename, "?"));
2328 }
2329
2330 $timestamp = filemtime($filename);
2331
2332 if ($query) $timestamp .= "&$query";
2333
2334 return "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
2335 }
2336
2337 function calculate_dep_timestamp() {
2338 $files = array_merge(glob("js/*.js"), glob("css/*.css"));
2339
2340 $max_ts = -1;
2341
2342 foreach ($files as $file) {
2343 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
2344 }
2345
2346 return $max_ts;
2347 }
2348
2349 function T_js_decl($s1, $s2) {
2350 if ($s1 && $s2) {
2351 $s1 = preg_replace("/\n/", "", $s1);
2352 $s2 = preg_replace("/\n/", "", $s2);
2353
2354 $s1 = preg_replace("/\"/", "\\\"", $s1);
2355 $s2 = preg_replace("/\"/", "\\\"", $s2);
2356
2357 return "T_messages[\"$s1\"] = \"$s2\";\n";
2358 }
2359 }
2360
2361 function init_js_translations() {
2362
2363 print 'var T_messages = new Object();
2364
2365 function __(msg) {
2366 if (T_messages[msg]) {
2367 return T_messages[msg];
2368 } else {
2369 return msg;
2370 }
2371 }
2372
2373 function ngettext(msg1, msg2, n) {
2374 return __((parseInt(n) > 1) ? msg2 : msg1);
2375 }';
2376
2377 $l10n = _get_reader();
2378
2379 for ($i = 0; $i < $l10n->total; $i++) {
2380 $orig = $l10n->get_original_string($i);
2381 if(strpos($orig, "\000") !== FALSE) { // Plural forms
2382 $key = explode(chr(0), $orig);
2383 print T_js_decl($key[0], _ngettext($key[0], $key[1], 1)); // Singular
2384 print T_js_decl($key[1], _ngettext($key[0], $key[1], 2)); // Plural
2385 } else {
2386 $translation = __($orig);
2387 print T_js_decl($orig, $translation);
2388 }
2389 }
2390 }
2391
2392 function label_to_feed_id($label) {
2393 return LABEL_BASE_INDEX - 1 - abs($label);
2394 }
2395
2396 function feed_to_label_id($feed) {
2397 return LABEL_BASE_INDEX - 1 + abs($feed);
2398 }
2399
f6cbe9a5 2400 function theme_valid($file) {
21d7585a 2401 if ($file == "default.css" || $file == "night.css") return true; // needed for array_filter
f6cbe9a5
AD
2402 $file = "themes/" . basename($file);
2403
2404 if (file_exists($file) && is_readable($file)) {
2405 $fh = fopen($file, "r");
2406
2407 if ($fh) {
2408 $header = fgets($fh);
2409 fclose($fh);
2410
2411 return strpos($header, "supports-version:" . VERSION_STATIC) !== FALSE;
2412 }
2413 }
2414
2415 return false;
2416 }
97b7d5c0 2417?>