2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 108);
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) {
290 $login = urlencode($login);
291 $pass = urlencode($pass);
293 global $fetch_last_error;
295 if (function_exists('curl_init') && !ini_get("open_basedir")) {
297 if (ini_get("safe_mode")) {
298 $ch = curl_init(geturl($url));
300 $ch = curl_init($url);
303 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : 15);
304 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : 45);
305 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode"));
306 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
307 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
308 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
309 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
310 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
311 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
312 curl_setopt($ch, CURLOPT_ENCODING
, "gzip");
313 curl_setopt($ch, CURLOPT_REFERER
, $url);
316 curl_setopt($ch, CURLOPT_POST
, true);
317 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
321 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
323 $contents = @curl_exec
($ch);
325 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
326 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
327 $contents = @curl_exec
($ch);
330 if ($contents === false) {
331 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
336 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
337 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
339 if ($http_code != 200 ||
$type && strpos($content_type, "$type") === false) {
340 if (curl_errno($ch) != 0) {
341 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
343 $fetch_last_error = "HTTP Code: $http_code";
353 if ($login && $pass ){
354 $url_parts = array();
356 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
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"];
1860 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1861 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1863 $max_feed_id = db_fetch_result($result, 0, "mid");
1864 $num_feeds = db_fetch_result($result, 0, "nf");
1866 $params["max_feed_id"] = (int) $max_feed_id;
1867 $params["num_feeds"] = (int) $num_feeds;
1869 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1870 $params["hotkeys"] = get_hotkeys_map($link);
1872 $params["csrf_token"] = $_SESSION["csrf_token"];
1873 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1875 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1880 function get_hotkeys_info($link) {
1882 __("Navigation") => array(
1883 "next_feed" => __("Open next feed"),
1884 "prev_feed" => __("Open previous feed"),
1885 "next_article" => __("Open next article"),
1886 "prev_article" => __("Open previous article"),
1887 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1888 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1889 "search_dialog" => __("Show search dialog")),
1890 __("Article") => array(
1891 "toggle_mark" => __("Toggle starred"),
1892 "toggle_publ" => __("Toggle published"),
1893 "toggle_unread" => __("Toggle unread"),
1894 "edit_tags" => __("Edit tags"),
1895 "dismiss_selected" => __("Dismiss selected"),
1896 "dismiss_read" => __("Dismiss read"),
1897 "open_in_new_window" => __("Open in new window"),
1898 "catchup_below" => __("Mark below as read"),
1899 "catchup_above" => __("Mark above as read"),
1900 "article_scroll_down" => __("Scroll down"),
1901 "article_scroll_up" => __("Scroll up"),
1902 "select_article_cursor" => __("Select article under cursor"),
1903 "email_article" => __("Email article"),
1904 "close_article" => __("Close/collapse article"),
1905 "toggle_widescreen" => __("Toggle widescreen mode"),
1906 "toggle_embed_original" => __("Toggle embed original")),
1907 __("Article selection") => array(
1908 "select_all" => __("Select all articles"),
1909 "select_unread" => __("Select unread"),
1910 "select_marked" => __("Select starred"),
1911 "select_published" => __("Select published"),
1912 "select_invert" => __("Invert selection"),
1913 "select_none" => __("Deselect everything")),
1914 __("Feed") => array(
1915 "feed_refresh" => __("Refresh current feed"),
1916 "feed_unhide_read" => __("Un/hide read feeds"),
1917 "feed_subscribe" => __("Subscribe to feed"),
1918 "feed_edit" => __("Edit feed"),
1919 "feed_catchup" => __("Mark as read"),
1920 "feed_reverse" => __("Reverse headlines"),
1921 "feed_debug_update" => __("Debug feed update"),
1922 "catchup_all" => __("Mark all feeds as read"),
1923 "cat_toggle_collapse" => __("Un/collapse current category"),
1924 "toggle_combined_mode" => __("Toggle combined mode")),
1925 __("Go to") => array(
1926 "goto_all" => __("All articles"),
1927 "goto_fresh" => __("Fresh"),
1928 "goto_marked" => __("Starred"),
1929 "goto_published" => __("Published"),
1930 "goto_tagcloud" => __("Tag cloud"),
1931 "goto_prefs" => __("Preferences")),
1932 __("Other") => array(
1933 "create_label" => __("Create label"),
1934 "create_filter" => __("Create filter"),
1935 "collapse_sidebar" => __("Un/collapse sidebar"),
1936 "help_dialog" => __("Show help dialog"))
1942 function get_hotkeys_map($link) {
1944 // "navigation" => array(
1947 "n" => "next_article",
1948 "p" => "prev_article",
1949 "(38)|up" => "prev_article",
1950 "(40)|down" => "next_article",
1951 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1952 // "^(40)|Ctrl-down" => "next_article_noscroll",
1953 "(191)|/" => "search_dialog",
1954 // "article" => array(
1955 "s" => "toggle_mark",
1956 "*s" => "toggle_publ",
1957 "u" => "toggle_unread",
1958 "*t" => "edit_tags",
1959 "*d" => "dismiss_selected",
1960 "*x" => "dismiss_read",
1961 "o" => "open_in_new_window",
1962 "c p" => "catchup_below",
1963 "c n" => "catchup_above",
1964 "*n" => "article_scroll_down",
1965 "*p" => "article_scroll_up",
1966 "*(38)|Shift+up" => "article_scroll_up",
1967 "*(40)|Shift+down" => "article_scroll_down",
1968 "a *w" => "toggle_widescreen",
1969 "a e" => "toggle_embed_original",
1970 "e" => "email_article",
1971 "a q" => "close_article",
1972 // "article_selection" => array(
1973 "a a" => "select_all",
1974 "a u" => "select_unread",
1975 "a *u" => "select_marked",
1976 "a p" => "select_published",
1977 "a i" => "select_invert",
1978 "a n" => "select_none",
1980 "f r" => "feed_refresh",
1981 "f a" => "feed_unhide_read",
1982 "f s" => "feed_subscribe",
1983 "f e" => "feed_edit",
1984 "f q" => "feed_catchup",
1985 "f x" => "feed_reverse",
1986 "f *d" => "feed_debug_update",
1987 "f *c" => "toggle_combined_mode",
1988 "*q" => "catchup_all",
1989 "x" => "cat_toggle_collapse",
1991 "g a" => "goto_all",
1992 "g f" => "goto_fresh",
1993 "g s" => "goto_marked",
1994 "g p" => "goto_published",
1995 "g t" => "goto_tagcloud",
1996 "g *p" => "goto_prefs",
1997 // "other" => array(
1998 "(9)|Tab" => "select_article_cursor", // tab
1999 "c l" => "create_label",
2000 "c f" => "create_filter",
2001 "c s" => "collapse_sidebar",
2002 "^(191)|Ctrl+/" => "help_dialog",
2005 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2006 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2007 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2011 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2012 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2015 $prefixes = array();
2017 foreach (array_keys($hotkeys) as $hotkey) {
2018 $pair = explode(" ", $hotkey, 2);
2020 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2021 array_push($prefixes, $pair[0]);
2025 return array($prefixes, $hotkeys);
2028 function make_runtime_info($link) {
2031 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2032 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2034 $max_feed_id = db_fetch_result($result, 0, "mid");
2035 $num_feeds = db_fetch_result($result, 0, "nf");
2037 $data["max_feed_id"] = (int) $max_feed_id;
2038 $data["num_feeds"] = (int) $num_feeds;
2040 $data['last_article_id'] = getLastArticleId($link);
2041 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2043 $data['dep_ts'] = calculate_dep_timestamp();
2044 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2046 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2048 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2050 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2052 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2055 $stamp_delta = time() - $stamp;
2057 if ($stamp_delta > 1800) {
2061 $_SESSION["daemon_stamp_check"] = time();
2064 $data['daemon_stamp_ok'] = $stamp_check;
2066 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2068 $data['daemon_stamp'] = $stamp_fmt;
2073 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2074 $new_version_details = @check_for_update
($link);
2076 $data['new_version_available'] = (int) ($new_version_details != false);
2078 $_SESSION["last_version_check"] = time();
2079 $_SESSION["version_data"] = $new_version_details;
2085 function search_to_sql($link, $search) {
2087 $search_query_part = "";
2089 $keywords = explode(" ", $search);
2090 $query_keywords = array();
2092 foreach ($keywords as $k) {
2093 if (strpos($k, "-") === 0) {
2100 $commandpair = explode(":", mb_strtolower($k), 2);
2102 if ($commandpair[0] == "note" && $commandpair[1]) {
2104 if ($commandpair[1] == "true")
2105 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2107 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2109 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2111 if ($commandpair[1] == "true")
2112 array_push($query_keywords, "($not (marked = true))");
2114 array_push($query_keywords, "($not (marked = false))");
2116 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2118 if ($commandpair[1] == "true")
2119 array_push($query_keywords, "($not (published = true))");
2121 array_push($query_keywords, "($not (published = false))");
2123 } else if (strpos($k, "@") === 0) {
2125 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2126 $orig_ts = strtotime(substr($k, 1));
2127 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2129 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2131 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2133 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2134 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2138 $search_query_part = implode("AND", $query_keywords);
2140 return $search_query_part;
2143 function getParentCategories($link, $cat, $owner_uid) {
2146 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2147 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2149 while ($line = db_fetch_assoc($result)) {
2150 array_push($rv, $line["parent_cat"]);
2151 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2157 function getChildCategories($link, $cat, $owner_uid) {
2160 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2161 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2163 while ($line = db_fetch_assoc($result)) {
2164 array_push($rv, $line["id"]);
2165 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2171 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) {
2173 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2175 $ext_tables_part = "";
2179 if (SPHINX_ENABLED
) {
2180 $ids = join(",", @sphinx_search
($search, 0, 500));
2183 $search_query_part = "ref_id IN ($ids) AND ";
2185 $search_query_part = "ref_id = -1 AND ";
2188 $search_query_part = search_to_sql($link, $search);
2189 $search_query_part .= " AND ";
2193 $search_query_part = "";
2198 if (DB_TYPE
== "pgsql") {
2199 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2201 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2204 $override_order = "updated DESC";
2206 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2208 // Try to check if SQL regexp implementation chokes on a valid regexp
2209 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2210 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2211 WHERE $filter_query_part LIMIT 1", false);
2214 $test = db_fetch_result($result, 0, "true_val");
2217 $filter_query_part = "false AND";
2219 $filter_query_part .= " AND";
2222 $filter_query_part = "false AND";
2226 $filter_query_part = "";
2230 $since_id_part = "ttrss_entries.id > $since_id AND ";
2232 $since_id_part = "";
2235 $view_query_part = "";
2237 if ($view_mode == "adaptive" ||
$view_query_part == "noscores") {
2239 $view_query_part = " ";
2240 } else if ($feed != -1) {
2242 $unread = getFeedUnread($link, $feed, $cat_view);
2244 if ($cat_view && $feed > 0 && $include_children)
2245 $unread +
= getCategoryChildrenUnread($link, $feed);
2248 $view_query_part = " unread = true AND ";
2253 if ($view_mode == "marked") {
2254 $view_query_part = " marked = true AND ";
2257 if ($view_mode == "published") {
2258 $view_query_part = " published = true AND ";
2261 if ($view_mode == "unread" && $feed != -6) {
2262 $view_query_part = " unread = true AND ";
2265 if ($view_mode == "updated") {
2266 $view_query_part = " (last_read is null and unread = false) AND ";
2270 $limit_query_part = "LIMIT " . $limit;
2273 $allow_archived = false;
2275 $vfeed_query_part = "";
2277 // override query strategy and enable feed display when searching globally
2278 if ($search && $search_mode == "all_feeds") {
2279 $query_strategy_part = "true";
2280 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2282 } else if (!is_numeric($feed)) {
2283 $query_strategy_part = "true";
2284 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2285 id = feed_id) as feed_title,";
2286 } else if ($search && $search_mode == "this_cat") {
2287 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2290 if ($include_children) {
2291 $subcats = getChildCategories($link, $feed, $owner_uid);
2292 array_push($subcats, $feed);
2293 $cats_qpart = join(",", $subcats);
2295 $cats_qpart = $feed;
2298 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2301 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2304 } else if ($feed > 0) {
2309 if ($include_children) {
2311 $subcats = getChildCategories($link, $feed, $owner_uid);
2313 array_push($subcats, $feed);
2314 $query_strategy_part = "cat_id IN (".
2315 implode(",", $subcats).")";
2318 $query_strategy_part = "cat_id = '$feed'";
2322 $query_strategy_part = "cat_id IS NULL";
2325 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2328 $query_strategy_part = "feed_id = '$feed'";
2330 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2331 $query_strategy_part = "feed_id IS NULL";
2332 $allow_archived = true;
2333 } else if ($feed == 0 && $cat_view) { // uncategorized
2334 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2335 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2336 } else if ($feed == -1) { // starred virtual feed
2337 $query_strategy_part = "marked = true";
2338 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2339 $allow_archived = true;
2341 if (!$override_order) {
2342 if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
2343 $override_order = "date_entered";
2345 $override_order = "last_marked DESC, date_entered DESC";
2349 } else if ($feed == -2) { // published virtual feed OR labels category
2352 $query_strategy_part = "published = true";
2353 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2354 $allow_archived = true;
2356 if (!$override_order) {
2357 if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
2358 $override_order = "date_entered";
2360 $override_order = "last_published DESC, date_entered DESC";
2365 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2367 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2369 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2370 ttrss_user_labels2.article_id = ref_id";
2373 } else if ($feed == -6) { // recently read
2374 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2375 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2376 $allow_archived = true;
2378 if (!$override_order) $override_order = "last_read DESC";
2379 } else if ($feed == -3) { // fresh virtual feed
2380 $query_strategy_part = "unread = true AND score >= 0";
2382 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2384 if (DB_TYPE
== "pgsql") {
2385 $query_strategy_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
2387 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2390 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2391 } else if ($feed == -4) { // all articles virtual feed
2392 $query_strategy_part = "true";
2393 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2394 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2395 $label_id = feed_to_label_id($feed);
2397 $query_strategy_part = "label_id = '$label_id' AND
2398 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2399 ttrss_user_labels2.article_id = ref_id";
2401 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2402 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2403 $allow_archived = true;
2406 $query_strategy_part = "true";
2409 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2410 $date_sort_field = "updated";
2412 $date_sort_field = "date_entered";
2415 if (get_pref($link, 'REVERSE_HEADLINES', $owner_uid)) {
2416 $order_by = "$date_sort_field";
2418 $order_by = "$date_sort_field DESC";
2421 if ($view_mode != "noscores") {
2422 $order_by = "score DESC, $order_by";
2425 if ($view_mode == "unread_first") {
2426 $order_by = "unread DESC, $order_by";
2429 if ($override_order) {
2430 $order_by = $override_order;
2436 $feed_title = T_sprintf("Search results: %s", $search);
2439 $feed_title = getCategoryTitle($link, $feed);
2441 if (is_numeric($feed) && $feed > 0) {
2442 $result = db_query($link, "SELECT title,site_url,last_error
2443 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2445 $feed_title = db_fetch_result($result, 0, "title");
2446 $feed_site_url = db_fetch_result($result, 0, "site_url");
2447 $last_error = db_fetch_result($result, 0, "last_error");
2449 $feed_title = getFeedTitle($link, $feed);
2454 $content_query_part = "content as content_preview, cached_content, ";
2456 if (is_numeric($feed)) {
2459 $feed_kind = "Feeds";
2461 $feed_kind = "Labels";
2464 if ($limit_query_part) {
2465 $offset_query_part = "OFFSET $offset";
2468 // proper override_order applied above
2469 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2470 if (!$override_order) {
2471 $order_by = "ttrss_feeds.title, $order_by";
2473 $order_by = "ttrss_feeds.title, $override_order";
2477 if (!$allow_archived) {
2478 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2479 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2482 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2483 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2486 $query = "SELECT DISTINCT
2489 ttrss_entries.id,ttrss_entries.title,
2493 always_display_enclosures,
2500 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2501 last_marked, last_published,
2502 ".SUBSTRING_FOR_DATE
."(last_read,1,19) as last_read_noms,
2505 ".SUBSTRING_FOR_DATE
."(updated,1,19) as updated_noms,
2511 ttrss_user_entries.ref_id = ttrss_entries.id AND
2512 ttrss_user_entries.owner_uid = '$owner_uid' AND
2517 $query_strategy_part ORDER BY $order_by
2518 $limit_query_part $offset_query_part";
2520 if ($_REQUEST["debug"]) print $query;
2522 $result = db_query($link, $query);
2527 $select_qpart = "SELECT DISTINCT " .
2531 "ttrss_entries.id as id," .
2544 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2545 "last_marked, last_published, " .
2546 SUBSTRING_FOR_DATE
. "(last_read,1,19) as last_read_noms," .
2549 $content_query_part .
2550 SUBSTRING_FOR_DATE
. "(updated,1,19) as updated_noms," .
2553 $feed_kind = "Tags";
2554 $all_tags = explode(",", $feed);
2555 if ($search_mode == 'any') {
2556 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2557 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2558 $where_qpart = " WHERE " .
2559 "ref_id = ttrss_entries.id AND " .
2560 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2561 "post_int_id = int_id AND $tag_sql AND " .
2563 $search_query_part .
2564 $query_strategy_part . " ORDER BY $order_by " .
2569 $sub_selects = array();
2570 $sub_ands = array();
2571 foreach ($all_tags as $term) {
2572 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");
2579 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2584 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2585 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2586 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2587 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2589 // error_log("TAG SQL: " . $tag_sql);
2590 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2592 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2593 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2596 return array($result, $feed_title, $feed_site_url, $last_error);
2600 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2601 if (!$owner) $owner = $_SESSION["uid"];
2603 $res = trim($str); if (!$res) return '';
2605 if (strpos($res, "href=") === false)
2606 $res = rewrite_urls($res);
2608 $charset_hack = '<head>
2609 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2612 $res = trim($res); if (!$res) return '';
2614 libxml_use_internal_errors(true);
2616 $doc = new DOMDocument();
2617 $doc->loadHTML($charset_hack . $res);
2618 $xpath = new DOMXPath($doc);
2620 $entries = $xpath->query('(//a[@href]|//img[@src])');
2622 foreach ($entries as $entry) {
2626 if ($entry->hasAttribute('href'))
2627 $entry->setAttribute('href',
2628 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2630 if ($entry->hasAttribute('src')) {
2631 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2633 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2635 if (file_exists($cached_filename)) {
2636 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2639 $entry->setAttribute('src', $src);
2642 if ($entry->nodeName
== 'img') {
2643 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2644 $force_remove_images) {
2646 $p = $doc->createElement('p');
2648 $a = $doc->createElement('a');
2649 $a->setAttribute('href', $entry->getAttribute('src'));
2651 $a->appendChild(new DOMText($entry->getAttribute('src')));
2652 $a->setAttribute('target', '_blank');
2654 $p->appendChild($a);
2656 $entry->parentNode
->replaceChild($p, $entry);
2661 if (strtolower($entry->nodeName
) == "a") {
2662 $entry->setAttribute("target", "_blank");
2666 $entries = $xpath->query('//iframe');
2667 foreach ($entries as $entry) {
2668 $entry->setAttribute('sandbox', 'allow-scripts');
2674 if (isset($pluginhost)) {
2675 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2676 $doc = $plugin->hook_sanitize($doc, $site_url);
2680 $doc->removeChild($doc->firstChild
); //remove doctype
2681 $doc = strip_harmful_tags($doc);
2682 $res = $doc->saveHTML();
2686 function strip_harmful_tags($doc) {
2687 $entries = $doc->getElementsByTagName("*");
2689 $allowed_elements = array('a', 'address', 'audio', 'article',
2690 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2691 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2692 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2693 'header', 'html', 'i', 'img', 'ins', 'kbd',
2694 'li', 'nav', 'ol', 'p', 'pre', 'q', 's','small',
2695 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2696 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2697 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2699 if ($_SESSION['hasSandbox']) array_push($allowed_elements, 'iframe');
2701 $disallowed_attributes = array('id', 'style', 'class');
2703 foreach ($entries as $entry) {
2704 if (!in_array($entry->nodeName
, $allowed_elements)) {
2705 $entry->parentNode
->removeChild($entry);
2708 if ($entry->hasAttributes()) {
2709 $attrs_to_remove = array();
2711 foreach ($entry->attributes
as $attr) {
2713 if (strpos($attr->nodeName
, 'on') === 0) {
2714 array_push($attrs_to_remove, $attr);
2717 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2718 array_push($attrs_to_remove, $attr);
2722 foreach ($attrs_to_remove as $attr) {
2723 $entry->removeAttributeNode($attr);
2731 function check_for_update($link) {
2732 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2733 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2734 "&iid=" . sha1(SELF_URL_PATH
);
2736 $version_data = @fetch_file_contents
($version_url);
2738 if ($version_data) {
2739 $version_data = json_decode($version_data, true);
2740 if ($version_data && $version_data['version']) {
2742 if (version_compare(VERSION
, $version_data['version']) == -1) {
2743 return $version_data;
2751 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2753 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2754 if (count($ids) == 0) return;
2758 foreach ($ids as $id) {
2759 array_push($tmp_ids, "ref_id = '$id'");
2762 $ids_qpart = join(" OR ", $tmp_ids);
2765 db_query($link, "UPDATE ttrss_user_entries SET
2766 unread = false,last_read = NOW()
2767 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2768 } else if ($cmode == 1) {
2769 db_query($link, "UPDATE ttrss_user_entries SET
2771 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2773 db_query($link, "UPDATE ttrss_user_entries SET
2774 unread = NOT unread,last_read = NOW()
2775 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2780 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2781 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2783 while ($line = db_fetch_assoc($result)) {
2784 ccache_update($link, $line["feed_id"], $owner_uid);
2788 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2790 $a_id = db_escape_string($link, $id);
2792 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2794 $query = "SELECT DISTINCT tag_name,
2795 owner_uid as owner FROM
2796 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2797 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2799 $obj_id = md5("TAGS:$owner_uid:$id");
2802 /* check cache first */
2804 if ($tag_cache === false) {
2805 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2806 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2808 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2812 $tags = explode(",", $tag_cache);
2815 /* do it the hard way */
2817 $tmp_result = db_query($link, $query);
2819 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2820 array_push($tags, $tmp_line["tag_name"]);
2823 /* update the cache */
2825 $tags_str = db_escape_string($link, join(",", $tags));
2827 db_query($link, "UPDATE ttrss_user_entries
2828 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2829 AND owner_uid = $owner_uid");
2835 function trim_array($array) {
2837 array_walk($tmp, 'trim');
2841 function tag_is_valid($tag) {
2842 if ($tag == '') return false;
2843 if (preg_match("/^[0-9]*$/", $tag)) return false;
2844 if (mb_strlen($tag) > 250) return false;
2846 if (function_exists('iconv')) {
2847 $tag = iconv("utf-8", "utf-8", $tag);
2850 if (!$tag) return false;
2855 function render_login_form($link) {
2856 require_once "login_form.php";
2860 // from http://developer.apple.com/internet/safari/faq.html
2861 function no_cache_incantation() {
2862 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2863 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2864 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2865 header("Cache-Control: post-check=0, pre-check=0", false);
2866 header("Pragma: no-cache"); // HTTP/1.0
2869 function format_warning($msg, $id = "") {
2871 return "<div class=\"warning\" id=\"$id\">
2872 <img src=\"images/sign_excl.svg\">$msg</div>";
2875 function format_notice($msg, $id = "") {
2877 return "<div class=\"notice\" id=\"$id\">
2878 <img src=\"images/sign_info.svg\">$msg</div>";
2881 function format_error($msg, $id = "") {
2883 return "<div class=\"error\" id=\"$id\">
2884 <img src=\"images/sign_excl.svg\">$msg</div>";
2887 function print_notice($msg) {
2888 return print format_notice($msg);
2891 function print_warning($msg) {
2892 return print format_warning($msg);
2895 function print_error($msg) {
2896 return print format_error($msg);
2900 function T_sprintf() {
2901 $args = func_get_args();
2902 return vsprintf(__(array_shift($args)), $args);
2905 function format_inline_player($link, $url, $ctype) {
2909 $url = htmlspecialchars($url);
2911 if (strpos($ctype, "audio/") === 0) {
2913 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2914 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2915 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2917 $id = 'AUDIO-' . uniqid();
2919 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2920 <source type=\"$ctype\" src=\"$url\"></source>
2923 $entry .= "<span onclick=\"player(this)\"
2924 title=\"".__("Click to play")."\" status=\"0\"
2925 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2929 $entry .= "<object type=\"application/x-shockwave-flash\"
2930 data=\"lib/button/musicplayer.swf?song_url=$url\"
2931 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2932 <param name=\"movie\"
2933 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2937 if ($entry) $entry .= " <a target=\"_blank\"
2938 href=\"$url\">" . basename($url) . "</a>";
2946 /* $filename = substr($url, strrpos($url, "/")+1);
2948 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2949 $filename . " (" . $ctype . ")" . "</a>"; */
2953 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2954 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2960 /* we can figure out feed_id from article id anyway, why do we
2961 * pass feed_id here? let's ignore the argument :( */
2963 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2964 WHERE ref_id = '$id'");
2966 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2968 $rv['feed_id'] = $feed_id;
2970 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2972 if ($mark_as_read) {
2973 $result = db_query($link, "UPDATE ttrss_user_entries
2974 SET unread = false,last_read = NOW()
2975 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2977 ccache_update($link, $feed_id, $owner_uid);
2980 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2981 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2982 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2983 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2990 FROM ttrss_entries,ttrss_user_entries
2991 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2995 $line = db_fetch_assoc($result);
2997 $tag_cache = $line["tag_cache"];
2999 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3000 unset($line["tag_cache"]);
3002 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3006 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
3007 $line = $p->hook_render_article($line);
3010 $num_comments = $line["num_comments"];
3011 $entry_comments = "";
3013 if ($num_comments > 0) {
3014 if ($line["comments"]) {
3015 $comments_url = htmlspecialchars($line["comments"]);
3017 $comments_url = htmlspecialchars($line["link"]);
3019 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3021 if ($line["comments"] && $line["link"] != $line["comments"]) {
3022 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3027 header("Content-Type: text/html");
3028 $rv['content'] .= "<html><head>
3029 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3030 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3031 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3032 </head><body id=\"ttrssZoom\">";
3035 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3037 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3039 $entry_author = $line["author"];
3041 if ($entry_author) {
3042 $entry_author = __(" - ") . $entry_author;
3045 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3048 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3050 if ($line["link"]) {
3051 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3052 title=\"".htmlspecialchars($line['title'])."\"
3054 htmlspecialchars($line["link"]) . "\">" .
3055 $line["title"] . "</a>" .
3056 "<span class='author'>$entry_author</span></div>";
3058 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3061 $tags_str = format_tags_string($line["tags"], $id);
3062 $tags_str_full = join(", ", $line["tags"]);
3064 if (!$tags_str_full) $tags_str_full = __("no tags");
3066 if (!$entry_comments) $entry_comments = " "; # placeholder
3068 $rv['content'] .= "<div class='postTags' style='float : right'>
3069 <img src='images/tag.png'
3070 class='tagsPic' alt='Tags' title='Tags'> ";
3073 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3074 <a title=\"".__('Edit tags for this article')."\"
3075 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3077 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3078 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3079 position=\"below\">$tags_str_full</div>";
3083 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3084 $rv['content'] .= $p->hook_article_button($line);
3089 $tags_str = strip_tags($tags_str);
3090 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3092 $rv['content'] .= "</div>";
3093 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3095 if ($line["orig_feed_id"]) {
3097 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3098 WHERE id = ".$line["orig_feed_id"]);
3100 if (db_num_rows($tmp_result) != 0) {
3102 $rv['content'] .= "<div clear='both'>";
3103 $rv['content'] .= __("Originally from:");
3105 $rv['content'] .= " ";
3107 $tmp_line = db_fetch_assoc($tmp_result);
3109 $rv['content'] .= "<a target='_blank'
3110 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3111 $tmp_line['title'] . "</a>";
3113 $rv['content'] .= " ";
3115 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3116 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3118 $rv['content'] .= "</div>";
3122 $rv['content'] .= "</div>";
3124 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3125 if ($line['note']) {
3126 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3128 $rv['content'] .= "</div>";
3130 $rv['content'] .= "<div class=\"postContent\">";
3132 $rv['content'] .= $line["content"];
3134 $rv['content'] .= format_article_enclosures($link, $id,
3135 $always_display_enclosures, $line["content"], $line["hide_images"]);
3137 $rv['content'] .= "</div>";
3139 $rv['content'] .= "</div>";
3145 <div class='footer'>
3146 <button onclick=\"return window.close()\">".
3147 __("Close this window")."</button></div>";
3148 $rv['content'] .= "</body></html>";
3155 function print_checkpoint($n, $s) {
3156 $ts = microtime(true);
3157 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3161 function sanitize_tag($tag) {
3164 $tag = mb_strtolower($tag, 'utf-8');
3166 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3168 // $tag = str_replace('"', "", $tag);
3169 // $tag = str_replace("+", " ", $tag);
3170 $tag = str_replace("technorati tag: ", "", $tag);
3175 function get_self_url_prefix() {
3176 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3177 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3179 return SELF_URL_PATH
;
3184 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3186 * @return string The Mozilla Firefox feed adding URL.
3188 function add_feed_url() {
3189 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3191 $url_path = get_self_url_prefix() .
3192 "/public.php?op=subscribe&feed_url=%s";
3194 } // function add_feed_url
3196 function encrypt_password($pass, $salt = '', $mode2 = false) {
3197 if ($salt && $mode2) {
3198 return "MODE2:" . hash('sha256', $salt . $pass);
3200 return "SHA1X:" . sha1("$salt:$pass");
3202 return "SHA1:" . sha1($pass);
3204 } // function encrypt_password
3206 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3209 $cat_id = (int)getFeedCategory($link, $feed_id);
3211 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3212 owner_uid = $owner_uid AND enabled = true");
3214 $check_cats = join(",", array_merge(
3215 getParentCategories($link, $cat_id, $owner_uid),
3218 while ($line = db_fetch_assoc($result)) {
3219 $filter_id = $line["id"];
3221 $result2 = db_query($link, "SELECT
3222 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3223 FROM ttrss_filters2_rules AS r,
3224 ttrss_filter_types AS t
3226 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3227 (feed_id IS NULL OR feed_id = '$feed_id') AND
3228 filter_type = t.id AND filter_id = '$filter_id'");
3233 while ($rule_line = db_fetch_assoc($result2)) {
3234 # print_r($rule_line);
3237 $rule["reg_exp"] = $rule_line["reg_exp"];
3238 $rule["type"] = $rule_line["type_name"];
3239 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3241 array_push($rules, $rule);
3244 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3245 FROM ttrss_filters2_actions AS a,
3246 ttrss_filter_actions AS t
3248 action_id = t.id AND filter_id = '$filter_id'");
3250 while ($action_line = db_fetch_assoc($result2)) {
3251 # print_r($action_line);
3254 $action["type"] = $action_line["type_name"];
3255 $action["param"] = $action_line["action_param"];
3257 array_push($actions, $action);
3262 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3263 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3264 $filter["rules"] = $rules;
3265 $filter["actions"] = $actions;
3267 if (count($rules) > 0 && count($actions) > 0) {
3268 array_push($filters, $filter);
3275 function get_score_pic($score) {
3277 return "score_high.png";
3278 } else if ($score > 0) {
3279 return "score_half_high.png";
3280 } else if ($score < -100) {
3281 return "score_low.png";
3282 } else if ($score < 0) {
3283 return "score_half_low.png";
3285 return "score_neutral.png";
3289 function feed_has_icon($id) {
3290 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3293 function init_connection($link) {
3296 if (DB_TYPE
== "pgsql") {
3297 pg_query($link, "set client_encoding = 'UTF-8'");
3298 pg_set_client_encoding("UNICODE");
3299 pg_query($link, "set datestyle = 'ISO, european'");
3300 pg_query($link, "set TIME ZONE 0");
3302 db_query($link, "SET time_zone = '+0:0'");
3304 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3305 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3311 $pluginhost = new PluginHost($link);
3312 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3316 print "Unable to connect to database:" . db_last_error();
3321 function format_tags_string($tags, $id) {
3324 $tags_nolinks_str = "";
3330 $formatted_tags = array();
3332 foreach ($tags as $tag) {
3334 $tag_escaped = str_replace("'", "\\'", $tag);
3336 if (mb_strlen($tag) > 30) {
3337 $tag = truncate_string($tag, 30);
3340 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3342 array_push($formatted_tags, $tag_str);
3344 $tmp_tags_str = implode(", ", $formatted_tags);
3346 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3351 $tags_str = implode(", ", $formatted_tags);
3353 if ($num_tags < count($tags)) {
3354 $tags_str .= ", …";
3357 if ($num_tags == 0) {
3358 $tags_str = __("no tags");
3365 function format_article_labels($labels, $id) {
3369 foreach ($labels as $l) {
3370 $labels_str .= sprintf("<span class='hlLabelRef'
3371 style='color : %s; background-color : %s'>%s</span>",
3372 $l[2], $l[3], $l[1]);
3379 function format_article_note($id, $note, $allow_edit = true) {
3381 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3382 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3383 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3389 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3390 if ($parent_cat_id) {
3391 $parent_qpart = "parent_cat = '$parent_cat_id'";
3392 $parent_insert = "'$parent_cat_id'";
3394 $parent_qpart = "parent_cat IS NULL";
3395 $parent_insert = "NULL";
3398 $result = db_query($link,
3399 "SELECT id FROM ttrss_feed_categories
3400 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3402 if (db_num_rows($result) == 0) {
3405 return db_fetch_result($result, 0, "id");
3409 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3411 if (!$feed_cat) return false;
3413 db_query($link, "BEGIN");
3415 if ($parent_cat_id) {
3416 $parent_qpart = "parent_cat = '$parent_cat_id'";
3417 $parent_insert = "'$parent_cat_id'";
3419 $parent_qpart = "parent_cat IS NULL";
3420 $parent_insert = "NULL";
3423 $result = db_query($link,
3424 "SELECT id FROM ttrss_feed_categories
3425 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3427 if (db_num_rows($result) == 0) {
3429 $result = db_query($link,
3430 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3431 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3433 db_query($link, "COMMIT");
3441 function getArticleFeed($link, $id) {
3442 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3443 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3445 if (db_num_rows($result) != 0) {
3446 return db_fetch_result($result, 0, "feed_id");
3453 * Fixes incomplete URLs by prepending "http://".
3454 * Also replaces feed:// with http://, and
3455 * prepends a trailing slash if the url is a domain name only.
3457 * @param string $url Possibly incomplete URL
3459 * @return string Fixed URL.
3461 function fix_url($url) {
3462 if (strpos($url, '://') === false) {
3463 $url = 'http://' . $url;
3464 } else if (substr($url, 0, 5) == 'feed:') {
3465 $url = 'http:' . substr($url, 5);
3468 //prepend slash if the URL has no slash in it
3469 // "http://www.example" -> "http://www.example/"
3470 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3474 if ($url != "http:///")
3480 function validate_feed_url($url) {
3481 $parts = parse_url($url);
3483 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3487 function get_article_enclosures($link, $id) {
3489 $query = "SELECT * FROM ttrss_enclosures
3490 WHERE post_id = '$id' AND content_url != ''";
3494 $result = db_query($link, $query);
3496 if (db_num_rows($result) > 0) {
3497 while ($line = db_fetch_assoc($result)) {
3498 array_push($rv, $line);
3505 function save_email_address($link, $email) {
3506 // FIXME: implement persistent storage of emails
3508 if (!$_SESSION['stored_emails'])
3509 $_SESSION['stored_emails'] = array();
3511 if (!in_array($email, $_SESSION['stored_emails']))
3512 array_push($_SESSION['stored_emails'], $email);
3516 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3518 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3520 $sql_is_cat = bool_to_sql_bool($is_cat);
3522 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3523 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3524 AND owner_uid = " . $owner_uid);
3526 if (db_num_rows($result) == 1) {
3527 return db_fetch_result($result, 0, "access_key");
3529 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3531 $result = db_query($link, "INSERT INTO ttrss_access_keys
3532 (access_key, feed_id, is_cat, owner_uid)
3533 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3540 function get_feeds_from_html($url, $content)
3542 $url = fix_url($url);
3543 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3545 libxml_use_internal_errors(true);
3547 $doc = new DOMDocument();
3548 $doc->loadHTML($content);
3549 $xpath = new DOMXPath($doc);
3550 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3551 $feedUrls = array();
3552 foreach ($entries as $entry) {
3553 if ($entry->hasAttribute('href')) {
3554 $title = $entry->getAttribute('title');
3556 $title = $entry->getAttribute('type');
3558 $feedUrl = rewrite_relative_url(
3559 $baseUrl, $entry->getAttribute('href')
3561 $feedUrls[$feedUrl] = $title;
3567 function is_html($content) {
3568 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3571 function url_is_html($url, $login = false, $pass = false) {
3572 return is_html(fetch_file_contents($url, false, $login, $pass));
3575 function print_label_select($link, $name, $value, $attributes = "") {
3577 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3578 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3580 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3581 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3583 while ($line = db_fetch_assoc($result)) {
3585 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3587 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3588 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3592 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3599 function format_article_enclosures($link, $id, $always_display_enclosures,
3600 $article_content, $hide_images = false) {
3602 $result = get_article_enclosures($link, $id);
3605 if (count($result) > 0) {
3607 $entries_html = array();
3609 $entries_inline = array();
3611 foreach ($result as $line) {
3613 $url = $line["content_url"];
3614 $ctype = $line["content_type"];
3616 if (!$ctype) $ctype = __("unknown type");
3618 $filename = substr($url, strrpos($url, "/")+
1);
3620 $player = format_inline_player($link, $url, $ctype);
3622 if ($player) array_push($entries_inline, $player);
3624 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3625 # $filename . " (" . $ctype . ")" . "</a>";
3627 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3628 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3630 array_push($entries_html, $entry);
3634 $entry["type"] = $ctype;
3635 $entry["filename"] = $filename;
3636 $entry["url"] = $url;
3638 array_push($entries, $entry);
3641 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES")) {
3642 if ($always_display_enclosures ||
3643 !preg_match("/<img/i", $article_content)) {
3645 foreach ($entries as $entry) {
3647 if (preg_match("/image/", $entry["type"]) ||
3648 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3650 if (!$hide_images) {
3652 alt=\"".htmlspecialchars($entry["filename"])."\"
3653 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3655 $rv .= "<p><a target=\"_blank\"
3656 href=\"".htmlspecialchars($entry["url"])."\"
3657 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3665 if (count($entries_inline) > 0) {
3666 $rv .= "<hr clear='both'/>";
3667 foreach ($entries_inline as $entry) { $rv .= $entry; };
3668 $rv .= "<hr clear='both'/>";
3671 $rv .= "<br/><div dojoType=\"dijit.form.DropDownButton\">".
3672 "<span>" . __('Attachments')."</span>";
3673 $rv .= "<div dojoType=\"dijit.Menu\" style=\"display: none;\">";
3675 foreach ($entries_html as $entry) { $rv .= $entry; };
3677 $rv .= "</div></div>";
3683 function getLastArticleId($link) {
3684 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3685 WHERE owner_uid = " . $_SESSION["uid"]);
3687 if (db_num_rows($result) == 1) {
3688 return db_fetch_result($result, 0, "id");
3694 function build_url($parts) {
3695 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3699 * Converts a (possibly) relative URL to a absolute one.
3701 * @param string $url Base URL (i.e. from where the document is)
3702 * @param string $rel_url Possibly relative URL in the document
3704 * @return string Absolute URL
3706 function rewrite_relative_url($url, $rel_url) {
3707 if (strpos($rel_url, "magnet:") === 0) {
3709 } else if (strpos($rel_url, "://") !== false) {
3711 } else if (strpos($rel_url, "//") === 0) {
3712 # protocol-relative URL (rare but they exist)
3714 } else if (strpos($rel_url, "/") === 0)
3716 $parts = parse_url($url);
3717 $parts['path'] = $rel_url;
3719 return build_url($parts);
3722 $parts = parse_url($url);
3723 if (!isset($parts['path'])) {
3724 $parts['path'] = '/';
3726 $dir = $parts['path'];
3727 if (substr($dir, -1) !== '/') {
3728 $dir = dirname($parts['path']);
3729 $dir !== '/' && $dir .= '/';
3731 $parts['path'] = $dir . $rel_url;
3733 return build_url($parts);
3737 function sphinx_search($query, $offset = 0, $limit = 30) {
3738 require_once 'lib/sphinxapi.php';
3740 $sphinxClient = new SphinxClient();
3742 $sphinxClient->SetServer('localhost', 9312);
3743 $sphinxClient->SetConnectTimeout(1);
3745 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3746 'feed_title' => 20));
3748 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3749 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3750 $sphinxClient->SetLimits($offset, $limit, 1000);
3751 $sphinxClient->SetArrayResult(false);
3752 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3754 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3758 if (is_array($result['matches'])) {
3759 foreach (array_keys($result['matches']) as $int_id) {
3760 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3761 array_push($ids, $ref_id);
3768 function cleanup_tags($link, $days = 14, $limit = 1000) {
3770 if (DB_TYPE
== "pgsql") {
3771 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3772 } else if (DB_TYPE
== "mysql") {
3773 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3778 while ($limit > 0) {
3781 $query = "SELECT ttrss_tags.id AS id
3782 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3783 WHERE post_int_id = int_id AND $interval_query AND
3784 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3786 $result = db_query($link, $query);
3790 while ($line = db_fetch_assoc($result)) {
3791 array_push($ids, $line['id']);
3794 if (count($ids) > 0) {
3795 $ids = join(",", $ids);
3798 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3799 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3804 $limit -= $limit_part;
3809 return $tags_deleted;
3812 function print_user_stylesheet($link) {
3813 $value = get_pref($link, 'USER_STYLESHEET');
3816 print "<style type=\"text/css\">";
3817 print str_replace("<br/>", "\n", $value);
3823 function rewrite_urls($html) {
3824 libxml_use_internal_errors(true);
3826 $charset_hack = '<head>
3827 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3830 $doc = new DOMDocument();
3831 $doc->loadHTML($charset_hack . $html);
3832 $xpath = new DOMXPath($doc);
3834 $entries = $xpath->query('//*/text()');
3836 foreach ($entries as $entry) {
3837 if (strstr($entry->wholeText
, "://") !== false) {
3838 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3839 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3841 if ($text != $entry->wholeText
) {
3842 $cdoc = new DOMDocument();
3843 $cdoc->loadHTML($charset_hack . $text);
3846 foreach ($cdoc->childNodes
as $cnode) {
3847 $cnode = $doc->importNode($cnode, true);
3850 $entry->parentNode
->insertBefore($cnode);
3854 $entry->parentNode
->removeChild($entry);
3860 $node = $doc->getElementsByTagName('body')->item(0);
3862 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3864 return $doc->saveXML($node);
3869 function filter_to_sql($link, $filter, $owner_uid) {
3872 if (DB_TYPE
== "pgsql")
3875 $reg_qpart = "REGEXP";
3877 foreach ($filter["rules"] AS $rule) {
3878 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3879 $rule['reg_exp']) !== FALSE;
3881 if ($regexp_valid) {
3883 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3885 switch ($rule["type"]) {
3887 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3888 $rule['reg_exp'] . "')";
3891 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3892 $rule['reg_exp'] . "')";
3895 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3896 $rule['reg_exp'] . "') OR LOWER(" .
3897 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3900 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3901 $rule['reg_exp'] . "')";
3904 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3905 $rule['reg_exp'] . "')";
3908 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3909 $rule['reg_exp'] . "')";
3913 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3915 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3916 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3919 if (isset($rule["cat_id"])) {
3921 if ($rule["cat_id"] > 0) {
3922 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3923 array_push($children, $rule["cat_id"]);
3925 $children = join(",", $children);
3927 $cat_qpart = "cat_id IN ($children)";
3929 $cat_qpart = "cat_id IS NULL";
3932 $qpart .= " AND $cat_qpart";
3935 array_push($query, "($qpart)");
3940 if (count($query) > 0) {
3941 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3943 $fullquery = "(false)";
3946 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3951 if (!function_exists('gzdecode')) {
3952 function gzdecode($string) { // no support for 2nd argument
3953 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3954 base64_encode($string));
3958 function get_random_bytes($length) {
3959 if (function_exists('openssl_random_pseudo_bytes')) {
3960 return openssl_random_pseudo_bytes($length);
3964 for ($i = 0; $i < $length; $i++
)
3965 $output .= chr(mt_rand(0, 255));
3971 function read_stdin() {
3972 $fp = fopen("php://stdin", "r");
3975 $line = trim(fgets($fp));
3983 function tmpdirname($path, $prefix) {
3984 // Use PHP's tmpfile function to create a temporary
3985 // directory name. Delete the file and keep the name.
3986 $tempname = tempnam($path,$prefix);
3990 if (!unlink($tempname))
3996 function getFeedCategory($link, $feed) {
3997 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3998 WHERE id = '$feed'");
4000 if (db_num_rows($result) > 0) {
4001 return db_fetch_result($result, 0, "cat_id");
4008 function implements_interface($class, $interface) {
4009 return in_array($interface, class_implements($class));
4012 function geturl($url){
4014 (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');
4016 $curl = curl_init();
4017 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4018 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4019 $header[] = "Cache-Control: max-age=0";
4020 $header[] = "Connection: keep-alive";
4021 $header[] = "Keep-Alive: 300";
4022 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4023 $header[] = "Accept-Language: en-us,en;q=0.5";
4024 $header[] = "Pragma: ";
4026 curl_setopt($curl, CURLOPT_URL
, $url);
4027 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4028 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4029 curl_setopt($curl, CURLOPT_HEADER
, true);
4030 curl_setopt($curl, CURLOPT_REFERER
, $url);
4031 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4032 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4033 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4034 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4035 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4037 $html = curl_exec($curl);
4039 $status = curl_getinfo($curl);
4042 if($status['http_code']!=200){
4043 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4044 list($header) = explode("\r\n\r\n", $html, 2);
4046 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4047 $url = trim(str_replace($matches[1],"",$matches[0]));
4048 $url_parsed = parse_url($url);
4049 return (isset($url_parsed))?
geturl($url, $referer):'';
4052 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4053 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4054 # $handle = @fopen('./curl.error.log', 'a');
4055 # fwrite($handle, $line);
4061 function get_minified_js($files) {
4062 require_once 'lib/jshrink/Minifier.php';
4066 foreach ($files as $js) {
4067 if (!isset($_GET['debug'])) {
4068 $cached_file = CACHE_DIR
. "/js/$js.js";
4070 if (file_exists($cached_file) &&
4071 is_readable($cached_file) &&
4072 filemtime($cached_file) >= filemtime("js/$js.js")) {
4074 $rv .= file_get_contents($cached_file);
4077 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4078 file_put_contents($cached_file, $minified);
4082 $rv .= file_get_contents("js/$js.js");
4089 function stylesheet_tag($filename) {
4090 $timestamp = filemtime($filename);
4092 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4095 function javascript_tag($filename) {
4098 if (!(strpos($filename, "?") === FALSE)) {
4099 $query = substr($filename, strpos($filename, "?")+
1);
4100 $filename = substr($filename, 0, strpos($filename, "?"));
4103 $timestamp = filemtime($filename);
4105 if ($query) $timestamp .= "&$query";
4107 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4110 function calculate_dep_timestamp() {
4111 $files = array_merge(glob("js/*.js"), glob("*.css"));
4115 foreach ($files as $file) {
4116 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4122 function T_js_decl($s1, $s2) {
4124 $s1 = preg_replace("/\n/", "", $s1);
4125 $s2 = preg_replace("/\n/", "", $s2);
4127 $s1 = preg_replace("/\"/", "\\\"", $s1);
4128 $s2 = preg_replace("/\"/", "\\\"", $s2);
4130 return "T_messages[\"$s1\"] = \"$s2\";\n";
4134 function init_js_translations() {
4136 print 'var T_messages = new Object();
4139 if (T_messages[msg]) {
4140 return T_messages[msg];
4146 function ngettext(msg1, msg2, n) {
4147 return (parseInt(n) > 1) ? msg2 : msg1;
4150 $l10n = _get_reader();
4152 for ($i = 0; $i < $l10n->total
; $i++
) {
4153 $orig = $l10n->get_original_string($i);
4154 $translation = __($orig);
4156 print T_js_decl($orig, $translation);
4160 function label_to_feed_id($label) {
4161 return LABEL_BASE_INDEX
- 1 - abs($label);
4164 function feed_to_label_id($feed) {
4165 return LABEL_BASE_INDEX
- 1 +
abs($feed);