2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 109);
5 define('LABEL_BASE_INDEX', -1024);
7 $fetch_last_error = false;
10 function __autoload($class) {
11 $class_file = str_replace("_", "/", strtolower(basename($class)));
13 $file = dirname(__FILE__
)."/../classes/$class_file.php";
15 if (file_exists($file)) {
21 mb_internal_encoding("UTF-8");
22 date_default_timezone_set('UTC');
23 if (defined('E_DEPRECATED')) {
24 error_reporting(E_ALL
& ~E_NOTICE
& ~E_DEPRECATED
);
26 error_reporting(E_ALL
& ~E_NOTICE
);
29 require_once 'config.php';
31 if (DB_TYPE
== "pgsql") {
32 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
34 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
37 define('THEME_VERSION_REQUIRED', 1.1);
40 * Return available translations names.
43 * @return array A array of available translations.
45 function get_translations() {
47 "auto" => "Detect automatically",
53 "fr_FR" => "Français",
54 "hu_HU" => "Magyar (Hungarian)",
55 "it_IT" => "Italiano",
56 "ja_JP" => "日本語 (Japanese)",
57 "lv_LV" => "Latviešu",
58 "nb_NO" => "Norwegian bokmål",
62 "pt_BR" => "Portuguese/Brazil",
63 "zh_CN" => "Simplified Chinese");
68 require_once "lib/accept-to-gettext.php";
69 require_once "lib/gettext/gettext.inc";
72 function startup_gettext() {
74 # Get locale from Accept-Language header
75 $lang = al2gt(array_keys(get_translations()), "text/html");
77 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
78 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
81 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
82 $lang = $_SESSION["language"];
86 if (defined('LC_MESSAGES')) {
87 _setlocale(LC_MESSAGES
, $lang);
88 } else if (defined('LC_ALL')) {
89 _setlocale(LC_ALL
, $lang);
92 _bindtextdomain("messages", "locale");
94 _textdomain("messages");
95 _bind_textdomain_codeset("messages", "UTF-8");
101 require_once 'db-prefs.php';
102 require_once 'version.php';
103 require_once 'ccache.php';
104 require_once 'labels.php';
106 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
107 ini_set('user_agent', SELF_USER_AGENT
);
109 require_once 'lib/pubsubhubbub/publisher.php';
112 $utc_tz = new DateTimeZone('UTC');
113 $schema_version = false;
116 * Print a timestamped debug message.
118 * @param string $msg The debug message.
121 function _debug($msg) {
122 $ts = strftime("%H:%M:%S", time());
123 if (function_exists('posix_getpid')) {
124 $ts = "$ts/" . posix_getpid();
127 if (!(defined('QUIET') && QUIET
)) {
128 print "[$ts] $msg\n";
131 if (defined('LOGFILE')) {
132 $fp = fopen(LOGFILE
, 'a+');
135 fputs($fp, "[$ts] $msg\n");
143 * Purge a feed old posts.
145 * @param mixed $link A database connection.
146 * @param mixed $feed_id The id of the purged feed.
147 * @param mixed $purge_interval Olderness of purged posts.
148 * @param boolean $debug Set to True to enable the debug. False by default.
152 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
154 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
158 $result = db_query($link,
159 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
163 if (db_num_rows($result) == 1) {
164 $owner_uid = db_fetch_result($result, 0, "owner_uid");
167 if ($purge_interval == -1 ||
!$purge_interval) {
169 ccache_update($link, $feed_id, $owner_uid);
174 if (!$owner_uid) return;
176 if (FORCE_ARTICLE_PURGE
== 0) {
177 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
180 $purge_unread = true;
181 $purge_interval = FORCE_ARTICLE_PURGE
;
184 if (!$purge_unread) $query_limit = " unread = false AND ";
186 if (DB_TYPE
== "pgsql") {
187 $pg_version = get_pgsql_version($link);
189 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
191 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
192 ttrss_entries.id = ref_id AND
194 feed_id = '$feed_id' AND
196 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
200 $result = db_query($link, "DELETE FROM ttrss_user_entries
202 WHERE ttrss_entries.id = ref_id AND
204 feed_id = '$feed_id' AND
206 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
209 $rows = pg_affected_rows($result);
213 /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
214 marked = false AND feed_id = '$feed_id' AND
215 (SELECT date_updated FROM ttrss_entries WHERE
216 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
218 $result = db_query($link, "DELETE FROM ttrss_user_entries
219 USING ttrss_user_entries, ttrss_entries
220 WHERE ttrss_entries.id = ref_id AND
222 feed_id = '$feed_id' AND
224 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
226 $rows = mysql_affected_rows($link);
230 ccache_update($link, $feed_id, $owner_uid);
233 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
237 } // function purge_feed
239 function feed_purge_interval($link, $feed_id) {
241 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
242 WHERE id = '$feed_id'");
244 if (db_num_rows($result) == 1) {
245 $purge_interval = db_fetch_result($result, 0, "purge_interval");
246 $owner_uid = db_fetch_result($result, 0, "owner_uid");
248 if ($purge_interval == 0) $purge_interval = get_pref($link,
249 'PURGE_OLD_DAYS', $owner_uid);
251 return $purge_interval;
258 function purge_orphans($link, $do_output = false) {
260 // purge orphaned posts in main content table
261 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
262 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
265 $rows = db_affected_rows($link, $result);
266 _debug("Purged $rows orphaned posts.");
270 function get_feed_update_interval($link, $feed_id) {
271 $result = db_query($link, "SELECT owner_uid, update_interval FROM
272 ttrss_feeds WHERE id = '$feed_id'");
274 if (db_num_rows($result) == 1) {
275 $update_interval = db_fetch_result($result, 0, "update_interval");
276 $owner_uid = db_fetch_result($result, 0, "owner_uid");
278 if ($update_interval != 0) {
279 return $update_interval;
281 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
289 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false) {
291 global $fetch_last_error;
293 if (function_exists('curl_init') && !ini_get("open_basedir")) {
295 if (ini_get("safe_mode")) {
296 $ch = curl_init(geturl($url));
298 $ch = curl_init($url);
301 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : 15);
302 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : 45);
303 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode"));
304 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
305 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
306 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
307 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
308 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
309 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
310 curl_setopt($ch, CURLOPT_ENCODING
, "gzip");
311 curl_setopt($ch, CURLOPT_REFERER
, $url);
314 curl_setopt($ch, CURLOPT_POST
, true);
315 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
319 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
321 $contents = @curl_exec
($ch);
323 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
324 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
325 $contents = @curl_exec
($ch);
328 if ($contents === false) {
329 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
334 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
335 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
337 if ($http_code != 200 ||
$type && strpos($content_type, "$type") === false) {
338 if (curl_errno($ch) != 0) {
339 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
341 $fetch_last_error = "HTTP Code: $http_code";
351 if ($login && $pass){
352 $url_parts = array();
354 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
356 $pass = urlencode($pass);
358 if ($url_parts[1] && $url_parts[2]) {
359 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
363 $data = @file_get_contents
($url);
365 @$gzdecoded = gzdecode($data);
366 if ($gzdecoded) $data = $gzdecoded;
368 if (!$data && function_exists('error_get_last')) {
369 $error = error_get_last();
370 $fetch_last_error = $error["message"];
378 * Try to determine the favicon URL for a feed.
379 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
380 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
382 * @param string $url A feed or page URL
384 * @return mixed The favicon URL, or false if none was found.
386 function get_favicon_url($url) {
388 $favicon_url = false;
390 if ($html = @fetch_file_contents
($url)) {
392 libxml_use_internal_errors(true);
394 $doc = new DOMDocument();
395 $doc->loadHTML($html);
396 $xpath = new DOMXPath($doc);
398 $base = $xpath->query('/html/head/base');
399 foreach ($base as $b) {
400 $url = $b->getAttribute("href");
404 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
405 if (count($entries) > 0) {
406 foreach ($entries as $entry) {
407 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
414 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
417 } // function get_favicon_url
419 function check_feed_favicon($site_url, $feed, $link) {
420 # print "FAVICON [$site_url]: $favicon_url\n";
422 $icon_file = ICONS_DIR
. "/$feed.ico";
424 if (!file_exists($icon_file)) {
425 $favicon_url = get_favicon_url($site_url);
428 // Limiting to "image" type misses those served with text/plain
429 $contents = fetch_file_contents($favicon_url); // , "image");
432 // Crude image type matching.
433 // Patterns gleaned from the file(1) source code.
434 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
435 // 0 string \000\000\001\000 MS Windows icon resource
436 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
438 elseif (preg_match('/^GIF8/', $contents)) {
439 // 0 string GIF8 GIF image data
440 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
442 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
443 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
444 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
446 elseif (preg_match('/^\xff\xd8/', $contents)) {
447 // 0 beshort 0xffd8 JPEG image data
448 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
451 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
457 $fp = @fopen
($icon_file, "w");
460 fwrite($fp, $contents);
462 chmod($icon_file, 0644);
469 function print_select($id, $default, $values, $attributes = "") {
470 print "<select name=\"$id\" id=\"$id\" $attributes>";
471 foreach ($values as $v) {
473 $sel = "selected=\"1\"";
479 print "<option value=\"$v\" $sel>$v</option>";
484 function print_select_hash($id, $default, $values, $attributes = "") {
485 print "<select name=\"$id\" id='$id' $attributes>";
486 foreach (array_keys($values) as $v) {
488 $sel = 'selected="selected"';
494 print "<option $sel value=\"$v\">".$values[$v]."</option>";
500 function print_radio($id, $default, $true_is, $values, $attributes = "") {
501 foreach ($values as $v) {
508 if ($v == $true_is) {
509 $sel .= " value=\"1\"";
511 $sel .= " value=\"0\"";
514 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
515 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
520 function initialize_user_prefs($link, $uid, $profile = false) {
522 $uid = db_escape_string($link, $uid);
526 $profile_qpart = "AND profile IS NULL";
528 $profile_qpart = "AND profile = '$profile'";
531 if (get_schema_version($link) < 63) $profile_qpart = "";
533 db_query($link, "BEGIN");
535 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
537 $u_result = db_query($link, "SELECT pref_name
538 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
540 $active_prefs = array();
542 while ($line = db_fetch_assoc($u_result)) {
543 array_push($active_prefs, $line["pref_name"]);
546 while ($line = db_fetch_assoc($result)) {
547 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
548 // print "adding " . $line["pref_name"] . "<br>";
550 if (get_schema_version($link) < 63) {
551 db_query($link, "INSERT INTO ttrss_user_prefs
552 (owner_uid,pref_name,value) VALUES
553 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
556 db_query($link, "INSERT INTO ttrss_user_prefs
557 (owner_uid,pref_name,value, profile) VALUES
558 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
564 db_query($link, "COMMIT");
568 function get_ssl_certificate_id() {
569 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
570 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
571 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
572 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
573 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
578 function authenticate_user($link, $login, $password, $check_only = false) {
580 if (!SINGLE_USER_MODE
) {
585 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
587 $user_id = (int) $plugin->authenticate($login, $password);
590 $_SESSION["auth_module"] = strtolower(get_class($plugin));
595 if ($user_id && !$check_only) {
596 $_SESSION["uid"] = $user_id;
598 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
599 WHERE id = '$user_id'");
601 $_SESSION["name"] = db_fetch_result($result, 0, "login");
602 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
603 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
605 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
608 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
609 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
611 $_SESSION["last_version_check"] = time();
613 initialize_user_prefs($link, $_SESSION["uid"]);
622 $_SESSION["uid"] = 1;
623 $_SESSION["name"] = "admin";
624 $_SESSION["access_level"] = 10;
626 $_SESSION["hide_hello"] = true;
627 $_SESSION["hide_logout"] = true;
629 $_SESSION["auth_module"] = false;
631 if (!$_SESSION["csrf_token"]) {
632 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
635 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
637 initialize_user_prefs($link, $_SESSION["uid"]);
643 function make_password($length = 8) {
646 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
650 while ($i < $length) {
651 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
653 if (!strstr($password, $char)) {
661 // this is called after user is created to initialize default feeds, labels
664 // user preferences are checked on every login, not here
666 function initialize_user($link, $uid) {
668 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
669 values ('$uid', 'Tiny Tiny RSS: New Releases',
670 'http://tt-rss.org/releases.rss')");
672 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
673 values ('$uid', 'Tiny Tiny RSS: Forum',
674 'http://tt-rss.org/forum/rss.php')");
677 function logout_user() {
679 if (isset($_COOKIE[session_name()])) {
680 setcookie(session_name(), '', time()-42000, '/');
684 function validate_csrf($csrf_token) {
685 return $csrf_token == $_SESSION['csrf_token'];
688 function validate_session($link) {
689 if (SINGLE_USER_MODE
) return true;
691 $check_ip = $_SESSION['ip_address'];
693 switch (SESSION_CHECK_ADDRESS
) {
698 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
701 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.'));
702 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
706 if ($check_ip && strpos($_SERVER['REMOTE_ADDR'], $check_ip) !== 0) {
707 $_SESSION["login_error_msg"] =
708 __("Session failed to validate (incorrect IP)");
712 if ($_SESSION["ref_schema_version"] != get_schema_version($link, true))
715 if ($_SESSION["uid"]) {
717 $result = db_query($link,
718 "SELECT pwd_hash FROM ttrss_users WHERE id = '".$_SESSION["uid"]."'");
720 $pwd_hash = db_fetch_result($result, 0, "pwd_hash");
722 if ($pwd_hash != $_SESSION["pwd_hash"]) {
727 /* if ($_SESSION["cookie_lifetime"] && $_SESSION["uid"]) {
729 //print_r($_SESSION);
731 if (time() > $_SESSION["cookie_lifetime"]) {
739 function load_user_plugins($link, $owner_uid) {
741 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
744 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
746 if (get_schema_version($link) > 100) {
747 $pluginhost->load_data();
752 function login_sequence($link) {
753 $_SESSION["prefs_cache"] = false;
755 if (SINGLE_USER_MODE
) {
756 authenticate_user($link, "admin", null);
758 load_user_plugins($link, $_SESSION["uid"]);
760 if (!$_SESSION["uid"] ||
!validate_session($link)) {
762 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
763 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
765 authenticate_user($link, null, null, true);
768 if (!$_SESSION["uid"]) render_login_form($link);
771 /* bump login timestamp */
772 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
774 $_SESSION["last_login_update"] = time();
777 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
778 setcookie("ttrss_lang", $_SESSION["language"],
779 time() + SESSION_COOKIE_LIFETIME
);
782 if ($_SESSION["uid"]) {
784 load_user_plugins($link, $_SESSION["uid"]);
788 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
789 $_SESSION["uid"] . " AND
790 (SELECT COUNT(id) FROM ttrss_feeds WHERE
791 ttrss_feeds.id = feed_id) = 0");
793 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
794 $_SESSION["uid"] . " AND
795 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
796 ttrss_feed_categories.id = feed_id) = 0");
803 function truncate_string($str, $max_len, $suffix = '…') {
804 if (mb_strlen($str, "utf-8") > $max_len - 3) {
805 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
811 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
814 $source_tz = new DateTimeZone($source_tz);
815 } catch (Exception
$e) {
816 $source_tz = new DateTimeZone('UTC');
820 $dest_tz = new DateTimeZone($dest_tz);
821 } catch (Exception
$e) {
822 $dest_tz = new DateTimeZone('UTC');
825 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
826 return $dt->format('U') +
$dest_tz->getOffset($dt);
829 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
830 $no_smart_dt = false) {
832 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
833 if (!$timestamp) $timestamp = '1970-01-01 0:00';
838 # We store date in UTC internally
839 $dt = new DateTime($timestamp, $utc_tz);
841 if ($tz_offset == -1) {
843 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
846 $user_tz = new DateTimeZone($user_tz_string);
847 } catch (Exception
$e) {
851 $tz_offset = $user_tz->getOffset($dt);
854 $user_timestamp = $dt->format('U') +
$tz_offset;
857 return smart_date_time($link, $user_timestamp,
858 $tz_offset, $owner_uid);
861 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
863 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
865 return date($format, $user_timestamp);
869 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
870 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
872 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
873 return date("G:i", $timestamp);
874 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
875 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
876 return date($format, $timestamp);
878 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
879 return date($format, $timestamp);
883 function sql_bool_to_bool($s) {
884 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
891 function bool_to_sql_bool($s) {
899 // Session caching removed due to causing wrong redirects to upgrade
900 // script when get_schema_version() is called on an obsolete session
901 // created on a previous schema version.
902 function get_schema_version($link, $nocache = false) {
903 global $schema_version;
905 if (!$schema_version) {
906 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
907 $version = db_fetch_result($result, 0, "schema_version");
908 $schema_version = $version;
911 return $schema_version;
915 function sanity_check($link) {
916 require_once 'errors.php';
919 $schema_version = get_schema_version($link, true);
921 if ($schema_version != SCHEMA_VERSION
) {
925 if (DB_TYPE
== "mysql") {
926 $result = db_query($link, "SELECT true", false);
927 if (db_num_rows($result) != 1) {
932 if (db_escape_string($link, "testTEST") != "testTEST") {
936 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
939 function file_is_locked($filename) {
940 if (function_exists('flock')) {
941 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
943 if (flock($fp, LOCK_EX | LOCK_NB
)) {
954 return true; // consider the file always locked and skip the test
957 function make_lockfile($filename) {
958 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
960 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
961 if (function_exists('posix_getpid')) {
962 fwrite($fp, posix_getpid() . "\n");
970 function make_stampfile($filename) {
971 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
973 if (flock($fp, LOCK_EX | LOCK_NB
)) {
974 fwrite($fp, time() . "\n");
983 function sql_random_function() {
984 if (DB_TYPE
== "mysql") {
991 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false) {
993 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
995 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
997 $ref_check_qpart = ($max_id &&
998 !get_pref($link, 'REVERSE_HEADLINES')) ?
"ref_id <= '$max_id'" : "true";
1000 if (is_numeric($feed)) {
1006 $children = getChildCategories($link, $feed, $owner_uid);
1007 array_push($children, $feed);
1009 $children = join(",", $children);
1011 $cat_qpart = "cat_id IN ($children)";
1013 $cat_qpart = "cat_id IS NULL";
1016 db_query($link, "UPDATE ttrss_user_entries
1017 SET unread = false,last_read = NOW()
1018 WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart)
1019 AND $ref_check_qpart AND unread = true
1020 AND owner_uid = $owner_uid");
1022 } else if ($feed == -2) {
1024 db_query($link, "UPDATE ttrss_user_entries
1025 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1026 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1027 AND $ref_check_qpart
1028 AND unread = true AND owner_uid = $owner_uid");
1031 } else if ($feed > 0) {
1033 db_query($link, "UPDATE ttrss_user_entries
1034 SET unread = false,last_read = NOW()
1035 WHERE feed_id = '$feed'
1036 AND $ref_check_qpart AND unread = true
1037 AND owner_uid = $owner_uid");
1039 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1042 db_query($link, "UPDATE ttrss_user_entries
1043 SET unread = false,last_read = NOW()
1045 AND $ref_check_qpart AND unread = true
1046 AND owner_uid = $owner_uid");
1050 db_query($link, "UPDATE ttrss_user_entries
1051 SET unread = false,last_read = NOW()
1052 WHERE published = true
1053 AND $ref_check_qpart AND unread = true
1054 AND owner_uid = $owner_uid");
1059 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1061 if (DB_TYPE
== "pgsql") {
1062 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1064 $match_part = "updated > DATE_SUB(NOW(),
1065 INTERVAL $intl HOUR) ";
1068 $result = db_query($link, "SELECT id FROM ttrss_entries,
1069 ttrss_user_entries WHERE $match_part AND
1071 ttrss_user_entries.ref_id = ttrss_entries.id AND
1072 owner_uid = $owner_uid");
1074 $affected_ids = array();
1076 while ($line = db_fetch_assoc($result)) {
1077 array_push($affected_ids, $line["id"]);
1080 catchupArticlesById($link, $affected_ids, 0);
1084 db_query($link, "UPDATE ttrss_user_entries
1085 SET unread = false,last_read = NOW()
1086 WHERE $ref_check_qpart AND unread = true AND
1087 owner_uid = $owner_uid");
1090 } else if ($feed < LABEL_BASE_INDEX
) { // label
1092 $label_id = feed_to_label_id($feed);
1094 db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
1095 SET unread = false, last_read = NOW()
1096 WHERE label_id = '$label_id' AND unread = true
1097 AND $ref_check_qpart
1098 AND owner_uid = '$owner_uid' AND ref_id = article_id");
1102 ccache_update($link, $feed, $owner_uid, $cat_view);
1105 db_query($link, "BEGIN");
1107 $tag_name = db_escape_string($link, $feed);
1109 $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
1110 WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
1112 while ($line = db_fetch_assoc($result)) {
1113 db_query($link, "UPDATE ttrss_user_entries SET
1114 unread = false, last_read = NOW()
1115 WHERE $ref_check_qpart AND unread = true
1116 AND int_id = " . $line["post_int_id"]);
1118 db_query($link, "COMMIT");
1122 function getAllCounters($link) {
1123 $data = getGlobalCounters($link);
1125 $data = array_merge($data, getVirtCounters($link));
1126 $data = array_merge($data, getLabelCounters($link));
1127 $data = array_merge($data, getFeedCounters($link, $active_feed));
1128 $data = array_merge($data, getCategoryCounters($link));
1133 function getCategoryTitle($link, $cat_id) {
1135 if ($cat_id == -1) {
1136 return __("Special");
1137 } else if ($cat_id == -2) {
1138 return __("Labels");
1141 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1144 if (db_num_rows($result) == 1) {
1145 return db_fetch_result($result, 0, "title");
1147 return __("Uncategorized");
1153 function getCategoryCounters($link) {
1156 /* Labels category */
1158 $cv = array("id" => -2, "kind" => "cat",
1159 "counter" => getCategoryUnread($link, -2));
1161 array_push($ret_arr, $cv);
1163 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1164 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1165 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1166 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1167 WHERE ttrss_cat_counters_cache.feed_id = id AND
1168 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1169 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1171 while ($line = db_fetch_assoc($result)) {
1172 $line["cat_id"] = (int) $line["cat_id"];
1174 if ($line["num_children"] > 0) {
1175 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1180 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1181 "counter" => $line["unread"] +
$child_counter);
1183 array_push($ret_arr, $cv);
1186 /* Special case: NULL category doesn't actually exist in the DB */
1188 $cv = array("id" => 0, "kind" => "cat",
1189 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1191 array_push($ret_arr, $cv);
1196 // only accepts real cats (>= 0)
1197 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1198 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1200 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1201 AND owner_uid = $owner_uid");
1205 while ($line = db_fetch_assoc($result)) {
1206 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1207 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1213 function getCategoryUnread($link, $cat, $owner_uid = false) {
1215 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1220 $cat_query = "cat_id = '$cat'";
1222 $cat_query = "cat_id IS NULL";
1225 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1226 AND owner_uid = " . $owner_uid);
1228 $cat_feeds = array();
1229 while ($line = db_fetch_assoc($result)) {
1230 array_push($cat_feeds, "feed_id = " . $line["id"]);
1233 if (count($cat_feeds) == 0) return 0;
1235 $match_part = implode(" OR ", $cat_feeds);
1237 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1238 FROM ttrss_user_entries
1239 WHERE unread = true AND ($match_part)
1240 AND owner_uid = " . $owner_uid);
1244 # this needs to be rewritten
1245 while ($line = db_fetch_assoc($result)) {
1246 $unread +
= $line["unread"];
1250 } else if ($cat == -1) {
1251 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1252 } else if ($cat == -2) {
1254 $result = db_query($link, "
1255 SELECT COUNT(unread) AS unread FROM
1256 ttrss_user_entries, ttrss_user_labels2
1257 WHERE article_id = ref_id AND unread = true
1258 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1260 $unread = db_fetch_result($result, 0, "unread");
1267 function getFeedUnread($link, $feed, $is_cat = false) {
1268 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1271 function getLabelUnread($link, $label_id, $owner_uid = false) {
1272 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1274 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1275 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1277 if (db_num_rows($result) != 0) {
1278 return db_fetch_result($result, 0, "unread");
1284 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1285 $owner_uid = false) {
1287 $n_feed = (int) $feed;
1288 $need_entries = false;
1290 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1293 $unread_qpart = "unread = true";
1295 $unread_qpart = "true";
1299 return getCategoryUnread($link, $n_feed, $owner_uid);
1300 } else if ($n_feed == -6) {
1302 } else if ($feed != "0" && $n_feed == 0) {
1304 $feed = db_escape_string($link, $feed);
1306 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1307 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1308 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1309 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1310 return db_fetch_result($result, 0, "count");
1312 } else if ($n_feed == -1) {
1313 $match_part = "marked = true";
1314 } else if ($n_feed == -2) {
1315 $match_part = "published = true";
1316 } else if ($n_feed == -3) {
1317 $match_part = "unread = true AND score >= 0";
1319 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1321 if (DB_TYPE
== "pgsql") {
1322 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1324 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1327 $need_entries = true;
1329 } else if ($n_feed == -4) {
1330 $match_part = "true";
1331 } else if ($n_feed >= 0) {
1334 $match_part = "feed_id = '$n_feed'";
1336 $match_part = "feed_id IS NULL";
1339 } else if ($feed < LABEL_BASE_INDEX
) {
1341 $label_id = feed_to_label_id($feed);
1343 return getLabelUnread($link, $label_id, $owner_uid);
1349 if ($need_entries) {
1350 $from_qpart = "ttrss_user_entries,ttrss_entries";
1351 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1353 $from_qpart = "ttrss_user_entries";
1356 $query = "SELECT count(int_id) AS unread
1357 FROM $from_qpart WHERE
1358 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1360 //echo "[$feed/$query]\n";
1362 $result = db_query($link, $query);
1366 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1367 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1368 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1369 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1372 $unread = db_fetch_result($result, 0, "unread");
1377 function getGlobalUnread($link, $user_id = false) {
1380 $user_id = $_SESSION["uid"];
1383 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1384 WHERE owner_uid = '$user_id' AND feed_id > 0");
1386 $c_id = db_fetch_result($result, 0, "c_id");
1391 function getGlobalCounters($link, $global_unread = -1) {
1394 if ($global_unread == -1) {
1395 $global_unread = getGlobalUnread($link);
1398 $cv = array("id" => "global-unread",
1399 "counter" => (int) $global_unread);
1401 array_push($ret_arr, $cv);
1403 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1404 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1406 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1408 $cv = array("id" => "subscribed-feeds",
1409 "counter" => (int) $subscribed_feeds);
1411 array_push($ret_arr, $cv);
1416 function getVirtCounters($link) {
1420 for ($i = 0; $i >= -4; $i--) {
1422 $count = getFeedUnread($link, $i);
1424 $cv = array("id" => $i,
1425 "counter" => (int) $count);
1427 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1428 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1430 array_push($ret_arr, $cv);
1436 function getLabelCounters($link, $descriptions = false) {
1440 $owner_uid = $_SESSION["uid"];
1442 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1443 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1444 (ttrss_labels2.id = label_id)
1445 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1446 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1447 ttrss_labels2.caption");
1449 while ($line = db_fetch_assoc($result)) {
1451 $id = label_to_feed_id($line["id"]);
1453 $label_name = $line["caption"];
1454 $count = $line["unread"];
1456 $cv = array("id" => $id,
1457 "counter" => (int) $count);
1460 $cv["description"] = $label_name;
1462 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1463 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1465 array_push($ret_arr, $cv);
1471 function getFeedCounters($link, $active_feed = false) {
1475 $query = "SELECT ttrss_feeds.id,
1477 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1478 last_error, value AS count
1479 FROM ttrss_feeds, ttrss_counters_cache
1480 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1481 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1482 AND ttrss_counters_cache.feed_id = id";
1484 $result = db_query($link, $query);
1485 $fctrs_modified = false;
1487 while ($line = db_fetch_assoc($result)) {
1490 $count = $line["count"];
1491 $last_error = htmlspecialchars($line["last_error"]);
1493 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1495 $has_img = feed_has_icon($id);
1497 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1500 $cv = array("id" => $id,
1501 "updated" => $last_updated,
1502 "counter" => (int) $count,
1503 "has_img" => (int) $has_img);
1506 $cv["error"] = $last_error;
1508 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1509 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1511 if ($active_feed && $id == $active_feed)
1512 $cv["title"] = truncate_string($line["title"], 30);
1514 array_push($ret_arr, $cv);
1521 function get_pgsql_version($link) {
1522 $result = db_query($link, "SELECT version() AS version");
1523 $version = explode(" ", db_fetch_result($result, 0, "version"));
1528 * @return array (code => Status code, message => error message if available)
1530 * 0 - OK, Feed already exists
1531 * 1 - OK, Feed added
1533 * 3 - URL content is HTML, no feeds available
1534 * 4 - URL content is HTML which contains multiple feeds.
1535 * Here you should call extractfeedurls in rpc-backend
1536 * to get all possible feeds.
1537 * 5 - Couldn't download the URL content.
1539 function subscribe_to_feed($link, $url, $cat_id = 0,
1540 $auth_login = '', $auth_pass = '') {
1542 global $fetch_last_error;
1544 require_once "include/rssfuncs.php";
1546 $url = fix_url($url);
1548 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1550 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1553 return array("code" => 5, "message" => $fetch_last_error);
1556 if (is_html($contents)) {
1557 $feedUrls = get_feeds_from_html($url, $contents);
1559 if (count($feedUrls) == 0) {
1560 return array("code" => 3);
1561 } else if (count($feedUrls) > 1) {
1562 return array("code" => 4, "feeds" => $feedUrls);
1564 //use feed url as new URL
1565 $url = key($feedUrls);
1568 if ($cat_id == "0" ||
!$cat_id) {
1569 $cat_qpart = "NULL";
1571 $cat_qpart = "'$cat_id'";
1574 $result = db_query($link,
1575 "SELECT id FROM ttrss_feeds
1576 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1578 if (db_num_rows($result) == 0) {
1579 $result = db_query($link,
1580 "INSERT INTO ttrss_feeds
1581 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1582 VALUES ('".$_SESSION["uid"]."', '$url',
1583 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1585 $result = db_query($link,
1586 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1587 AND owner_uid = " . $_SESSION["uid"]);
1589 $feed_id = db_fetch_result($result, 0, "id");
1592 update_rss_feed($link, $feed_id, true);
1595 return array("code" => 1);
1597 return array("code" => 0);
1601 function print_feed_select($link, $id, $default_id = "",
1602 $attributes = "", $include_all_feeds = true,
1603 $root_id = false, $nest_level = 0) {
1606 print "<select id=\"$id\" name=\"$id\" $attributes>";
1607 if ($include_all_feeds) {
1608 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1609 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1613 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1616 $parent_qpart = "parent_cat = '$root_id'";
1618 $parent_qpart = "parent_cat IS NULL";
1620 $result = db_query($link, "SELECT id,title,
1621 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1622 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1623 FROM ttrss_feed_categories
1624 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1626 while ($line = db_fetch_assoc($result)) {
1628 for ($i = 0; $i < $nest_level; $i++
)
1629 $line["title"] = " - " . $line["title"];
1631 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1633 printf("<option $is_selected value='CAT:%d'>%s</option>",
1634 $line["id"], htmlspecialchars($line["title"]));
1636 if ($line["num_children"] > 0)
1637 print_feed_select($link, $id, $default_id, $attributes,
1638 $include_all_feeds, $line["id"], $nest_level+
1);
1640 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1641 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1643 while ($fline = db_fetch_assoc($feed_result)) {
1644 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1646 $fline["title"] = " + " . $fline["title"];
1648 for ($i = 0; $i < $nest_level; $i++
)
1649 $fline["title"] = " - " . $fline["title"];
1651 printf("<option $is_selected value='%d'>%s</option>",
1652 $fline["id"], htmlspecialchars($fline["title"]));
1657 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1659 printf("<option $is_selected value='CAT:0'>%s</option>",
1660 __("Uncategorized"));
1662 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1663 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1665 while ($fline = db_fetch_assoc($feed_result)) {
1666 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1668 $fline["title"] = " + " . $fline["title"];
1670 for ($i = 0; $i < $nest_level; $i++
)
1671 $fline["title"] = " - " . $fline["title"];
1673 printf("<option $is_selected value='%d'>%s</option>",
1674 $fline["id"], htmlspecialchars($fline["title"]));
1679 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1680 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1682 while ($line = db_fetch_assoc($result)) {
1684 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1686 printf("<option $is_selected value='%d'>%s</option>",
1687 $line["id"], htmlspecialchars($line["title"]));
1696 function print_feed_cat_select($link, $id, $default_id,
1697 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1700 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1704 $parent_qpart = "parent_cat = '$root_id'";
1706 $parent_qpart = "parent_cat IS NULL";
1708 $result = db_query($link, "SELECT id,title,
1709 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1710 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1711 FROM ttrss_feed_categories
1712 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1714 while ($line = db_fetch_assoc($result)) {
1715 if ($line["id"] == $default_id) {
1716 $is_selected = "selected=\"1\"";
1721 for ($i = 0; $i < $nest_level; $i++
)
1722 $line["title"] = " - " . $line["title"];
1725 printf("<option $is_selected value='%d'>%s</option>",
1726 $line["id"], htmlspecialchars($line["title"]));
1728 if ($line["num_children"] > 0)
1729 print_feed_cat_select($link, $id, $default_id, $attributes,
1730 $include_all_cats, $line["id"], $nest_level+
1);
1734 if ($include_all_cats) {
1735 if (db_num_rows($result) > 0) {
1736 print "<option disabled=\"1\">--------</option>";
1739 if ($default_id == 0) {
1740 $is_selected = "selected=\"1\"";
1745 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1751 function checkbox_to_sql_bool($val) {
1752 return ($val == "on") ?
"true" : "false";
1755 function getFeedCatTitle($link, $id) {
1757 return __("Special");
1758 } else if ($id < LABEL_BASE_INDEX
) {
1759 return __("Labels");
1760 } else if ($id > 0) {
1761 $result = db_query($link, "SELECT ttrss_feed_categories.title
1762 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1763 cat_id = ttrss_feed_categories.id");
1764 if (db_num_rows($result) == 1) {
1765 return db_fetch_result($result, 0, "title");
1767 return __("Uncategorized");
1770 return "getFeedCatTitle($id) failed";
1775 function getFeedIcon($id) {
1778 return "images/archive.png";
1781 return "images/mark_set.svg";
1784 return "images/pub_set.svg";
1787 return "images/fresh.png";
1790 return "images/tag.png";
1793 return "images/recently_read.png";
1796 if ($id < LABEL_BASE_INDEX
) {
1797 return "images/label.png";
1799 if (file_exists(ICONS_DIR
. "/$id.ico"))
1800 return ICONS_URL
. "/$id.ico";
1806 function getFeedTitle($link, $id, $cat = false) {
1808 return getCategoryTitle($link, $id);
1809 } else if ($id == -1) {
1810 return __("Starred articles");
1811 } else if ($id == -2) {
1812 return __("Published articles");
1813 } else if ($id == -3) {
1814 return __("Fresh articles");
1815 } else if ($id == -4) {
1816 return __("All articles");
1817 } else if ($id === 0 ||
$id === "0") {
1818 return __("Archived articles");
1819 } else if ($id == -6) {
1820 return __("Recently read");
1821 } else if ($id < LABEL_BASE_INDEX
) {
1822 $label_id = feed_to_label_id($id);
1823 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1824 if (db_num_rows($result) == 1) {
1825 return db_fetch_result($result, 0, "caption");
1827 return "Unknown label ($label_id)";
1830 } else if (is_numeric($id) && $id > 0) {
1831 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1832 if (db_num_rows($result) == 1) {
1833 return db_fetch_result($result, 0, "title");
1835 return "Unknown feed ($id)";
1842 function make_init_params($link) {
1845 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1846 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1847 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1848 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1850 $params[strtolower($param)] = (int) get_pref($link, $param);
1853 $params["icons_url"] = ICONS_URL
;
1854 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1855 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1856 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1857 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1858 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1859 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1861 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1862 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1864 $max_feed_id = db_fetch_result($result, 0, "mid");
1865 $num_feeds = db_fetch_result($result, 0, "nf");
1867 $params["max_feed_id"] = (int) $max_feed_id;
1868 $params["num_feeds"] = (int) $num_feeds;
1870 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1871 $params["hotkeys"] = get_hotkeys_map($link);
1873 $params["csrf_token"] = $_SESSION["csrf_token"];
1874 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1876 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1881 function get_hotkeys_info($link) {
1883 __("Navigation") => array(
1884 "next_feed" => __("Open next feed"),
1885 "prev_feed" => __("Open previous feed"),
1886 "next_article" => __("Open next article"),
1887 "prev_article" => __("Open previous article"),
1888 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1889 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1890 "search_dialog" => __("Show search dialog")),
1891 __("Article") => array(
1892 "toggle_mark" => __("Toggle starred"),
1893 "toggle_publ" => __("Toggle published"),
1894 "toggle_unread" => __("Toggle unread"),
1895 "edit_tags" => __("Edit tags"),
1896 "dismiss_selected" => __("Dismiss selected"),
1897 "dismiss_read" => __("Dismiss read"),
1898 "open_in_new_window" => __("Open in new window"),
1899 "catchup_below" => __("Mark below as read"),
1900 "catchup_above" => __("Mark above as read"),
1901 "article_scroll_down" => __("Scroll down"),
1902 "article_scroll_up" => __("Scroll up"),
1903 "select_article_cursor" => __("Select article under cursor"),
1904 "email_article" => __("Email article"),
1905 "close_article" => __("Close/collapse article"),
1906 "toggle_widescreen" => __("Toggle widescreen mode"),
1907 "toggle_embed_original" => __("Toggle embed original")),
1908 __("Article selection") => array(
1909 "select_all" => __("Select all articles"),
1910 "select_unread" => __("Select unread"),
1911 "select_marked" => __("Select starred"),
1912 "select_published" => __("Select published"),
1913 "select_invert" => __("Invert selection"),
1914 "select_none" => __("Deselect everything")),
1915 __("Feed") => array(
1916 "feed_refresh" => __("Refresh current feed"),
1917 "feed_unhide_read" => __("Un/hide read feeds"),
1918 "feed_subscribe" => __("Subscribe to feed"),
1919 "feed_edit" => __("Edit feed"),
1920 "feed_catchup" => __("Mark as read"),
1921 "feed_reverse" => __("Reverse headlines"),
1922 "feed_debug_update" => __("Debug feed update"),
1923 "catchup_all" => __("Mark all feeds as read"),
1924 "cat_toggle_collapse" => __("Un/collapse current category"),
1925 "toggle_combined_mode" => __("Toggle combined mode")),
1926 __("Go to") => array(
1927 "goto_all" => __("All articles"),
1928 "goto_fresh" => __("Fresh"),
1929 "goto_marked" => __("Starred"),
1930 "goto_published" => __("Published"),
1931 "goto_tagcloud" => __("Tag cloud"),
1932 "goto_prefs" => __("Preferences")),
1933 __("Other") => array(
1934 "create_label" => __("Create label"),
1935 "create_filter" => __("Create filter"),
1936 "collapse_sidebar" => __("Un/collapse sidebar"),
1937 "help_dialog" => __("Show help dialog"))
1943 function get_hotkeys_map($link) {
1945 // "navigation" => array(
1948 "n" => "next_article",
1949 "p" => "prev_article",
1950 "(38)|up" => "prev_article",
1951 "(40)|down" => "next_article",
1952 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1953 // "^(40)|Ctrl-down" => "next_article_noscroll",
1954 "(191)|/" => "search_dialog",
1955 // "article" => array(
1956 "s" => "toggle_mark",
1957 "*s" => "toggle_publ",
1958 "u" => "toggle_unread",
1959 "*t" => "edit_tags",
1960 "*d" => "dismiss_selected",
1961 "*x" => "dismiss_read",
1962 "o" => "open_in_new_window",
1963 "c p" => "catchup_below",
1964 "c n" => "catchup_above",
1965 "*n" => "article_scroll_down",
1966 "*p" => "article_scroll_up",
1967 "*(38)|Shift+up" => "article_scroll_up",
1968 "*(40)|Shift+down" => "article_scroll_down",
1969 "a *w" => "toggle_widescreen",
1970 "a e" => "toggle_embed_original",
1971 "e" => "email_article",
1972 "a q" => "close_article",
1973 // "article_selection" => array(
1974 "a a" => "select_all",
1975 "a u" => "select_unread",
1976 "a *u" => "select_marked",
1977 "a p" => "select_published",
1978 "a i" => "select_invert",
1979 "a n" => "select_none",
1981 "f r" => "feed_refresh",
1982 "f a" => "feed_unhide_read",
1983 "f s" => "feed_subscribe",
1984 "f e" => "feed_edit",
1985 "f q" => "feed_catchup",
1986 "f x" => "feed_reverse",
1987 "f *d" => "feed_debug_update",
1988 "f *c" => "toggle_combined_mode",
1989 "*q" => "catchup_all",
1990 "x" => "cat_toggle_collapse",
1992 "g a" => "goto_all",
1993 "g f" => "goto_fresh",
1994 "g s" => "goto_marked",
1995 "g p" => "goto_published",
1996 "g t" => "goto_tagcloud",
1997 "g *p" => "goto_prefs",
1998 // "other" => array(
1999 "(9)|Tab" => "select_article_cursor", // tab
2000 "c l" => "create_label",
2001 "c f" => "create_filter",
2002 "c s" => "collapse_sidebar",
2003 "^(191)|Ctrl+/" => "help_dialog",
2006 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2007 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2008 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2012 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2013 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2016 $prefixes = array();
2018 foreach (array_keys($hotkeys) as $hotkey) {
2019 $pair = explode(" ", $hotkey, 2);
2021 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2022 array_push($prefixes, $pair[0]);
2026 return array($prefixes, $hotkeys);
2029 function make_runtime_info($link) {
2032 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2033 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2035 $max_feed_id = db_fetch_result($result, 0, "mid");
2036 $num_feeds = db_fetch_result($result, 0, "nf");
2038 $data["max_feed_id"] = (int) $max_feed_id;
2039 $data["num_feeds"] = (int) $num_feeds;
2041 $data['last_article_id'] = getLastArticleId($link);
2042 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2044 $data['dep_ts'] = calculate_dep_timestamp();
2045 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2047 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2049 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2051 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2053 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2056 $stamp_delta = time() - $stamp;
2058 if ($stamp_delta > 1800) {
2062 $_SESSION["daemon_stamp_check"] = time();
2065 $data['daemon_stamp_ok'] = $stamp_check;
2067 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2069 $data['daemon_stamp'] = $stamp_fmt;
2074 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2075 $new_version_details = @check_for_update
($link);
2077 $data['new_version_available'] = (int) ($new_version_details != false);
2079 $_SESSION["last_version_check"] = time();
2080 $_SESSION["version_data"] = $new_version_details;
2086 function search_to_sql($link, $search) {
2088 $search_query_part = "";
2090 $keywords = explode(" ", $search);
2091 $query_keywords = array();
2093 foreach ($keywords as $k) {
2094 if (strpos($k, "-") === 0) {
2101 $commandpair = explode(":", mb_strtolower($k), 2);
2103 if ($commandpair[0] == "note" && $commandpair[1]) {
2105 if ($commandpair[1] == "true")
2106 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2108 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2110 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2112 if ($commandpair[1] == "true")
2113 array_push($query_keywords, "($not (marked = true))");
2115 array_push($query_keywords, "($not (marked = false))");
2117 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2119 if ($commandpair[1] == "true")
2120 array_push($query_keywords, "($not (published = true))");
2122 array_push($query_keywords, "($not (published = false))");
2124 } else if (strpos($k, "@") === 0) {
2126 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2127 $orig_ts = strtotime(substr($k, 1));
2128 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2130 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2132 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2134 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2135 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2139 $search_query_part = implode("AND", $query_keywords);
2141 return $search_query_part;
2144 function getParentCategories($link, $cat, $owner_uid) {
2147 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2148 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2150 while ($line = db_fetch_assoc($result)) {
2151 array_push($rv, $line["parent_cat"]);
2152 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2158 function getChildCategories($link, $cat, $owner_uid) {
2161 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2162 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2164 while ($line = db_fetch_assoc($result)) {
2165 array_push($rv, $line["id"]);
2166 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2172 function queryFeedHeadlines($link, $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) {
2174 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2176 $ext_tables_part = "";
2180 if (SPHINX_ENABLED
) {
2181 $ids = join(",", @sphinx_search
($search, 0, 500));
2184 $search_query_part = "ref_id IN ($ids) AND ";
2186 $search_query_part = "ref_id = -1 AND ";
2189 $search_query_part = search_to_sql($link, $search);
2190 $search_query_part .= " AND ";
2194 $search_query_part = "";
2199 if (DB_TYPE
== "pgsql") {
2200 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2202 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2205 $override_order = "updated DESC";
2207 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2209 // Try to check if SQL regexp implementation chokes on a valid regexp
2210 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2211 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2212 WHERE $filter_query_part LIMIT 1", false);
2215 $test = db_fetch_result($result, 0, "true_val");
2218 $filter_query_part = "false AND";
2220 $filter_query_part .= " AND";
2223 $filter_query_part = "false AND";
2227 $filter_query_part = "";
2231 $since_id_part = "ttrss_entries.id > $since_id AND ";
2233 $since_id_part = "";
2236 $view_query_part = "";
2238 if ($view_mode == "adaptive" ||
$view_query_part == "noscores") {
2240 $view_query_part = " ";
2241 } else if ($feed != -1) {
2243 $unread = getFeedUnread($link, $feed, $cat_view);
2245 if ($cat_view && $feed > 0 && $include_children)
2246 $unread +
= getCategoryChildrenUnread($link, $feed);
2249 $view_query_part = " unread = true AND ";
2254 if ($view_mode == "marked") {
2255 $view_query_part = " marked = true AND ";
2258 if ($view_mode == "published") {
2259 $view_query_part = " published = true AND ";
2262 if ($view_mode == "unread" && $feed != -6) {
2263 $view_query_part = " unread = true AND ";
2266 if ($view_mode == "updated") {
2267 $view_query_part = " (last_read is null and unread = false) AND ";
2271 $limit_query_part = "LIMIT " . $limit;
2274 $allow_archived = false;
2276 $vfeed_query_part = "";
2278 // override query strategy and enable feed display when searching globally
2279 if ($search && $search_mode == "all_feeds") {
2280 $query_strategy_part = "true";
2281 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2283 } else if (!is_numeric($feed)) {
2284 $query_strategy_part = "true";
2285 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2286 id = feed_id) as feed_title,";
2287 } else if ($search && $search_mode == "this_cat") {
2288 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2291 if ($include_children) {
2292 $subcats = getChildCategories($link, $feed, $owner_uid);
2293 array_push($subcats, $feed);
2294 $cats_qpart = join(",", $subcats);
2296 $cats_qpart = $feed;
2299 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2302 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2305 } else if ($feed > 0) {
2310 if ($include_children) {
2312 $subcats = getChildCategories($link, $feed, $owner_uid);
2314 array_push($subcats, $feed);
2315 $query_strategy_part = "cat_id IN (".
2316 implode(",", $subcats).")";
2319 $query_strategy_part = "cat_id = '$feed'";
2323 $query_strategy_part = "cat_id IS NULL";
2326 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2329 $query_strategy_part = "feed_id = '$feed'";
2331 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2332 $query_strategy_part = "feed_id IS NULL";
2333 $allow_archived = true;
2334 } else if ($feed == 0 && $cat_view) { // uncategorized
2335 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2336 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2337 } else if ($feed == -1) { // starred virtual feed
2338 $query_strategy_part = "marked = true";
2339 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2340 $allow_archived = true;
2342 if (!$override_order) {
2343 if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
2344 $override_order = "date_entered";
2346 $override_order = "last_marked DESC, date_entered DESC";
2350 } else if ($feed == -2) { // published virtual feed OR labels category
2353 $query_strategy_part = "published = true";
2354 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2355 $allow_archived = true;
2357 if (!$override_order) {
2358 if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
2359 $override_order = "date_entered";
2361 $override_order = "last_published DESC, date_entered DESC";
2366 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2368 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2370 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2371 ttrss_user_labels2.article_id = ref_id";
2374 } else if ($feed == -6) { // recently read
2375 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2376 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2377 $allow_archived = true;
2379 if (!$override_order) $override_order = "last_read DESC";
2380 } else if ($feed == -3) { // fresh virtual feed
2381 $query_strategy_part = "unread = true AND score >= 0";
2383 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2385 if (DB_TYPE
== "pgsql") {
2386 $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
2388 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2391 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2392 } else if ($feed == -4) { // all articles virtual feed
2393 $query_strategy_part = "true";
2394 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2395 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2396 $label_id = feed_to_label_id($feed);
2398 $query_strategy_part = "label_id = '$label_id' AND
2399 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2400 ttrss_user_labels2.article_id = ref_id";
2402 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2403 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2404 $allow_archived = true;
2407 $query_strategy_part = "true";
2410 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2411 $date_sort_field = "updated";
2413 $date_sort_field = "date_entered";
2416 if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
2417 $order_by = "$date_sort_field";
2419 $order_by = "$date_sort_field DESC";
2422 if ($view_mode != "noscores") {
2423 $order_by = "score DESC, $order_by";
2426 if ($view_mode == "unread_first") {
2427 $order_by = "unread DESC, $order_by";
2430 if ($override_order) {
2431 $order_by = $override_order;
2437 $feed_title = T_sprintf("Search results: %s", $search);
2440 $feed_title = getCategoryTitle($link, $feed);
2442 if (is_numeric($feed) && $feed > 0) {
2443 $result = db_query($link, "SELECT title,site_url,last_error
2444 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2446 $feed_title = db_fetch_result($result, 0, "title");
2447 $feed_site_url = db_fetch_result($result, 0, "site_url");
2448 $last_error = db_fetch_result($result, 0, "last_error");
2450 $feed_title = getFeedTitle($link, $feed);
2455 $content_query_part = "content as content_preview, cached_content, ";
2457 if (is_numeric($feed)) {
2460 $feed_kind = "Feeds";
2462 $feed_kind = "Labels";
2465 if ($limit_query_part) {
2466 $offset_query_part = "OFFSET $offset";
2469 // proper override_order applied above
2470 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2471 if (!$override_order) {
2472 $order_by = "ttrss_feeds.title, $order_by";
2474 $order_by = "ttrss_feeds.title, $override_order";
2478 if (!$allow_archived) {
2479 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2480 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2483 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2484 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2487 $query = "SELECT DISTINCT
2490 ttrss_entries.id,ttrss_entries.title,
2494 always_display_enclosures,
2501 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2502 last_marked, last_published,
2503 ".SUBSTRING_FOR_DATE
."(last_read,1,19) as last_read_noms,
2506 ".SUBSTRING_FOR_DATE
."(updated,1,19) as updated_noms,
2512 ttrss_user_entries.ref_id = ttrss_entries.id AND
2513 ttrss_user_entries.owner_uid = '$owner_uid' AND
2518 $query_strategy_part ORDER BY $order_by
2519 $limit_query_part $offset_query_part";
2521 if ($_REQUEST["debug"]) print $query;
2523 $result = db_query($link, $query);
2528 $select_qpart = "SELECT DISTINCT " .
2532 "ttrss_entries.id as id," .
2545 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2546 "last_marked, last_published, " .
2547 SUBSTRING_FOR_DATE
. "(last_read,1,19) as last_read_noms," .
2550 $content_query_part .
2551 SUBSTRING_FOR_DATE
. "(updated,1,19) as updated_noms," .
2554 $feed_kind = "Tags";
2555 $all_tags = explode(",", $feed);
2556 if ($search_mode == 'any') {
2557 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2558 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2559 $where_qpart = " WHERE " .
2560 "ref_id = ttrss_entries.id AND " .
2561 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2562 "post_int_id = int_id AND $tag_sql AND " .
2564 $search_query_part .
2565 $query_strategy_part . " ORDER BY $order_by " .
2570 $sub_selects = array();
2571 $sub_ands = array();
2572 foreach ($all_tags as $term) {
2573 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");
2580 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2585 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2586 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2587 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2588 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2590 // error_log("TAG SQL: " . $tag_sql);
2591 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2593 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2594 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2597 return array($result, $feed_title, $feed_site_url, $last_error);
2601 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2602 if (!$owner) $owner = $_SESSION["uid"];
2604 $res = trim($str); if (!$res) return '';
2606 if (strpos($res, "href=") === false)
2607 $res = rewrite_urls($res);
2609 $charset_hack = '<head>
2610 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2613 $res = trim($res); if (!$res) return '';
2615 libxml_use_internal_errors(true);
2617 $doc = new DOMDocument();
2618 $doc->loadHTML($charset_hack . $res);
2619 $xpath = new DOMXPath($doc);
2621 $entries = $xpath->query('(//a[@href]|//img[@src])');
2623 foreach ($entries as $entry) {
2627 if ($entry->hasAttribute('href'))
2628 $entry->setAttribute('href',
2629 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2631 if ($entry->hasAttribute('src')) {
2632 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2634 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2636 if (file_exists($cached_filename)) {
2637 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2640 $entry->setAttribute('src', $src);
2643 if ($entry->nodeName
== 'img') {
2644 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2645 $force_remove_images) {
2647 $p = $doc->createElement('p');
2649 $a = $doc->createElement('a');
2650 $a->setAttribute('href', $entry->getAttribute('src'));
2652 $a->appendChild(new DOMText($entry->getAttribute('src')));
2653 $a->setAttribute('target', '_blank');
2655 $p->appendChild($a);
2657 $entry->parentNode
->replaceChild($p, $entry);
2662 if (strtolower($entry->nodeName
) == "a") {
2663 $entry->setAttribute("target", "_blank");
2667 $entries = $xpath->query('//iframe');
2668 foreach ($entries as $entry) {
2669 $entry->setAttribute('sandbox', 'allow-scripts');
2675 if (isset($pluginhost)) {
2676 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2677 $doc = $plugin->hook_sanitize($doc, $site_url);
2681 $doc->removeChild($doc->firstChild
); //remove doctype
2682 $doc = strip_harmful_tags($doc);
2683 $res = $doc->saveHTML();
2687 function strip_harmful_tags($doc) {
2688 $entries = $doc->getElementsByTagName("*");
2690 $allowed_elements = array('a', 'address', 'audio', 'article',
2691 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2692 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2693 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2694 'header', 'html', 'i', 'img', 'ins', 'kbd',
2695 'li', 'nav', 'ol', 'p', 'pre', 'q', 's','small',
2696 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2697 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2698 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2700 if ($_SESSION['hasSandbox']) array_push($allowed_elements, 'iframe');
2702 $disallowed_attributes = array('id', 'style', 'class');
2704 foreach ($entries as $entry) {
2705 if (!in_array($entry->nodeName
, $allowed_elements)) {
2706 $entry->parentNode
->removeChild($entry);
2709 if ($entry->hasAttributes()) {
2710 $attrs_to_remove = array();
2712 foreach ($entry->attributes
as $attr) {
2714 if (strpos($attr->nodeName
, 'on') === 0) {
2715 array_push($attrs_to_remove, $attr);
2718 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2719 array_push($attrs_to_remove, $attr);
2723 foreach ($attrs_to_remove as $attr) {
2724 $entry->removeAttributeNode($attr);
2732 function check_for_update($link) {
2733 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2734 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2735 "&iid=" . sha1(SELF_URL_PATH
);
2737 $version_data = @fetch_file_contents
($version_url);
2739 if ($version_data) {
2740 $version_data = json_decode($version_data, true);
2741 if ($version_data && $version_data['version']) {
2743 if (version_compare(VERSION
, $version_data['version']) == -1) {
2744 return $version_data;
2752 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2754 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2755 if (count($ids) == 0) return;
2759 foreach ($ids as $id) {
2760 array_push($tmp_ids, "ref_id = '$id'");
2763 $ids_qpart = join(" OR ", $tmp_ids);
2766 db_query($link, "UPDATE ttrss_user_entries SET
2767 unread = false,last_read = NOW()
2768 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2769 } else if ($cmode == 1) {
2770 db_query($link, "UPDATE ttrss_user_entries SET
2772 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2774 db_query($link, "UPDATE ttrss_user_entries SET
2775 unread = NOT unread,last_read = NOW()
2776 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2781 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2782 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2784 while ($line = db_fetch_assoc($result)) {
2785 ccache_update($link, $line["feed_id"], $owner_uid);
2789 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2791 $a_id = db_escape_string($link, $id);
2793 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2795 $query = "SELECT DISTINCT tag_name,
2796 owner_uid as owner FROM
2797 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2798 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2800 $obj_id = md5("TAGS:$owner_uid:$id");
2803 /* check cache first */
2805 if ($tag_cache === false) {
2806 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2807 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2809 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2813 $tags = explode(",", $tag_cache);
2816 /* do it the hard way */
2818 $tmp_result = db_query($link, $query);
2820 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2821 array_push($tags, $tmp_line["tag_name"]);
2824 /* update the cache */
2826 $tags_str = db_escape_string($link, join(",", $tags));
2828 db_query($link, "UPDATE ttrss_user_entries
2829 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2830 AND owner_uid = $owner_uid");
2836 function trim_array($array) {
2838 array_walk($tmp, 'trim');
2842 function tag_is_valid($tag) {
2843 if ($tag == '') return false;
2844 if (preg_match("/^[0-9]*$/", $tag)) return false;
2845 if (mb_strlen($tag) > 250) return false;
2847 if (function_exists('iconv')) {
2848 $tag = iconv("utf-8", "utf-8", $tag);
2851 if (!$tag) return false;
2856 function render_login_form($link) {
2857 require_once "login_form.php";
2861 // from http://developer.apple.com/internet/safari/faq.html
2862 function no_cache_incantation() {
2863 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2864 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2865 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2866 header("Cache-Control: post-check=0, pre-check=0", false);
2867 header("Pragma: no-cache"); // HTTP/1.0
2870 function format_warning($msg, $id = "") {
2872 return "<div class=\"warning\" id=\"$id\">
2873 <img src=\"images/sign_excl.svg\">$msg</div>";
2876 function format_notice($msg, $id = "") {
2878 return "<div class=\"notice\" id=\"$id\">
2879 <img src=\"images/sign_info.svg\">$msg</div>";
2882 function format_error($msg, $id = "") {
2884 return "<div class=\"error\" id=\"$id\">
2885 <img src=\"images/sign_excl.svg\">$msg</div>";
2888 function print_notice($msg) {
2889 return print format_notice($msg);
2892 function print_warning($msg) {
2893 return print format_warning($msg);
2896 function print_error($msg) {
2897 return print format_error($msg);
2901 function T_sprintf() {
2902 $args = func_get_args();
2903 return vsprintf(__(array_shift($args)), $args);
2906 function format_inline_player($link, $url, $ctype) {
2910 $url = htmlspecialchars($url);
2912 if (strpos($ctype, "audio/") === 0) {
2914 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2915 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2916 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2918 $id = 'AUDIO-' . uniqid();
2920 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2921 <source type=\"$ctype\" src=\"$url\"></source>
2924 $entry .= "<span onclick=\"player(this)\"
2925 title=\"".__("Click to play")."\" status=\"0\"
2926 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2930 $entry .= "<object type=\"application/x-shockwave-flash\"
2931 data=\"lib/button/musicplayer.swf?song_url=$url\"
2932 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2933 <param name=\"movie\"
2934 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2938 if ($entry) $entry .= " <a target=\"_blank\"
2939 href=\"$url\">" . basename($url) . "</a>";
2947 /* $filename = substr($url, strrpos($url, "/")+1);
2949 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2950 $filename . " (" . $ctype . ")" . "</a>"; */
2954 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2955 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2961 /* we can figure out feed_id from article id anyway, why do we
2962 * pass feed_id here? let's ignore the argument :( */
2964 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2965 WHERE ref_id = '$id'");
2967 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2969 $rv['feed_id'] = $feed_id;
2971 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2973 if ($mark_as_read) {
2974 $result = db_query($link, "UPDATE ttrss_user_entries
2975 SET unread = false,last_read = NOW()
2976 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2978 ccache_update($link, $feed_id, $owner_uid);
2981 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2982 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2983 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2984 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2991 FROM ttrss_entries,ttrss_user_entries
2992 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2996 $line = db_fetch_assoc($result);
2998 $tag_cache = $line["tag_cache"];
3000 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3001 unset($line["tag_cache"]);
3003 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3007 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
3008 $line = $p->hook_render_article($line);
3011 $num_comments = $line["num_comments"];
3012 $entry_comments = "";
3014 if ($num_comments > 0) {
3015 if ($line["comments"]) {
3016 $comments_url = htmlspecialchars($line["comments"]);
3018 $comments_url = htmlspecialchars($line["link"]);
3020 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3022 if ($line["comments"] && $line["link"] != $line["comments"]) {
3023 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3028 header("Content-Type: text/html");
3029 $rv['content'] .= "<html><head>
3030 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3031 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3032 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3033 </head><body id=\"ttrssZoom\">";
3036 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3038 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3040 $entry_author = $line["author"];
3042 if ($entry_author) {
3043 $entry_author = __(" - ") . $entry_author;
3046 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3049 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3051 if ($line["link"]) {
3052 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3053 title=\"".htmlspecialchars($line['title'])."\"
3055 htmlspecialchars($line["link"]) . "\">" .
3056 $line["title"] . "</a>" .
3057 "<span class='author'>$entry_author</span></div>";
3059 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3062 $tags_str = format_tags_string($line["tags"], $id);
3063 $tags_str_full = join(", ", $line["tags"]);
3065 if (!$tags_str_full) $tags_str_full = __("no tags");
3067 if (!$entry_comments) $entry_comments = " "; # placeholder
3069 $rv['content'] .= "<div class='postTags' style='float : right'>
3070 <img src='images/tag.png'
3071 class='tagsPic' alt='Tags' title='Tags'> ";
3074 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3075 <a title=\"".__('Edit tags for this article')."\"
3076 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3078 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3079 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3080 position=\"below\">$tags_str_full</div>";
3084 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3085 $rv['content'] .= $p->hook_article_button($line);
3090 $tags_str = strip_tags($tags_str);
3091 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3093 $rv['content'] .= "</div>";
3094 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3096 if ($line["orig_feed_id"]) {
3098 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3099 WHERE id = ".$line["orig_feed_id"]);
3101 if (db_num_rows($tmp_result) != 0) {
3103 $rv['content'] .= "<div clear='both'>";
3104 $rv['content'] .= __("Originally from:");
3106 $rv['content'] .= " ";
3108 $tmp_line = db_fetch_assoc($tmp_result);
3110 $rv['content'] .= "<a target='_blank'
3111 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3112 $tmp_line['title'] . "</a>";
3114 $rv['content'] .= " ";
3116 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3117 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3119 $rv['content'] .= "</div>";
3123 $rv['content'] .= "</div>";
3125 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3126 if ($line['note']) {
3127 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3129 $rv['content'] .= "</div>";
3131 $rv['content'] .= "<div class=\"postContent\">";
3133 $rv['content'] .= $line["content"];
3135 $rv['content'] .= format_article_enclosures($link, $id,
3136 $always_display_enclosures, $line["content"], $line["hide_images"]);
3138 $rv['content'] .= "</div>";
3140 $rv['content'] .= "</div>";
3146 <div class='footer'>
3147 <button onclick=\"return window.close()\">".
3148 __("Close this window")."</button></div>";
3149 $rv['content'] .= "</body></html>";
3156 function print_checkpoint($n, $s) {
3157 $ts = microtime(true);
3158 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3162 function sanitize_tag($tag) {
3165 $tag = mb_strtolower($tag, 'utf-8');
3167 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3169 // $tag = str_replace('"', "", $tag);
3170 // $tag = str_replace("+", " ", $tag);
3171 $tag = str_replace("technorati tag: ", "", $tag);
3176 function get_self_url_prefix() {
3177 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3178 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3180 return SELF_URL_PATH
;
3185 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3187 * @return string The Mozilla Firefox feed adding URL.
3189 function add_feed_url() {
3190 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3192 $url_path = get_self_url_prefix() .
3193 "/public.php?op=subscribe&feed_url=%s";
3195 } // function add_feed_url
3197 function encrypt_password($pass, $salt = '', $mode2 = false) {
3198 if ($salt && $mode2) {
3199 return "MODE2:" . hash('sha256', $salt . $pass);
3201 return "SHA1X:" . sha1("$salt:$pass");
3203 return "SHA1:" . sha1($pass);
3205 } // function encrypt_password
3207 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3210 $cat_id = (int)getFeedCategory($link, $feed_id);
3212 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3213 owner_uid = $owner_uid AND enabled = true");
3215 $check_cats = join(",", array_merge(
3216 getParentCategories($link, $cat_id, $owner_uid),
3219 while ($line = db_fetch_assoc($result)) {
3220 $filter_id = $line["id"];
3222 $result2 = db_query($link, "SELECT
3223 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3224 FROM ttrss_filters2_rules AS r,
3225 ttrss_filter_types AS t
3227 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3228 (feed_id IS NULL OR feed_id = '$feed_id') AND
3229 filter_type = t.id AND filter_id = '$filter_id'");
3234 while ($rule_line = db_fetch_assoc($result2)) {
3235 # print_r($rule_line);
3238 $rule["reg_exp"] = $rule_line["reg_exp"];
3239 $rule["type"] = $rule_line["type_name"];
3240 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3242 array_push($rules, $rule);
3245 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3246 FROM ttrss_filters2_actions AS a,
3247 ttrss_filter_actions AS t
3249 action_id = t.id AND filter_id = '$filter_id'");
3251 while ($action_line = db_fetch_assoc($result2)) {
3252 # print_r($action_line);
3255 $action["type"] = $action_line["type_name"];
3256 $action["param"] = $action_line["action_param"];
3258 array_push($actions, $action);
3263 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3264 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3265 $filter["rules"] = $rules;
3266 $filter["actions"] = $actions;
3268 if (count($rules) > 0 && count($actions) > 0) {
3269 array_push($filters, $filter);
3276 function get_score_pic($score) {
3278 return "score_high.png";
3279 } else if ($score > 0) {
3280 return "score_half_high.png";
3281 } else if ($score < -100) {
3282 return "score_low.png";
3283 } else if ($score < 0) {
3284 return "score_half_low.png";
3286 return "score_neutral.png";
3290 function feed_has_icon($id) {
3291 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3294 function init_connection($link) {
3297 if (DB_TYPE
== "pgsql") {
3298 pg_query($link, "set client_encoding = 'UTF-8'");
3299 pg_set_client_encoding("UNICODE");
3300 pg_query($link, "set datestyle = 'ISO, european'");
3301 pg_query($link, "set TIME ZONE 0");
3303 db_query($link, "SET time_zone = '+0:0'");
3305 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3306 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3312 $pluginhost = new PluginHost($link);
3313 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3317 print "Unable to connect to database:" . db_last_error();
3322 function format_tags_string($tags, $id) {
3325 $tags_nolinks_str = "";
3331 $formatted_tags = array();
3333 foreach ($tags as $tag) {
3335 $tag_escaped = str_replace("'", "\\'", $tag);
3337 if (mb_strlen($tag) > 30) {
3338 $tag = truncate_string($tag, 30);
3341 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3343 array_push($formatted_tags, $tag_str);
3345 $tmp_tags_str = implode(", ", $formatted_tags);
3347 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3352 $tags_str = implode(", ", $formatted_tags);
3354 if ($num_tags < count($tags)) {
3355 $tags_str .= ", …";
3358 if ($num_tags == 0) {
3359 $tags_str = __("no tags");
3366 function format_article_labels($labels, $id) {
3370 foreach ($labels as $l) {
3371 $labels_str .= sprintf("<span class='hlLabelRef'
3372 style='color : %s; background-color : %s'>%s</span>",
3373 $l[2], $l[3], $l[1]);
3380 function format_article_note($id, $note, $allow_edit = true) {
3382 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3383 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3384 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3390 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3391 if ($parent_cat_id) {
3392 $parent_qpart = "parent_cat = '$parent_cat_id'";
3393 $parent_insert = "'$parent_cat_id'";
3395 $parent_qpart = "parent_cat IS NULL";
3396 $parent_insert = "NULL";
3399 $result = db_query($link,
3400 "SELECT id FROM ttrss_feed_categories
3401 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3403 if (db_num_rows($result) == 0) {
3406 return db_fetch_result($result, 0, "id");
3410 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3412 if (!$feed_cat) return false;
3414 db_query($link, "BEGIN");
3416 if ($parent_cat_id) {
3417 $parent_qpart = "parent_cat = '$parent_cat_id'";
3418 $parent_insert = "'$parent_cat_id'";
3420 $parent_qpart = "parent_cat IS NULL";
3421 $parent_insert = "NULL";
3424 $result = db_query($link,
3425 "SELECT id FROM ttrss_feed_categories
3426 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3428 if (db_num_rows($result) == 0) {
3430 $result = db_query($link,
3431 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3432 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3434 db_query($link, "COMMIT");
3442 function getArticleFeed($link, $id) {
3443 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3444 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3446 if (db_num_rows($result) != 0) {
3447 return db_fetch_result($result, 0, "feed_id");
3454 * Fixes incomplete URLs by prepending "http://".
3455 * Also replaces feed:// with http://, and
3456 * prepends a trailing slash if the url is a domain name only.
3458 * @param string $url Possibly incomplete URL
3460 * @return string Fixed URL.
3462 function fix_url($url) {
3463 if (strpos($url, '://') === false) {
3464 $url = 'http://' . $url;
3465 } else if (substr($url, 0, 5) == 'feed:') {
3466 $url = 'http:' . substr($url, 5);
3469 //prepend slash if the URL has no slash in it
3470 // "http://www.example" -> "http://www.example/"
3471 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3475 if ($url != "http:///")
3481 function validate_feed_url($url) {
3482 $parts = parse_url($url);
3484 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3488 function get_article_enclosures($link, $id) {
3490 $query = "SELECT * FROM ttrss_enclosures
3491 WHERE post_id = '$id' AND content_url != ''";
3495 $result = db_query($link, $query);
3497 if (db_num_rows($result) > 0) {
3498 while ($line = db_fetch_assoc($result)) {
3499 array_push($rv, $line);
3506 function save_email_address($link, $email) {
3507 // FIXME: implement persistent storage of emails
3509 if (!$_SESSION['stored_emails'])
3510 $_SESSION['stored_emails'] = array();
3512 if (!in_array($email, $_SESSION['stored_emails']))
3513 array_push($_SESSION['stored_emails'], $email);
3517 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3519 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3521 $sql_is_cat = bool_to_sql_bool($is_cat);
3523 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3524 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3525 AND owner_uid = " . $owner_uid);
3527 if (db_num_rows($result) == 1) {
3528 return db_fetch_result($result, 0, "access_key");
3530 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3532 $result = db_query($link, "INSERT INTO ttrss_access_keys
3533 (access_key, feed_id, is_cat, owner_uid)
3534 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3541 function get_feeds_from_html($url, $content)
3543 $url = fix_url($url);
3544 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3546 libxml_use_internal_errors(true);
3548 $doc = new DOMDocument();
3549 $doc->loadHTML($content);
3550 $xpath = new DOMXPath($doc);
3551 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3552 $feedUrls = array();
3553 foreach ($entries as $entry) {
3554 if ($entry->hasAttribute('href')) {
3555 $title = $entry->getAttribute('title');
3557 $title = $entry->getAttribute('type');
3559 $feedUrl = rewrite_relative_url(
3560 $baseUrl, $entry->getAttribute('href')
3562 $feedUrls[$feedUrl] = $title;
3568 function is_html($content) {
3569 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3572 function url_is_html($url, $login = false, $pass = false) {
3573 return is_html(fetch_file_contents($url, false, $login, $pass));
3576 function print_label_select($link, $name, $value, $attributes = "") {
3578 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3579 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3581 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3582 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3584 while ($line = db_fetch_assoc($result)) {
3586 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3588 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3589 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3593 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3600 function format_article_enclosures($link, $id, $always_display_enclosures,
3601 $article_content, $hide_images = false) {
3603 $result = get_article_enclosures($link, $id);
3606 if (count($result) > 0) {
3608 $entries_html = array();
3610 $entries_inline = array();
3612 foreach ($result as $line) {
3614 $url = $line["content_url"];
3615 $ctype = $line["content_type"];
3617 if (!$ctype) $ctype = __("unknown type");
3619 $filename = substr($url, strrpos($url, "/")+
1);
3621 $player = format_inline_player($link, $url, $ctype);
3623 if ($player) array_push($entries_inline, $player);
3625 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3626 # $filename . " (" . $ctype . ")" . "</a>";
3628 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3629 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3631 array_push($entries_html, $entry);
3635 $entry["type"] = $ctype;
3636 $entry["filename"] = $filename;
3637 $entry["url"] = $url;
3639 array_push($entries, $entry);
3642 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES")) {
3643 if ($always_display_enclosures ||
3644 !preg_match("/<img/i", $article_content)) {
3646 foreach ($entries as $entry) {
3648 if (preg_match("/image/", $entry["type"]) ||
3649 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3651 if (!$hide_images) {
3653 alt=\"".htmlspecialchars($entry["filename"])."\"
3654 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3656 $rv .= "<p><a target=\"_blank\"
3657 href=\"".htmlspecialchars($entry["url"])."\"
3658 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3666 if (count($entries_inline) > 0) {
3667 $rv .= "<hr clear='both'/>";
3668 foreach ($entries_inline as $entry) { $rv .= $entry; };
3669 $rv .= "<hr clear='both'/>";
3672 $rv .= "<br/><div dojoType=\"dijit.form.DropDownButton\">".
3673 "<span>" . __('Attachments')."</span>";
3674 $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
3676 foreach ($entries_html as $entry) { $rv .= $entry; };
3678 $rv .= "</div></div>";
3684 function getLastArticleId($link) {
3685 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3686 WHERE owner_uid = " . $_SESSION["uid"]);
3688 if (db_num_rows($result) == 1) {
3689 return db_fetch_result($result, 0, "id");
3695 function build_url($parts) {
3696 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3700 * Converts a (possibly) relative URL to a absolute one.
3702 * @param string $url Base URL (i.e. from where the document is)
3703 * @param string $rel_url Possibly relative URL in the document
3705 * @return string Absolute URL
3707 function rewrite_relative_url($url, $rel_url) {
3708 if (strpos($rel_url, "magnet:") === 0) {
3710 } else if (strpos($rel_url, "://") !== false) {
3712 } else if (strpos($rel_url, "//") === 0) {
3713 # protocol-relative URL (rare but they exist)
3715 } else if (strpos($rel_url, "/") === 0)
3717 $parts = parse_url($url);
3718 $parts['path'] = $rel_url;
3720 return build_url($parts);
3723 $parts = parse_url($url);
3724 if (!isset($parts['path'])) {
3725 $parts['path'] = '/';
3727 $dir = $parts['path'];
3728 if (substr($dir, -1) !== '/') {
3729 $dir = dirname($parts['path']);
3730 $dir !== '/' && $dir .= '/';
3732 $parts['path'] = $dir . $rel_url;
3734 return build_url($parts);
3738 function sphinx_search($query, $offset = 0, $limit = 30) {
3739 require_once 'lib/sphinxapi.php';
3741 $sphinxClient = new SphinxClient();
3743 $sphinxClient->SetServer('localhost', 9312);
3744 $sphinxClient->SetConnectTimeout(1);
3746 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3747 'feed_title' => 20));
3749 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3750 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3751 $sphinxClient->SetLimits($offset, $limit, 1000);
3752 $sphinxClient->SetArrayResult(false);
3753 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3755 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3759 if (is_array($result['matches'])) {
3760 foreach (array_keys($result['matches']) as $int_id) {
3761 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3762 array_push($ids, $ref_id);
3769 function cleanup_tags($link, $days = 14, $limit = 1000) {
3771 if (DB_TYPE
== "pgsql") {
3772 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3773 } else if (DB_TYPE
== "mysql") {
3774 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3779 while ($limit > 0) {
3782 $query = "SELECT ttrss_tags.id AS id
3783 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3784 WHERE post_int_id = int_id AND $interval_query AND
3785 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3787 $result = db_query($link, $query);
3791 while ($line = db_fetch_assoc($result)) {
3792 array_push($ids, $line['id']);
3795 if (count($ids) > 0) {
3796 $ids = join(",", $ids);
3799 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3800 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3805 $limit -= $limit_part;
3810 return $tags_deleted;
3813 function print_user_stylesheet($link) {
3814 $value = get_pref($link, 'USER_STYLESHEET');
3817 print "<style type=\"text/css\">";
3818 print str_replace("<br/>", "\n", $value);
3824 function rewrite_urls($html) {
3825 libxml_use_internal_errors(true);
3827 $charset_hack = '<head>
3828 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3831 $doc = new DOMDocument();
3832 $doc->loadHTML($charset_hack . $html);
3833 $xpath = new DOMXPath($doc);
3835 $entries = $xpath->query('//*/text()');
3837 foreach ($entries as $entry) {
3838 if (strstr($entry->wholeText
, "://") !== false) {
3839 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3840 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3842 if ($text != $entry->wholeText
) {
3843 $cdoc = new DOMDocument();
3844 $cdoc->loadHTML($charset_hack . $text);
3847 foreach ($cdoc->childNodes
as $cnode) {
3848 $cnode = $doc->importNode($cnode, true);
3851 $entry->parentNode
->insertBefore($cnode);
3855 $entry->parentNode
->removeChild($entry);
3861 $node = $doc->getElementsByTagName('body')->item(0);
3863 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3865 return $doc->saveXML($node);
3870 function filter_to_sql($link, $filter, $owner_uid) {
3873 if (DB_TYPE
== "pgsql")
3876 $reg_qpart = "REGEXP";
3878 foreach ($filter["rules"] AS $rule) {
3879 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3880 $rule['reg_exp']) !== FALSE;
3882 if ($regexp_valid) {
3884 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3886 switch ($rule["type"]) {
3888 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3889 $rule['reg_exp'] . "')";
3892 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3893 $rule['reg_exp'] . "')";
3896 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3897 $rule['reg_exp'] . "') OR LOWER(" .
3898 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3901 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3902 $rule['reg_exp'] . "')";
3905 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3906 $rule['reg_exp'] . "')";
3909 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3910 $rule['reg_exp'] . "')";
3914 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3916 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3917 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3920 if (isset($rule["cat_id"])) {
3922 if ($rule["cat_id"] > 0) {
3923 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3924 array_push($children, $rule["cat_id"]);
3926 $children = join(",", $children);
3928 $cat_qpart = "cat_id IN ($children)";
3930 $cat_qpart = "cat_id IS NULL";
3933 $qpart .= " AND $cat_qpart";
3936 array_push($query, "($qpart)");
3941 if (count($query) > 0) {
3942 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3944 $fullquery = "(false)";
3947 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3952 if (!function_exists('gzdecode')) {
3953 function gzdecode($string) { // no support for 2nd argument
3954 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3955 base64_encode($string));
3959 function get_random_bytes($length) {
3960 if (function_exists('openssl_random_pseudo_bytes')) {
3961 return openssl_random_pseudo_bytes($length);
3965 for ($i = 0; $i < $length; $i++
)
3966 $output .= chr(mt_rand(0, 255));
3972 function read_stdin() {
3973 $fp = fopen("php://stdin", "r");
3976 $line = trim(fgets($fp));
3984 function tmpdirname($path, $prefix) {
3985 // Use PHP's tmpfile function to create a temporary
3986 // directory name. Delete the file and keep the name.
3987 $tempname = tempnam($path,$prefix);
3991 if (!unlink($tempname))
3997 function getFeedCategory($link, $feed) {
3998 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3999 WHERE id = '$feed'");
4001 if (db_num_rows($result) > 0) {
4002 return db_fetch_result($result, 0, "cat_id");
4009 function implements_interface($class, $interface) {
4010 return in_array($interface, class_implements($class));
4013 function geturl($url){
4015 (function_exists('curl_init')) ?
'' : die('cURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini');
4017 $curl = curl_init();
4018 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4019 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4020 $header[] = "Cache-Control: max-age=0";
4021 $header[] = "Connection: keep-alive";
4022 $header[] = "Keep-Alive: 300";
4023 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4024 $header[] = "Accept-Language: en-us,en;q=0.5";
4025 $header[] = "Pragma: ";
4027 curl_setopt($curl, CURLOPT_URL
, $url);
4028 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4029 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4030 curl_setopt($curl, CURLOPT_HEADER
, true);
4031 curl_setopt($curl, CURLOPT_REFERER
, $url);
4032 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4033 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4034 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4035 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4036 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4038 $html = curl_exec($curl);
4040 $status = curl_getinfo($curl);
4043 if($status['http_code']!=200){
4044 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4045 list($header) = explode("\r\n\r\n", $html, 2);
4047 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4048 $url = trim(str_replace($matches[1],"",$matches[0]));
4049 $url_parsed = parse_url($url);
4050 return (isset($url_parsed))?
geturl($url, $referer):'';
4053 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4054 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4055 # $handle = @fopen('./curl.error.log', 'a');
4056 # fwrite($handle, $line);
4062 function get_minified_js($files) {
4063 require_once 'lib/jshrink/Minifier.php';
4067 foreach ($files as $js) {
4068 if (!isset($_GET['debug'])) {
4069 $cached_file = CACHE_DIR
. "/js/$js.js";
4071 if (file_exists($cached_file) &&
4072 is_readable($cached_file) &&
4073 filemtime($cached_file) >= filemtime("js/$js.js")) {
4075 $rv .= file_get_contents($cached_file);
4078 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4079 file_put_contents($cached_file, $minified);
4083 $rv .= file_get_contents("js/$js.js");
4090 function stylesheet_tag($filename) {
4091 $timestamp = filemtime($filename);
4093 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4096 function javascript_tag($filename) {
4099 if (!(strpos($filename, "?") === FALSE)) {
4100 $query = substr($filename, strpos($filename, "?")+
1);
4101 $filename = substr($filename, 0, strpos($filename, "?"));
4104 $timestamp = filemtime($filename);
4106 if ($query) $timestamp .= "&$query";
4108 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4111 function calculate_dep_timestamp() {
4112 $files = array_merge(glob("js/*.js"), glob("*.css"));
4116 foreach ($files as $file) {
4117 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4123 function T_js_decl($s1, $s2) {
4125 $s1 = preg_replace("/\n/", "", $s1);
4126 $s2 = preg_replace("/\n/", "", $s2);
4128 $s1 = preg_replace("/\"/", "\\\"", $s1);
4129 $s2 = preg_replace("/\"/", "\\\"", $s2);
4131 return "T_messages[\"$s1\"] = \"$s2\";\n";
4135 function init_js_translations() {
4137 print 'var T_messages = new Object();
4140 if (T_messages[msg]) {
4141 return T_messages[msg];
4147 function ngettext(msg1, msg2, n) {
4148 return (parseInt(n) > 1) ? msg2 : msg1;
4151 $l10n = _get_reader();
4153 for ($i = 0; $i < $l10n->total
; $i++
) {
4154 $orig = $l10n->get_original_string($i);
4155 $translation = __($orig);
4157 print T_js_decl($orig, $translation);
4161 function label_to_feed_id($label) {
4162 return LABEL_BASE_INDEX
- 1 - abs($label);
4165 function feed_to_label_id($feed) {
4166 return LABEL_BASE_INDEX
- 1 +
abs($feed);