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