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, updated";
2346 $override_order = "last_marked DESC, date_entered DESC, updated 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, updated";
2361 $override_order = "last_published DESC, date_entered DESC, updated 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 date_entered > NOW() - INTERVAL '$intl hour' ";
2388 $query_strategy_part .= " AND date_entered > 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, updated";
2419 $order_by = "$date_sort_field DESC, updated 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,
2510 ttrss_user_entries.ref_id = ttrss_entries.id AND
2511 ttrss_user_entries.owner_uid = '$owner_uid' AND
2516 $query_strategy_part ORDER BY $order_by
2517 $limit_query_part $offset_query_part";
2519 if ($_REQUEST["debug"]) print $query;
2521 $result = db_query($link, $query);
2526 $select_qpart = "SELECT DISTINCT " .
2530 "ttrss_entries.id as id," .
2543 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2544 "last_marked, last_published, " .
2547 $content_query_part .
2550 $feed_kind = "Tags";
2551 $all_tags = explode(",", $feed);
2552 if ($search_mode == 'any') {
2553 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2554 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2555 $where_qpart = " WHERE " .
2556 "ref_id = ttrss_entries.id AND " .
2557 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2558 "post_int_id = int_id AND $tag_sql AND " .
2560 $search_query_part .
2561 $query_strategy_part . " ORDER BY $order_by " .
2566 $sub_selects = array();
2567 $sub_ands = array();
2568 foreach ($all_tags as $term) {
2569 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");
2576 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2581 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2582 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2583 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2584 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2586 // error_log("TAG SQL: " . $tag_sql);
2587 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2589 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2590 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2593 return array($result, $feed_title, $feed_site_url, $last_error);
2597 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2598 if (!$owner) $owner = $_SESSION["uid"];
2600 $res = trim($str); if (!$res) return '';
2602 if (strpos($res, "href=") === false)
2603 $res = rewrite_urls($res);
2605 $charset_hack = '<head>
2606 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2609 $res = trim($res); if (!$res) return '';
2611 libxml_use_internal_errors(true);
2613 $doc = new DOMDocument();
2614 $doc->loadHTML($charset_hack . $res);
2615 $xpath = new DOMXPath($doc);
2617 $entries = $xpath->query('(//a[@href]|//img[@src])');
2619 foreach ($entries as $entry) {
2623 if ($entry->hasAttribute('href'))
2624 $entry->setAttribute('href',
2625 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2627 if ($entry->hasAttribute('src')) {
2628 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2630 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2632 if (file_exists($cached_filename)) {
2633 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2636 $entry->setAttribute('src', $src);
2639 if ($entry->nodeName
== 'img') {
2640 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2641 $force_remove_images) {
2643 $p = $doc->createElement('p');
2645 $a = $doc->createElement('a');
2646 $a->setAttribute('href', $entry->getAttribute('src'));
2648 $a->appendChild(new DOMText($entry->getAttribute('src')));
2649 $a->setAttribute('target', '_blank');
2651 $p->appendChild($a);
2653 $entry->parentNode
->replaceChild($p, $entry);
2658 if (strtolower($entry->nodeName
) == "a") {
2659 $entry->setAttribute("target", "_blank");
2663 $entries = $xpath->query('//iframe');
2664 foreach ($entries as $entry) {
2665 $entry->setAttribute('sandbox', 'allow-scripts');
2671 if (isset($pluginhost)) {
2672 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2673 $doc = $plugin->hook_sanitize($doc, $site_url);
2677 $doc->removeChild($doc->firstChild
); //remove doctype
2678 $doc = strip_harmful_tags($doc);
2679 $res = $doc->saveHTML();
2683 function strip_harmful_tags($doc) {
2684 $entries = $doc->getElementsByTagName("*");
2686 $allowed_elements = array('a', 'address', 'audio', 'article',
2687 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2688 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2689 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2690 'header', 'html', 'i', 'img', 'ins', 'kbd',
2691 'li', 'nav', 'ol', 'p', 'pre', 'q', 's','small',
2692 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2693 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2694 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2696 if ($_SESSION['hasSandbox']) array_push($allowed_elements, 'iframe');
2698 $disallowed_attributes = array('id', 'style', 'class');
2700 foreach ($entries as $entry) {
2701 if (!in_array($entry->nodeName
, $allowed_elements)) {
2702 $entry->parentNode
->removeChild($entry);
2705 if ($entry->hasAttributes()) {
2706 $attrs_to_remove = array();
2708 foreach ($entry->attributes
as $attr) {
2710 if (strpos($attr->nodeName
, 'on') === 0) {
2711 array_push($attrs_to_remove, $attr);
2714 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2715 array_push($attrs_to_remove, $attr);
2719 foreach ($attrs_to_remove as $attr) {
2720 $entry->removeAttributeNode($attr);
2728 function check_for_update($link) {
2729 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2730 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2731 "&iid=" . sha1(SELF_URL_PATH
);
2733 $version_data = @fetch_file_contents
($version_url);
2735 if ($version_data) {
2736 $version_data = json_decode($version_data, true);
2737 if ($version_data && $version_data['version']) {
2739 if (version_compare(VERSION
, $version_data['version']) == -1) {
2740 return $version_data;
2748 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2750 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2751 if (count($ids) == 0) return;
2755 foreach ($ids as $id) {
2756 array_push($tmp_ids, "ref_id = '$id'");
2759 $ids_qpart = join(" OR ", $tmp_ids);
2762 db_query($link, "UPDATE ttrss_user_entries SET
2763 unread = false,last_read = NOW()
2764 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2765 } else if ($cmode == 1) {
2766 db_query($link, "UPDATE ttrss_user_entries SET
2768 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2770 db_query($link, "UPDATE ttrss_user_entries SET
2771 unread = NOT unread,last_read = NOW()
2772 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2777 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2778 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2780 while ($line = db_fetch_assoc($result)) {
2781 ccache_update($link, $line["feed_id"], $owner_uid);
2785 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2787 $a_id = db_escape_string($link, $id);
2789 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2791 $query = "SELECT DISTINCT tag_name,
2792 owner_uid as owner FROM
2793 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2794 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2796 $obj_id = md5("TAGS:$owner_uid:$id");
2799 /* check cache first */
2801 if ($tag_cache === false) {
2802 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2803 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2805 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2809 $tags = explode(",", $tag_cache);
2812 /* do it the hard way */
2814 $tmp_result = db_query($link, $query);
2816 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2817 array_push($tags, $tmp_line["tag_name"]);
2820 /* update the cache */
2822 $tags_str = db_escape_string($link, join(",", $tags));
2824 db_query($link, "UPDATE ttrss_user_entries
2825 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2826 AND owner_uid = $owner_uid");
2832 function trim_array($array) {
2834 array_walk($tmp, 'trim');
2838 function tag_is_valid($tag) {
2839 if ($tag == '') return false;
2840 if (preg_match("/^[0-9]*$/", $tag)) return false;
2841 if (mb_strlen($tag) > 250) return false;
2843 if (function_exists('iconv')) {
2844 $tag = iconv("utf-8", "utf-8", $tag);
2847 if (!$tag) return false;
2852 function render_login_form($link) {
2853 require_once "login_form.php";
2857 // from http://developer.apple.com/internet/safari/faq.html
2858 function no_cache_incantation() {
2859 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2860 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2861 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2862 header("Cache-Control: post-check=0, pre-check=0", false);
2863 header("Pragma: no-cache"); // HTTP/1.0
2866 function format_warning($msg, $id = "") {
2868 return "<div class=\"warning\" id=\"$id\">
2869 <img src=\"images/sign_excl.svg\">$msg</div>";
2872 function format_notice($msg, $id = "") {
2874 return "<div class=\"notice\" id=\"$id\">
2875 <img src=\"images/sign_info.svg\">$msg</div>";
2878 function format_error($msg, $id = "") {
2880 return "<div class=\"error\" id=\"$id\">
2881 <img src=\"images/sign_excl.svg\">$msg</div>";
2884 function print_notice($msg) {
2885 return print format_notice($msg);
2888 function print_warning($msg) {
2889 return print format_warning($msg);
2892 function print_error($msg) {
2893 return print format_error($msg);
2897 function T_sprintf() {
2898 $args = func_get_args();
2899 return vsprintf(__(array_shift($args)), $args);
2902 function format_inline_player($link, $url, $ctype) {
2906 $url = htmlspecialchars($url);
2908 if (strpos($ctype, "audio/") === 0) {
2910 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2911 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2912 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2914 $id = 'AUDIO-' . uniqid();
2916 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2917 <source type=\"$ctype\" src=\"$url\"></source>
2920 $entry .= "<span onclick=\"player(this)\"
2921 title=\"".__("Click to play")."\" status=\"0\"
2922 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2926 $entry .= "<object type=\"application/x-shockwave-flash\"
2927 data=\"lib/button/musicplayer.swf?song_url=$url\"
2928 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2929 <param name=\"movie\"
2930 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2934 if ($entry) $entry .= " <a target=\"_blank\"
2935 href=\"$url\">" . basename($url) . "</a>";
2943 /* $filename = substr($url, strrpos($url, "/")+1);
2945 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2946 $filename . " (" . $ctype . ")" . "</a>"; */
2950 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2951 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2957 /* we can figure out feed_id from article id anyway, why do we
2958 * pass feed_id here? let's ignore the argument :( */
2960 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2961 WHERE ref_id = '$id'");
2963 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2965 $rv['feed_id'] = $feed_id;
2967 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2969 if ($mark_as_read) {
2970 $result = db_query($link, "UPDATE ttrss_user_entries
2971 SET unread = false,last_read = NOW()
2972 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2974 ccache_update($link, $feed_id, $owner_uid);
2977 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2978 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2979 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2980 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2987 FROM ttrss_entries,ttrss_user_entries
2988 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2992 $line = db_fetch_assoc($result);
2994 $tag_cache = $line["tag_cache"];
2996 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
2997 unset($line["tag_cache"]);
2999 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3003 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
3004 $line = $p->hook_render_article($line);
3007 $num_comments = $line["num_comments"];
3008 $entry_comments = "";
3010 if ($num_comments > 0) {
3011 if ($line["comments"]) {
3012 $comments_url = htmlspecialchars($line["comments"]);
3014 $comments_url = htmlspecialchars($line["link"]);
3016 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3018 if ($line["comments"] && $line["link"] != $line["comments"]) {
3019 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3024 header("Content-Type: text/html");
3025 $rv['content'] .= "<html><head>
3026 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3027 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3028 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3029 </head><body id=\"ttrssZoom\">";
3032 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3034 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3036 $entry_author = $line["author"];
3038 if ($entry_author) {
3039 $entry_author = __(" - ") . $entry_author;
3042 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3045 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3047 if ($line["link"]) {
3048 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3049 title=\"".htmlspecialchars($line['title'])."\"
3051 htmlspecialchars($line["link"]) . "\">" .
3052 $line["title"] . "</a>" .
3053 "<span class='author'>$entry_author</span></div>";
3055 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3058 $tags_str = format_tags_string($line["tags"], $id);
3059 $tags_str_full = join(", ", $line["tags"]);
3061 if (!$tags_str_full) $tags_str_full = __("no tags");
3063 if (!$entry_comments) $entry_comments = " "; # placeholder
3065 $rv['content'] .= "<div class='postTags' style='float : right'>
3066 <img src='images/tag.png'
3067 class='tagsPic' alt='Tags' title='Tags'> ";
3070 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3071 <a title=\"".__('Edit tags for this article')."\"
3072 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3074 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3075 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3076 position=\"below\">$tags_str_full</div>";
3080 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3081 $rv['content'] .= $p->hook_article_button($line);
3086 $tags_str = strip_tags($tags_str);
3087 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3089 $rv['content'] .= "</div>";
3090 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3092 if ($line["orig_feed_id"]) {
3094 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3095 WHERE id = ".$line["orig_feed_id"]);
3097 if (db_num_rows($tmp_result) != 0) {
3099 $rv['content'] .= "<div clear='both'>";
3100 $rv['content'] .= __("Originally from:");
3102 $rv['content'] .= " ";
3104 $tmp_line = db_fetch_assoc($tmp_result);
3106 $rv['content'] .= "<a target='_blank'
3107 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3108 $tmp_line['title'] . "</a>";
3110 $rv['content'] .= " ";
3112 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3113 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3115 $rv['content'] .= "</div>";
3119 $rv['content'] .= "</div>";
3121 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3122 if ($line['note']) {
3123 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3125 $rv['content'] .= "</div>";
3127 $rv['content'] .= "<div class=\"postContent\">";
3129 $rv['content'] .= $line["content"];
3131 $rv['content'] .= format_article_enclosures($link, $id,
3132 $always_display_enclosures, $line["content"], $line["hide_images"]);
3134 $rv['content'] .= "</div>";
3136 $rv['content'] .= "</div>";
3142 <div class='footer'>
3143 <button onclick=\"return window.close()\">".
3144 __("Close this window")."</button></div>";
3145 $rv['content'] .= "</body></html>";
3152 function print_checkpoint($n, $s) {
3153 $ts = microtime(true);
3154 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3158 function sanitize_tag($tag) {
3161 $tag = mb_strtolower($tag, 'utf-8');
3163 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3165 // $tag = str_replace('"', "", $tag);
3166 // $tag = str_replace("+", " ", $tag);
3167 $tag = str_replace("technorati tag: ", "", $tag);
3172 function get_self_url_prefix() {
3173 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3174 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3176 return SELF_URL_PATH
;
3181 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3183 * @return string The Mozilla Firefox feed adding URL.
3185 function add_feed_url() {
3186 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3188 $url_path = get_self_url_prefix() .
3189 "/public.php?op=subscribe&feed_url=%s";
3191 } // function add_feed_url
3193 function encrypt_password($pass, $salt = '', $mode2 = false) {
3194 if ($salt && $mode2) {
3195 return "MODE2:" . hash('sha256', $salt . $pass);
3197 return "SHA1X:" . sha1("$salt:$pass");
3199 return "SHA1:" . sha1($pass);
3201 } // function encrypt_password
3203 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3206 $cat_id = (int)getFeedCategory($link, $feed_id);
3208 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3209 owner_uid = $owner_uid AND enabled = true");
3211 $check_cats = join(",", array_merge(
3212 getParentCategories($link, $cat_id, $owner_uid),
3215 while ($line = db_fetch_assoc($result)) {
3216 $filter_id = $line["id"];
3218 $result2 = db_query($link, "SELECT
3219 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3220 FROM ttrss_filters2_rules AS r,
3221 ttrss_filter_types AS t
3223 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3224 (feed_id IS NULL OR feed_id = '$feed_id') AND
3225 filter_type = t.id AND filter_id = '$filter_id'");
3230 while ($rule_line = db_fetch_assoc($result2)) {
3231 # print_r($rule_line);
3234 $rule["reg_exp"] = $rule_line["reg_exp"];
3235 $rule["type"] = $rule_line["type_name"];
3236 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3238 array_push($rules, $rule);
3241 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3242 FROM ttrss_filters2_actions AS a,
3243 ttrss_filter_actions AS t
3245 action_id = t.id AND filter_id = '$filter_id'");
3247 while ($action_line = db_fetch_assoc($result2)) {
3248 # print_r($action_line);
3251 $action["type"] = $action_line["type_name"];
3252 $action["param"] = $action_line["action_param"];
3254 array_push($actions, $action);
3259 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3260 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3261 $filter["rules"] = $rules;
3262 $filter["actions"] = $actions;
3264 if (count($rules) > 0 && count($actions) > 0) {
3265 array_push($filters, $filter);
3272 function get_score_pic($score) {
3274 return "score_high.png";
3275 } else if ($score > 0) {
3276 return "score_half_high.png";
3277 } else if ($score < -100) {
3278 return "score_low.png";
3279 } else if ($score < 0) {
3280 return "score_half_low.png";
3282 return "score_neutral.png";
3286 function feed_has_icon($id) {
3287 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3290 function init_connection($link) {
3293 if (DB_TYPE
== "pgsql") {
3294 pg_query($link, "set client_encoding = 'UTF-8'");
3295 pg_set_client_encoding("UNICODE");
3296 pg_query($link, "set datestyle = 'ISO, european'");
3297 pg_query($link, "set TIME ZONE 0");
3299 db_query($link, "SET time_zone = '+0:0'");
3301 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3302 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3308 $pluginhost = new PluginHost($link);
3309 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3313 print "Unable to connect to database:" . db_last_error();
3318 function format_tags_string($tags, $id) {
3321 $tags_nolinks_str = "";
3327 $formatted_tags = array();
3329 foreach ($tags as $tag) {
3331 $tag_escaped = str_replace("'", "\\'", $tag);
3333 if (mb_strlen($tag) > 30) {
3334 $tag = truncate_string($tag, 30);
3337 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3339 array_push($formatted_tags, $tag_str);
3341 $tmp_tags_str = implode(", ", $formatted_tags);
3343 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3348 $tags_str = implode(", ", $formatted_tags);
3350 if ($num_tags < count($tags)) {
3351 $tags_str .= ", …";
3354 if ($num_tags == 0) {
3355 $tags_str = __("no tags");
3362 function format_article_labels($labels, $id) {
3366 foreach ($labels as $l) {
3367 $labels_str .= sprintf("<span class='hlLabelRef'
3368 style='color : %s; background-color : %s'>%s</span>",
3369 $l[2], $l[3], $l[1]);
3376 function format_article_note($id, $note, $allow_edit = true) {
3378 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3379 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3380 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3386 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3387 if ($parent_cat_id) {
3388 $parent_qpart = "parent_cat = '$parent_cat_id'";
3389 $parent_insert = "'$parent_cat_id'";
3391 $parent_qpart = "parent_cat IS NULL";
3392 $parent_insert = "NULL";
3395 $result = db_query($link,
3396 "SELECT id FROM ttrss_feed_categories
3397 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3399 if (db_num_rows($result) == 0) {
3402 return db_fetch_result($result, 0, "id");
3406 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3408 if (!$feed_cat) return false;
3410 db_query($link, "BEGIN");
3412 if ($parent_cat_id) {
3413 $parent_qpart = "parent_cat = '$parent_cat_id'";
3414 $parent_insert = "'$parent_cat_id'";
3416 $parent_qpart = "parent_cat IS NULL";
3417 $parent_insert = "NULL";
3420 $result = db_query($link,
3421 "SELECT id FROM ttrss_feed_categories
3422 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3424 if (db_num_rows($result) == 0) {
3426 $result = db_query($link,
3427 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3428 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3430 db_query($link, "COMMIT");
3438 function getArticleFeed($link, $id) {
3439 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3440 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3442 if (db_num_rows($result) != 0) {
3443 return db_fetch_result($result, 0, "feed_id");
3450 * Fixes incomplete URLs by prepending "http://".
3451 * Also replaces feed:// with http://, and
3452 * prepends a trailing slash if the url is a domain name only.
3454 * @param string $url Possibly incomplete URL
3456 * @return string Fixed URL.
3458 function fix_url($url) {
3459 if (strpos($url, '://') === false) {
3460 $url = 'http://' . $url;
3461 } else if (substr($url, 0, 5) == 'feed:') {
3462 $url = 'http:' . substr($url, 5);
3465 //prepend slash if the URL has no slash in it
3466 // "http://www.example" -> "http://www.example/"
3467 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3471 if ($url != "http:///")
3477 function validate_feed_url($url) {
3478 $parts = parse_url($url);
3480 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3484 function get_article_enclosures($link, $id) {
3486 $query = "SELECT * FROM ttrss_enclosures
3487 WHERE post_id = '$id' AND content_url != ''";
3491 $result = db_query($link, $query);
3493 if (db_num_rows($result) > 0) {
3494 while ($line = db_fetch_assoc($result)) {
3495 array_push($rv, $line);
3502 function save_email_address($link, $email) {
3503 // FIXME: implement persistent storage of emails
3505 if (!$_SESSION['stored_emails'])
3506 $_SESSION['stored_emails'] = array();
3508 if (!in_array($email, $_SESSION['stored_emails']))
3509 array_push($_SESSION['stored_emails'], $email);
3513 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3515 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3517 $sql_is_cat = bool_to_sql_bool($is_cat);
3519 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3520 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3521 AND owner_uid = " . $owner_uid);
3523 if (db_num_rows($result) == 1) {
3524 return db_fetch_result($result, 0, "access_key");
3526 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3528 $result = db_query($link, "INSERT INTO ttrss_access_keys
3529 (access_key, feed_id, is_cat, owner_uid)
3530 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3537 function get_feeds_from_html($url, $content)
3539 $url = fix_url($url);
3540 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3542 libxml_use_internal_errors(true);
3544 $doc = new DOMDocument();
3545 $doc->loadHTML($content);
3546 $xpath = new DOMXPath($doc);
3547 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3548 $feedUrls = array();
3549 foreach ($entries as $entry) {
3550 if ($entry->hasAttribute('href')) {
3551 $title = $entry->getAttribute('title');
3553 $title = $entry->getAttribute('type');
3555 $feedUrl = rewrite_relative_url(
3556 $baseUrl, $entry->getAttribute('href')
3558 $feedUrls[$feedUrl] = $title;
3564 function is_html($content) {
3565 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3568 function url_is_html($url, $login = false, $pass = false) {
3569 return is_html(fetch_file_contents($url, false, $login, $pass));
3572 function print_label_select($link, $name, $value, $attributes = "") {
3574 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3575 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3577 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3578 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3580 while ($line = db_fetch_assoc($result)) {
3582 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3584 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3585 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3589 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3596 function format_article_enclosures($link, $id, $always_display_enclosures,
3597 $article_content, $hide_images = false) {
3599 $result = get_article_enclosures($link, $id);
3602 if (count($result) > 0) {
3604 $entries_html = array();
3606 $entries_inline = array();
3608 foreach ($result as $line) {
3610 $url = $line["content_url"];
3611 $ctype = $line["content_type"];
3613 if (!$ctype) $ctype = __("unknown type");
3615 $filename = substr($url, strrpos($url, "/")+
1);
3617 $player = format_inline_player($link, $url, $ctype);
3619 if ($player) array_push($entries_inline, $player);
3621 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3622 # $filename . " (" . $ctype . ")" . "</a>";
3624 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3625 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3627 array_push($entries_html, $entry);
3631 $entry["type"] = $ctype;
3632 $entry["filename"] = $filename;
3633 $entry["url"] = $url;
3635 array_push($entries, $entry);
3638 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES")) {
3639 if ($always_display_enclosures ||
3640 !preg_match("/<img/i", $article_content)) {
3642 foreach ($entries as $entry) {
3644 if (preg_match("/image/", $entry["type"]) ||
3645 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3647 if (!$hide_images) {
3649 alt=\"".htmlspecialchars($entry["filename"])."\"
3650 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3652 $rv .= "<p><a target=\"_blank\"
3653 href=\"".htmlspecialchars($entry["url"])."\"
3654 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3662 if (count($entries_inline) > 0) {
3663 $rv .= "<hr clear='both'/>";
3664 foreach ($entries_inline as $entry) { $rv .= $entry; };
3665 $rv .= "<hr clear='both'/>";
3668 $rv .= "<br/><div dojoType=\"dijit.form.DropDownButton\">".
3669 "<span>" . __('Attachments')."</span>";
3670 $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
3672 foreach ($entries_html as $entry) { $rv .= $entry; };
3674 $rv .= "</div></div>";
3680 function getLastArticleId($link) {
3681 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3682 WHERE owner_uid = " . $_SESSION["uid"]);
3684 if (db_num_rows($result) == 1) {
3685 return db_fetch_result($result, 0, "id");
3691 function build_url($parts) {
3692 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3696 * Converts a (possibly) relative URL to a absolute one.
3698 * @param string $url Base URL (i.e. from where the document is)
3699 * @param string $rel_url Possibly relative URL in the document
3701 * @return string Absolute URL
3703 function rewrite_relative_url($url, $rel_url) {
3704 if (strpos($rel_url, "magnet:") === 0) {
3706 } else if (strpos($rel_url, "://") !== false) {
3708 } else if (strpos($rel_url, "//") === 0) {
3709 # protocol-relative URL (rare but they exist)
3711 } else if (strpos($rel_url, "/") === 0)
3713 $parts = parse_url($url);
3714 $parts['path'] = $rel_url;
3716 return build_url($parts);
3719 $parts = parse_url($url);
3720 if (!isset($parts['path'])) {
3721 $parts['path'] = '/';
3723 $dir = $parts['path'];
3724 if (substr($dir, -1) !== '/') {
3725 $dir = dirname($parts['path']);
3726 $dir !== '/' && $dir .= '/';
3728 $parts['path'] = $dir . $rel_url;
3730 return build_url($parts);
3734 function sphinx_search($query, $offset = 0, $limit = 30) {
3735 require_once 'lib/sphinxapi.php';
3737 $sphinxClient = new SphinxClient();
3739 $sphinxClient->SetServer('localhost', 9312);
3740 $sphinxClient->SetConnectTimeout(1);
3742 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3743 'feed_title' => 20));
3745 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3746 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3747 $sphinxClient->SetLimits($offset, $limit, 1000);
3748 $sphinxClient->SetArrayResult(false);
3749 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3751 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3755 if (is_array($result['matches'])) {
3756 foreach (array_keys($result['matches']) as $int_id) {
3757 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3758 array_push($ids, $ref_id);
3765 function cleanup_tags($link, $days = 14, $limit = 1000) {
3767 if (DB_TYPE
== "pgsql") {
3768 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3769 } else if (DB_TYPE
== "mysql") {
3770 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3775 while ($limit > 0) {
3778 $query = "SELECT ttrss_tags.id AS id
3779 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3780 WHERE post_int_id = int_id AND $interval_query AND
3781 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3783 $result = db_query($link, $query);
3787 while ($line = db_fetch_assoc($result)) {
3788 array_push($ids, $line['id']);
3791 if (count($ids) > 0) {
3792 $ids = join(",", $ids);
3795 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3796 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3801 $limit -= $limit_part;
3806 return $tags_deleted;
3809 function print_user_stylesheet($link) {
3810 $value = get_pref($link, 'USER_STYLESHEET');
3813 print "<style type=\"text/css\">";
3814 print str_replace("<br/>", "\n", $value);
3820 function rewrite_urls($html) {
3821 libxml_use_internal_errors(true);
3823 $charset_hack = '<head>
3824 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3827 $doc = new DOMDocument();
3828 $doc->loadHTML($charset_hack . $html);
3829 $xpath = new DOMXPath($doc);
3831 $entries = $xpath->query('//*/text()');
3833 foreach ($entries as $entry) {
3834 if (strstr($entry->wholeText
, "://") !== false) {
3835 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3836 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3838 if ($text != $entry->wholeText
) {
3839 $cdoc = new DOMDocument();
3840 $cdoc->loadHTML($charset_hack . $text);
3843 foreach ($cdoc->childNodes
as $cnode) {
3844 $cnode = $doc->importNode($cnode, true);
3847 $entry->parentNode
->insertBefore($cnode);
3851 $entry->parentNode
->removeChild($entry);
3857 $node = $doc->getElementsByTagName('body')->item(0);
3859 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3861 return $doc->saveXML($node);
3866 function filter_to_sql($link, $filter, $owner_uid) {
3869 if (DB_TYPE
== "pgsql")
3872 $reg_qpart = "REGEXP";
3874 foreach ($filter["rules"] AS $rule) {
3875 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3876 $rule['reg_exp']) !== FALSE;
3878 if ($regexp_valid) {
3880 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3882 switch ($rule["type"]) {
3884 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3885 $rule['reg_exp'] . "')";
3888 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3889 $rule['reg_exp'] . "')";
3892 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3893 $rule['reg_exp'] . "') OR LOWER(" .
3894 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3897 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3898 $rule['reg_exp'] . "')";
3901 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3902 $rule['reg_exp'] . "')";
3905 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3906 $rule['reg_exp'] . "')";
3910 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3912 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3913 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3916 if (isset($rule["cat_id"])) {
3918 if ($rule["cat_id"] > 0) {
3919 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3920 array_push($children, $rule["cat_id"]);
3922 $children = join(",", $children);
3924 $cat_qpart = "cat_id IN ($children)";
3926 $cat_qpart = "cat_id IS NULL";
3929 $qpart .= " AND $cat_qpart";
3932 array_push($query, "($qpart)");
3937 if (count($query) > 0) {
3938 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3940 $fullquery = "(false)";
3943 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3948 if (!function_exists('gzdecode')) {
3949 function gzdecode($string) { // no support for 2nd argument
3950 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3951 base64_encode($string));
3955 function get_random_bytes($length) {
3956 if (function_exists('openssl_random_pseudo_bytes')) {
3957 return openssl_random_pseudo_bytes($length);
3961 for ($i = 0; $i < $length; $i++
)
3962 $output .= chr(mt_rand(0, 255));
3968 function read_stdin() {
3969 $fp = fopen("php://stdin", "r");
3972 $line = trim(fgets($fp));
3980 function tmpdirname($path, $prefix) {
3981 // Use PHP's tmpfile function to create a temporary
3982 // directory name. Delete the file and keep the name.
3983 $tempname = tempnam($path,$prefix);
3987 if (!unlink($tempname))
3993 function getFeedCategory($link, $feed) {
3994 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3995 WHERE id = '$feed'");
3997 if (db_num_rows($result) > 0) {
3998 return db_fetch_result($result, 0, "cat_id");
4005 function implements_interface($class, $interface) {
4006 return in_array($interface, class_implements($class));
4009 function geturl($url){
4011 (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');
4013 $curl = curl_init();
4014 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4015 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4016 $header[] = "Cache-Control: max-age=0";
4017 $header[] = "Connection: keep-alive";
4018 $header[] = "Keep-Alive: 300";
4019 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4020 $header[] = "Accept-Language: en-us,en;q=0.5";
4021 $header[] = "Pragma: ";
4023 curl_setopt($curl, CURLOPT_URL
, $url);
4024 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4025 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4026 curl_setopt($curl, CURLOPT_HEADER
, true);
4027 curl_setopt($curl, CURLOPT_REFERER
, $url);
4028 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4029 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4030 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4031 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4032 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4034 $html = curl_exec($curl);
4036 $status = curl_getinfo($curl);
4039 if($status['http_code']!=200){
4040 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4041 list($header) = explode("\r\n\r\n", $html, 2);
4043 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4044 $url = trim(str_replace($matches[1],"",$matches[0]));
4045 $url_parsed = parse_url($url);
4046 return (isset($url_parsed))?
geturl($url, $referer):'';
4049 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4050 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4051 # $handle = @fopen('./curl.error.log', 'a');
4052 # fwrite($handle, $line);
4058 function get_minified_js($files) {
4059 require_once 'lib/jshrink/Minifier.php';
4063 foreach ($files as $js) {
4064 if (!isset($_GET['debug'])) {
4065 $cached_file = CACHE_DIR
. "/js/$js.js";
4067 if (file_exists($cached_file) &&
4068 is_readable($cached_file) &&
4069 filemtime($cached_file) >= filemtime("js/$js.js")) {
4071 $rv .= file_get_contents($cached_file);
4074 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4075 file_put_contents($cached_file, $minified);
4079 $rv .= file_get_contents("js/$js.js");
4086 function stylesheet_tag($filename) {
4087 $timestamp = filemtime($filename);
4089 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4092 function javascript_tag($filename) {
4095 if (!(strpos($filename, "?") === FALSE)) {
4096 $query = substr($filename, strpos($filename, "?")+
1);
4097 $filename = substr($filename, 0, strpos($filename, "?"));
4100 $timestamp = filemtime($filename);
4102 if ($query) $timestamp .= "&$query";
4104 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4107 function calculate_dep_timestamp() {
4108 $files = array_merge(glob("js/*.js"), glob("*.css"));
4112 foreach ($files as $file) {
4113 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4119 function T_js_decl($s1, $s2) {
4121 $s1 = preg_replace("/\n/", "", $s1);
4122 $s2 = preg_replace("/\n/", "", $s2);
4124 $s1 = preg_replace("/\"/", "\\\"", $s1);
4125 $s2 = preg_replace("/\"/", "\\\"", $s2);
4127 return "T_messages[\"$s1\"] = \"$s2\";\n";
4131 function init_js_translations() {
4133 print 'var T_messages = new Object();
4136 if (T_messages[msg]) {
4137 return T_messages[msg];
4143 function ngettext(msg1, msg2, n) {
4144 return (parseInt(n) > 1) ? msg2 : msg1;
4147 $l10n = _get_reader();
4149 for ($i = 0; $i < $l10n->total
; $i++
) {
4150 $orig = $l10n->get_original_string($i);
4151 $translation = __($orig);
4153 print T_js_decl($orig, $translation);
4157 function label_to_feed_id($label) {
4158 return LABEL_BASE_INDEX
- 1 - abs($label);
4161 function feed_to_label_id($feed) {
4162 return LABEL_BASE_INDEX
- 1 +
abs($feed);