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