2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 111);
5 define('LABEL_BASE_INDEX', -1024);
6 define('PLUGIN_FEED_BASE_INDEX', -128);
8 $fetch_last_error = false;
11 function __autoload($class) {
12 $class_file = str_replace("_", "/", strtolower(basename($class)));
14 $file = dirname(__FILE__
)."/../classes/$class_file.php";
16 if (file_exists($file)) {
22 mb_internal_encoding("UTF-8");
23 date_default_timezone_set('UTC');
24 if (defined('E_DEPRECATED')) {
25 error_reporting(E_ALL
& ~E_NOTICE
& ~E_DEPRECATED
);
27 error_reporting(E_ALL
& ~E_NOTICE
);
30 require_once 'config.php';
32 if (DB_TYPE
== "pgsql") {
33 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
35 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
38 define('THEME_VERSION_REQUIRED', 1.1);
41 * Return available translations names.
44 * @return array A array of available translations.
46 function get_translations() {
48 "auto" => "Detect automatically",
54 "fr_FR" => "Français",
55 "hu_HU" => "Magyar (Hungarian)",
56 "it_IT" => "Italiano",
57 "ja_JP" => "日本語 (Japanese)",
58 "lv_LV" => "Latviešu",
59 "nb_NO" => "Norwegian bokmål",
63 "pt_BR" => "Portuguese/Brazil",
64 "zh_CN" => "Simplified Chinese");
69 require_once "lib/accept-to-gettext.php";
70 require_once "lib/gettext/gettext.inc";
73 function startup_gettext() {
75 # Get locale from Accept-Language header
76 $lang = al2gt(array_keys(get_translations()), "text/html");
78 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
79 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
82 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
83 $lang = $_SESSION["language"];
87 if (defined('LC_MESSAGES')) {
88 _setlocale(LC_MESSAGES
, $lang);
89 } else if (defined('LC_ALL')) {
90 _setlocale(LC_ALL
, $lang);
93 _bindtextdomain("messages", "locale");
95 _textdomain("messages");
96 _bind_textdomain_codeset("messages", "UTF-8");
102 require_once 'db-prefs.php';
103 require_once 'version.php';
104 require_once 'ccache.php';
105 require_once 'labels.php';
107 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
108 ini_set('user_agent', SELF_USER_AGENT
);
110 require_once 'lib/pubsubhubbub/publisher.php';
113 $utc_tz = new DateTimeZone('UTC');
114 $schema_version = false;
117 * Print a timestamped debug message.
119 * @param string $msg The debug message.
122 function _debug($msg) {
123 $ts = strftime("%H:%M:%S", time());
124 if (function_exists('posix_getpid')) {
125 $ts = "$ts/" . posix_getpid();
128 if (!(defined('QUIET') && QUIET
)) {
129 print "[$ts] $msg\n";
132 if (defined('LOGFILE')) {
133 $fp = fopen(LOGFILE
, 'a+');
136 fputs($fp, "[$ts] $msg\n");
144 * Purge a feed old posts.
146 * @param mixed $link A database connection.
147 * @param mixed $feed_id The id of the purged feed.
148 * @param mixed $purge_interval Olderness of purged posts.
149 * @param boolean $debug Set to True to enable the debug. False by default.
153 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
155 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
159 $result = db_query($link,
160 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
164 if (db_num_rows($result) == 1) {
165 $owner_uid = db_fetch_result($result, 0, "owner_uid");
168 if ($purge_interval == -1 ||
!$purge_interval) {
170 ccache_update($link, $feed_id, $owner_uid);
175 if (!$owner_uid) return;
177 if (FORCE_ARTICLE_PURGE
== 0) {
178 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
181 $purge_unread = true;
182 $purge_interval = FORCE_ARTICLE_PURGE
;
185 if (!$purge_unread) $query_limit = " unread = false AND ";
187 if (DB_TYPE
== "pgsql") {
188 $pg_version = get_pgsql_version($link);
190 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
192 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
193 ttrss_entries.id = ref_id AND
195 feed_id = '$feed_id' AND
197 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
201 $result = db_query($link, "DELETE FROM ttrss_user_entries
203 WHERE ttrss_entries.id = ref_id AND
205 feed_id = '$feed_id' AND
207 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
210 $rows = pg_affected_rows($result);
214 /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
215 marked = false AND feed_id = '$feed_id' AND
216 (SELECT date_updated FROM ttrss_entries WHERE
217 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
219 $result = db_query($link, "DELETE FROM ttrss_user_entries
220 USING ttrss_user_entries, ttrss_entries
221 WHERE ttrss_entries.id = ref_id AND
223 feed_id = '$feed_id' AND
225 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
227 $rows = mysql_affected_rows($link);
231 ccache_update($link, $feed_id, $owner_uid);
234 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
238 } // function purge_feed
240 function feed_purge_interval($link, $feed_id) {
242 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
243 WHERE id = '$feed_id'");
245 if (db_num_rows($result) == 1) {
246 $purge_interval = db_fetch_result($result, 0, "purge_interval");
247 $owner_uid = db_fetch_result($result, 0, "owner_uid");
249 if ($purge_interval == 0) $purge_interval = get_pref($link,
250 'PURGE_OLD_DAYS', $owner_uid);
252 return $purge_interval;
259 function purge_orphans($link, $do_output = false) {
261 // purge orphaned posts in main content table
262 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
263 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
266 $rows = db_affected_rows($link, $result);
267 _debug("Purged $rows orphaned posts.");
271 function get_feed_update_interval($link, $feed_id) {
272 $result = db_query($link, "SELECT owner_uid, update_interval FROM
273 ttrss_feeds WHERE id = '$feed_id'");
275 if (db_num_rows($result) == 1) {
276 $update_interval = db_fetch_result($result, 0, "update_interval");
277 $owner_uid = db_fetch_result($result, 0, "owner_uid");
279 if ($update_interval != 0) {
280 return $update_interval;
282 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
290 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false) {
292 global $fetch_last_error;
294 if (function_exists('curl_init') && !ini_get("open_basedir")) {
296 if (ini_get("safe_mode")) {
297 $ch = curl_init(geturl($url));
299 $ch = curl_init($url);
302 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : 15);
303 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : 45);
304 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode"));
305 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
306 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
307 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
308 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
309 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
310 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
311 curl_setopt($ch, CURLOPT_ENCODING
, "gzip");
312 curl_setopt($ch, CURLOPT_REFERER
, $url);
315 curl_setopt($ch, CURLOPT_POST
, true);
316 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
320 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
322 $contents = @curl_exec
($ch);
324 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
325 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
326 $contents = @curl_exec
($ch);
329 if ($contents === false) {
330 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
335 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
336 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
338 if ($http_code != 200 ||
$type && strpos($content_type, "$type") === false) {
339 if (curl_errno($ch) != 0) {
340 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
342 $fetch_last_error = "HTTP Code: $http_code";
352 if ($login && $pass){
353 $url_parts = array();
355 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
357 $pass = urlencode($pass);
359 if ($url_parts[1] && $url_parts[2]) {
360 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
364 $data = @file_get_contents
($url);
366 @$gzdecoded = gzdecode($data);
367 if ($gzdecoded) $data = $gzdecoded;
369 if (!$data && function_exists('error_get_last')) {
370 $error = error_get_last();
371 $fetch_last_error = $error["message"];
379 * Try to determine the favicon URL for a feed.
380 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
381 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
383 * @param string $url A feed or page URL
385 * @return mixed The favicon URL, or false if none was found.
387 function get_favicon_url($url) {
389 $favicon_url = false;
391 if ($html = @fetch_file_contents
($url)) {
393 libxml_use_internal_errors(true);
395 $doc = new DOMDocument();
396 $doc->loadHTML($html);
397 $xpath = new DOMXPath($doc);
399 $base = $xpath->query('/html/head/base');
400 foreach ($base as $b) {
401 $url = $b->getAttribute("href");
405 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
406 if (count($entries) > 0) {
407 foreach ($entries as $entry) {
408 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
415 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
418 } // function get_favicon_url
420 function check_feed_favicon($site_url, $feed, $link) {
421 # print "FAVICON [$site_url]: $favicon_url\n";
423 $icon_file = ICONS_DIR
. "/$feed.ico";
425 if (!file_exists($icon_file)) {
426 $favicon_url = get_favicon_url($site_url);
429 // Limiting to "image" type misses those served with text/plain
430 $contents = fetch_file_contents($favicon_url); // , "image");
433 // Crude image type matching.
434 // Patterns gleaned from the file(1) source code.
435 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
436 // 0 string \000\000\001\000 MS Windows icon resource
437 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
439 elseif (preg_match('/^GIF8/', $contents)) {
440 // 0 string GIF8 GIF image data
441 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
443 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
444 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
445 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
447 elseif (preg_match('/^\xff\xd8/', $contents)) {
448 // 0 beshort 0xffd8 JPEG image data
449 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
452 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
458 $fp = @fopen
($icon_file, "w");
461 fwrite($fp, $contents);
463 chmod($icon_file, 0644);
470 function print_select($id, $default, $values, $attributes = "") {
471 print "<select name=\"$id\" id=\"$id\" $attributes>";
472 foreach ($values as $v) {
474 $sel = "selected=\"1\"";
480 print "<option value=\"$v\" $sel>$v</option>";
485 function print_select_hash($id, $default, $values, $attributes = "") {
486 print "<select name=\"$id\" id='$id' $attributes>";
487 foreach (array_keys($values) as $v) {
489 $sel = 'selected="selected"';
495 print "<option $sel value=\"$v\">".$values[$v]."</option>";
501 function print_radio($id, $default, $true_is, $values, $attributes = "") {
502 foreach ($values as $v) {
509 if ($v == $true_is) {
510 $sel .= " value=\"1\"";
512 $sel .= " value=\"0\"";
515 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
516 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
521 function initialize_user_prefs($link, $uid, $profile = false) {
523 $uid = db_escape_string($link, $uid);
527 $profile_qpart = "AND profile IS NULL";
529 $profile_qpart = "AND profile = '$profile'";
532 if (get_schema_version($link) < 63) $profile_qpart = "";
534 db_query($link, "BEGIN");
536 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
538 $u_result = db_query($link, "SELECT pref_name
539 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
541 $active_prefs = array();
543 while ($line = db_fetch_assoc($u_result)) {
544 array_push($active_prefs, $line["pref_name"]);
547 while ($line = db_fetch_assoc($result)) {
548 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
549 // print "adding " . $line["pref_name"] . "<br>";
551 $line["def_value"] = db_escape_string($link, $line["def_value"]);
552 $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
554 if (get_schema_version($link) < 63) {
555 db_query($link, "INSERT INTO ttrss_user_prefs
556 (owner_uid,pref_name,value) VALUES
557 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
560 db_query($link, "INSERT INTO ttrss_user_prefs
561 (owner_uid,pref_name,value, profile) VALUES
562 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
568 db_query($link, "COMMIT");
572 function get_ssl_certificate_id() {
573 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
574 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
575 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
576 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
577 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
582 function authenticate_user($link, $login, $password, $check_only = false) {
584 if (!SINGLE_USER_MODE
) {
588 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
590 $user_id = (int) $plugin->authenticate($login, $password);
593 $_SESSION["auth_module"] = strtolower(get_class($plugin));
598 if ($user_id && !$check_only) {
601 $_SESSION["uid"] = $user_id;
603 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
604 WHERE id = '$user_id'");
606 $_SESSION["name"] = db_fetch_result($result, 0, "login");
607 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
608 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
610 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
613 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
614 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
616 $_SESSION["last_version_check"] = time();
618 initialize_user_prefs($link, $_SESSION["uid"]);
627 $_SESSION["uid"] = 1;
628 $_SESSION["name"] = "admin";
629 $_SESSION["access_level"] = 10;
631 $_SESSION["hide_hello"] = true;
632 $_SESSION["hide_logout"] = true;
634 $_SESSION["auth_module"] = false;
636 if (!$_SESSION["csrf_token"]) {
637 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
640 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
642 initialize_user_prefs($link, $_SESSION["uid"]);
648 function make_password($length = 8) {
651 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
655 while ($i < $length) {
656 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
658 if (!strstr($password, $char)) {
666 // this is called after user is created to initialize default feeds, labels
669 // user preferences are checked on every login, not here
671 function initialize_user($link, $uid) {
673 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
674 values ('$uid', 'Tiny Tiny RSS: New Releases',
675 'http://tt-rss.org/releases.rss')");
677 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
678 values ('$uid', 'Tiny Tiny RSS: Forum',
679 'http://tt-rss.org/forum/rss.php')");
682 function logout_user() {
684 if (isset($_COOKIE[session_name()])) {
685 setcookie(session_name(), '', time()-42000, '/');
689 function validate_csrf($csrf_token) {
690 return $csrf_token == $_SESSION['csrf_token'];
693 function validate_session($link) {
694 if (SINGLE_USER_MODE
) return true;
696 $check_ip = $_SESSION['ip_address'];
698 switch (SESSION_CHECK_ADDRESS
) {
703 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
706 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.'));
707 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
711 if ($check_ip && strpos($_SERVER['REMOTE_ADDR'], $check_ip) !== 0) {
712 $_SESSION["login_error_msg"] =
713 __("Session failed to validate (incorrect IP)");
717 if ($_SESSION["ref_schema_version"] != get_schema_version($link, true))
720 if ($_SESSION["uid"]) {
722 $result = db_query($link,
723 "SELECT pwd_hash FROM ttrss_users WHERE id = '".$_SESSION["uid"]."'");
725 $pwd_hash = db_fetch_result($result, 0, "pwd_hash");
727 if ($pwd_hash != $_SESSION["pwd_hash"]) {
732 /* if ($_SESSION["cookie_lifetime"] && $_SESSION["uid"]) {
734 //print_r($_SESSION);
736 if (time() > $_SESSION["cookie_lifetime"]) {
744 function load_user_plugins($link, $owner_uid) {
746 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
749 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
751 if (get_schema_version($link) > 100) {
752 $pluginhost->load_data();
757 function login_sequence($link) {
758 $_SESSION["prefs_cache"] = false;
760 if (SINGLE_USER_MODE
) {
762 authenticate_user($link, "admin", null);
764 load_user_plugins($link, $_SESSION["uid"]);
766 if (!$_SESSION["uid"] ||
!validate_session($link)) {
768 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
769 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
771 authenticate_user($link, null, null, true);
774 if (!$_SESSION["uid"]) render_login_form($link);
777 /* bump login timestamp */
778 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
780 $_SESSION["last_login_update"] = time();
783 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
784 setcookie("ttrss_lang", $_SESSION["language"],
785 time() + SESSION_COOKIE_LIFETIME
);
788 if ($_SESSION["uid"]) {
790 load_user_plugins($link, $_SESSION["uid"]);
794 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
795 $_SESSION["uid"] . " AND
796 (SELECT COUNT(id) FROM ttrss_feeds WHERE
797 ttrss_feeds.id = feed_id) = 0");
799 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
800 $_SESSION["uid"] . " AND
801 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
802 ttrss_feed_categories.id = feed_id) = 0");
809 function truncate_string($str, $max_len, $suffix = '…') {
810 if (mb_strlen($str, "utf-8") > $max_len - 3) {
811 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
817 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
820 $source_tz = new DateTimeZone($source_tz);
821 } catch (Exception
$e) {
822 $source_tz = new DateTimeZone('UTC');
826 $dest_tz = new DateTimeZone($dest_tz);
827 } catch (Exception
$e) {
828 $dest_tz = new DateTimeZone('UTC');
831 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
832 return $dt->format('U') +
$dest_tz->getOffset($dt);
835 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
836 $no_smart_dt = false) {
838 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
839 if (!$timestamp) $timestamp = '1970-01-01 0:00';
844 # We store date in UTC internally
845 $dt = new DateTime($timestamp, $utc_tz);
847 if ($tz_offset == -1) {
849 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
852 $user_tz = new DateTimeZone($user_tz_string);
853 } catch (Exception
$e) {
857 $tz_offset = $user_tz->getOffset($dt);
860 $user_timestamp = $dt->format('U') +
$tz_offset;
863 return smart_date_time($link, $user_timestamp,
864 $tz_offset, $owner_uid);
867 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
869 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
871 return date($format, $user_timestamp);
875 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
876 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
878 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
879 return date("G:i", $timestamp);
880 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
881 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
882 return date($format, $timestamp);
884 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
885 return date($format, $timestamp);
889 function sql_bool_to_bool($s) {
890 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
897 function bool_to_sql_bool($s) {
905 // Session caching removed due to causing wrong redirects to upgrade
906 // script when get_schema_version() is called on an obsolete session
907 // created on a previous schema version.
908 function get_schema_version($link, $nocache = false) {
909 global $schema_version;
911 if (!$schema_version) {
912 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
913 $version = db_fetch_result($result, 0, "schema_version");
914 $schema_version = $version;
917 return $schema_version;
921 function sanity_check($link) {
922 require_once 'errors.php';
925 $schema_version = get_schema_version($link, true);
927 if ($schema_version != SCHEMA_VERSION
) {
931 if (DB_TYPE
== "mysql") {
932 $result = db_query($link, "SELECT true", false);
933 if (db_num_rows($result) != 1) {
938 if (db_escape_string($link, "testTEST") != "testTEST") {
942 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
945 function file_is_locked($filename) {
946 if (function_exists('flock')) {
947 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
949 if (flock($fp, LOCK_EX | LOCK_NB
)) {
960 return true; // consider the file always locked and skip the test
963 function make_lockfile($filename) {
964 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
966 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
967 if (function_exists('posix_getpid')) {
968 fwrite($fp, posix_getpid() . "\n");
976 function make_stampfile($filename) {
977 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
979 if (flock($fp, LOCK_EX | LOCK_NB
)) {
980 fwrite($fp, time() . "\n");
989 function sql_random_function() {
990 if (DB_TYPE
== "mysql") {
997 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false) {
999 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
1001 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
1003 if (is_numeric($feed)) {
1009 $children = getChildCategories($link, $feed, $owner_uid);
1010 array_push($children, $feed);
1012 $children = join(",", $children);
1014 $cat_qpart = "cat_id IN ($children)";
1016 $cat_qpart = "cat_id IS NULL";
1019 db_query($link, "UPDATE ttrss_user_entries
1020 SET unread = false,last_read = NOW()
1021 WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart)
1023 AND owner_uid = $owner_uid");
1025 } else if ($feed == -2) {
1027 db_query($link, "UPDATE ttrss_user_entries
1028 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1029 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1030 AND unread = true AND owner_uid = $owner_uid");
1033 } else if ($feed > 0) {
1035 db_query($link, "UPDATE ttrss_user_entries
1036 SET unread = false,last_read = NOW()
1037 WHERE feed_id = '$feed'
1039 AND owner_uid = $owner_uid");
1041 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1044 db_query($link, "UPDATE ttrss_user_entries
1045 SET unread = false,last_read = NOW()
1048 AND owner_uid = $owner_uid");
1052 db_query($link, "UPDATE ttrss_user_entries
1053 SET unread = false,last_read = NOW()
1054 WHERE published = true
1056 AND owner_uid = $owner_uid");
1061 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1063 if (DB_TYPE
== "pgsql") {
1064 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1066 $match_part = "updated > DATE_SUB(NOW(),
1067 INTERVAL $intl HOUR) ";
1070 $result = db_query($link, "SELECT id FROM ttrss_entries,
1071 ttrss_user_entries WHERE $match_part AND
1073 ttrss_user_entries.ref_id = ttrss_entries.id AND
1074 owner_uid = $owner_uid");
1076 $affected_ids = array();
1078 while ($line = db_fetch_assoc($result)) {
1079 array_push($affected_ids, $line["id"]);
1082 catchupArticlesById($link, $affected_ids, 0);
1086 db_query($link, "UPDATE ttrss_user_entries
1087 SET unread = false,last_read = NOW()
1088 WHERE unread = true AND
1089 owner_uid = $owner_uid");
1092 } else if ($feed < LABEL_BASE_INDEX
) { // label
1094 $label_id = feed_to_label_id($feed);
1096 db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
1097 SET unread = false, last_read = NOW()
1098 WHERE label_id = '$label_id' AND unread = true
1099 AND owner_uid = '$owner_uid' AND ref_id = article_id");
1103 ccache_update($link, $feed, $owner_uid, $cat_view);
1106 db_query($link, "BEGIN");
1108 $tag_name = db_escape_string($link, $feed);
1110 $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
1111 WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
1113 while ($line = db_fetch_assoc($result)) {
1114 db_query($link, "UPDATE ttrss_user_entries SET
1115 unread = false, last_read = NOW()
1117 AND int_id = " . $line["post_int_id"]);
1119 db_query($link, "COMMIT");
1123 function getAllCounters($link) {
1124 $data = getGlobalCounters($link);
1126 $data = array_merge($data, getVirtCounters($link));
1127 $data = array_merge($data, getLabelCounters($link));
1128 $data = array_merge($data, getFeedCounters($link, $active_feed));
1129 $data = array_merge($data, getCategoryCounters($link));
1134 function getCategoryTitle($link, $cat_id) {
1136 if ($cat_id == -1) {
1137 return __("Special");
1138 } else if ($cat_id == -2) {
1139 return __("Labels");
1142 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1145 if (db_num_rows($result) == 1) {
1146 return db_fetch_result($result, 0, "title");
1148 return __("Uncategorized");
1154 function getCategoryCounters($link) {
1157 /* Labels category */
1159 $cv = array("id" => -2, "kind" => "cat",
1160 "counter" => getCategoryUnread($link, -2));
1162 array_push($ret_arr, $cv);
1164 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1165 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1166 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1167 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1168 WHERE ttrss_cat_counters_cache.feed_id = id AND
1169 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1170 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1172 while ($line = db_fetch_assoc($result)) {
1173 $line["cat_id"] = (int) $line["cat_id"];
1175 if ($line["num_children"] > 0) {
1176 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1181 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1182 "counter" => $line["unread"] +
$child_counter);
1184 array_push($ret_arr, $cv);
1187 /* Special case: NULL category doesn't actually exist in the DB */
1189 $cv = array("id" => 0, "kind" => "cat",
1190 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1192 array_push($ret_arr, $cv);
1197 // only accepts real cats (>= 0)
1198 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1199 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1201 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1202 AND owner_uid = $owner_uid");
1206 while ($line = db_fetch_assoc($result)) {
1207 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1208 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1214 function getCategoryUnread($link, $cat, $owner_uid = false) {
1216 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1221 $cat_query = "cat_id = '$cat'";
1223 $cat_query = "cat_id IS NULL";
1226 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1227 AND owner_uid = " . $owner_uid);
1229 $cat_feeds = array();
1230 while ($line = db_fetch_assoc($result)) {
1231 array_push($cat_feeds, "feed_id = " . $line["id"]);
1234 if (count($cat_feeds) == 0) return 0;
1236 $match_part = implode(" OR ", $cat_feeds);
1238 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1239 FROM ttrss_user_entries
1240 WHERE unread = true AND ($match_part)
1241 AND owner_uid = " . $owner_uid);
1245 # this needs to be rewritten
1246 while ($line = db_fetch_assoc($result)) {
1247 $unread +
= $line["unread"];
1251 } else if ($cat == -1) {
1252 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1253 } else if ($cat == -2) {
1255 $result = db_query($link, "
1256 SELECT COUNT(unread) AS unread FROM
1257 ttrss_user_entries, ttrss_user_labels2
1258 WHERE article_id = ref_id AND unread = true
1259 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1261 $unread = db_fetch_result($result, 0, "unread");
1268 function getFeedUnread($link, $feed, $is_cat = false) {
1269 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1272 function getLabelUnread($link, $label_id, $owner_uid = false) {
1273 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1275 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1276 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1278 if (db_num_rows($result) != 0) {
1279 return db_fetch_result($result, 0, "unread");
1285 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1286 $owner_uid = false) {
1288 $n_feed = (int) $feed;
1289 $need_entries = false;
1291 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1294 $unread_qpart = "unread = true";
1296 $unread_qpart = "true";
1300 return getCategoryUnread($link, $n_feed, $owner_uid);
1301 } else if ($n_feed == -6) {
1303 } else if ($feed != "0" && $n_feed == 0) {
1305 $feed = db_escape_string($link, $feed);
1307 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1308 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1309 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1310 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1311 return db_fetch_result($result, 0, "count");
1313 } else if ($n_feed == -1) {
1314 $match_part = "marked = true";
1315 } else if ($n_feed == -2) {
1316 $match_part = "published = true";
1317 } else if ($n_feed == -3) {
1318 $match_part = "unread = true AND score >= 0";
1320 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1322 if (DB_TYPE
== "pgsql") {
1323 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1325 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1328 $need_entries = true;
1330 } else if ($n_feed == -4) {
1331 $match_part = "true";
1332 } else if ($n_feed >= 0) {
1335 $match_part = "feed_id = '$n_feed'";
1337 $match_part = "feed_id IS NULL";
1340 } else if ($feed < LABEL_BASE_INDEX
) {
1342 $label_id = feed_to_label_id($feed);
1344 return getLabelUnread($link, $label_id, $owner_uid);
1350 if ($need_entries) {
1351 $from_qpart = "ttrss_user_entries,ttrss_entries";
1352 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1354 $from_qpart = "ttrss_user_entries";
1357 $query = "SELECT count(int_id) AS unread
1358 FROM $from_qpart WHERE
1359 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1361 //echo "[$feed/$query]\n";
1363 $result = db_query($link, $query);
1367 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1368 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1369 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1370 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1373 $unread = db_fetch_result($result, 0, "unread");
1378 function getGlobalUnread($link, $user_id = false) {
1381 $user_id = $_SESSION["uid"];
1384 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1385 WHERE owner_uid = '$user_id' AND feed_id > 0");
1387 $c_id = db_fetch_result($result, 0, "c_id");
1392 function getGlobalCounters($link, $global_unread = -1) {
1395 if ($global_unread == -1) {
1396 $global_unread = getGlobalUnread($link);
1399 $cv = array("id" => "global-unread",
1400 "counter" => (int) $global_unread);
1402 array_push($ret_arr, $cv);
1404 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1405 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1407 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1409 $cv = array("id" => "subscribed-feeds",
1410 "counter" => (int) $subscribed_feeds);
1412 array_push($ret_arr, $cv);
1417 function getVirtCounters($link) {
1421 for ($i = 0; $i >= -4; $i--) {
1423 $count = getFeedUnread($link, $i);
1425 $cv = array("id" => $i,
1426 "counter" => (int) $count);
1428 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1429 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1431 array_push($ret_arr, $cv);
1437 $feeds = $pluginhost->get_feeds(-1);
1439 if (is_array($feeds)) {
1440 foreach ($feeds as $feed) {
1441 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1442 "counter" => $feed['sender']->get_unread($feed['id']));
1444 array_push($ret_arr, $cv);
1452 function getLabelCounters($link, $descriptions = false) {
1456 $owner_uid = $_SESSION["uid"];
1458 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1459 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1460 (ttrss_labels2.id = label_id)
1461 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1462 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1463 ttrss_labels2.caption");
1465 while ($line = db_fetch_assoc($result)) {
1467 $id = label_to_feed_id($line["id"]);
1469 $label_name = $line["caption"];
1470 $count = $line["unread"];
1472 $cv = array("id" => $id,
1473 "counter" => (int) $count);
1476 $cv["description"] = $label_name;
1478 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1479 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1481 array_push($ret_arr, $cv);
1487 function getFeedCounters($link, $active_feed = false) {
1491 $query = "SELECT ttrss_feeds.id,
1493 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1494 last_error, value AS count
1495 FROM ttrss_feeds, ttrss_counters_cache
1496 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1497 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1498 AND ttrss_counters_cache.feed_id = id";
1500 $result = db_query($link, $query);
1501 $fctrs_modified = false;
1503 while ($line = db_fetch_assoc($result)) {
1506 $count = $line["count"];
1507 $last_error = htmlspecialchars($line["last_error"]);
1509 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1511 $has_img = feed_has_icon($id);
1513 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1516 $cv = array("id" => $id,
1517 "updated" => $last_updated,
1518 "counter" => (int) $count,
1519 "has_img" => (int) $has_img);
1522 $cv["error"] = $last_error;
1524 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1525 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1527 if ($active_feed && $id == $active_feed)
1528 $cv["title"] = truncate_string($line["title"], 30);
1530 array_push($ret_arr, $cv);
1537 function get_pgsql_version($link) {
1538 $result = db_query($link, "SELECT version() AS version");
1539 $version = explode(" ", db_fetch_result($result, 0, "version"));
1544 * @return array (code => Status code, message => error message if available)
1546 * 0 - OK, Feed already exists
1547 * 1 - OK, Feed added
1549 * 3 - URL content is HTML, no feeds available
1550 * 4 - URL content is HTML which contains multiple feeds.
1551 * Here you should call extractfeedurls in rpc-backend
1552 * to get all possible feeds.
1553 * 5 - Couldn't download the URL content.
1555 function subscribe_to_feed($link, $url, $cat_id = 0,
1556 $auth_login = '', $auth_pass = '') {
1558 global $fetch_last_error;
1560 require_once "include/rssfuncs.php";
1562 $url = fix_url($url);
1564 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1566 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1569 return array("code" => 5, "message" => $fetch_last_error);
1572 if (is_html($contents)) {
1573 $feedUrls = get_feeds_from_html($url, $contents);
1575 if (count($feedUrls) == 0) {
1576 return array("code" => 3);
1577 } else if (count($feedUrls) > 1) {
1578 return array("code" => 4, "feeds" => $feedUrls);
1580 //use feed url as new URL
1581 $url = key($feedUrls);
1584 if ($cat_id == "0" ||
!$cat_id) {
1585 $cat_qpart = "NULL";
1587 $cat_qpart = "'$cat_id'";
1590 $result = db_query($link,
1591 "SELECT id FROM ttrss_feeds
1592 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1594 if (db_num_rows($result) == 0) {
1595 $result = db_query($link,
1596 "INSERT INTO ttrss_feeds
1597 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1598 VALUES ('".$_SESSION["uid"]."', '$url',
1599 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1601 $result = db_query($link,
1602 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1603 AND owner_uid = " . $_SESSION["uid"]);
1605 $feed_id = db_fetch_result($result, 0, "id");
1608 update_rss_feed($link, $feed_id, true);
1611 return array("code" => 1);
1613 return array("code" => 0);
1617 function print_feed_select($link, $id, $default_id = "",
1618 $attributes = "", $include_all_feeds = true,
1619 $root_id = false, $nest_level = 0) {
1622 print "<select id=\"$id\" name=\"$id\" $attributes>";
1623 if ($include_all_feeds) {
1624 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1625 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1629 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1632 $parent_qpart = "parent_cat = '$root_id'";
1634 $parent_qpart = "parent_cat IS NULL";
1636 $result = db_query($link, "SELECT id,title,
1637 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1638 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1639 FROM ttrss_feed_categories
1640 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1642 while ($line = db_fetch_assoc($result)) {
1644 for ($i = 0; $i < $nest_level; $i++
)
1645 $line["title"] = " - " . $line["title"];
1647 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1649 printf("<option $is_selected value='CAT:%d'>%s</option>",
1650 $line["id"], htmlspecialchars($line["title"]));
1652 if ($line["num_children"] > 0)
1653 print_feed_select($link, $id, $default_id, $attributes,
1654 $include_all_feeds, $line["id"], $nest_level+
1);
1656 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1657 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1659 while ($fline = db_fetch_assoc($feed_result)) {
1660 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1662 $fline["title"] = " + " . $fline["title"];
1664 for ($i = 0; $i < $nest_level; $i++
)
1665 $fline["title"] = " - " . $fline["title"];
1667 printf("<option $is_selected value='%d'>%s</option>",
1668 $fline["id"], htmlspecialchars($fline["title"]));
1673 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1675 printf("<option $is_selected value='CAT:0'>%s</option>",
1676 __("Uncategorized"));
1678 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1679 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1681 while ($fline = db_fetch_assoc($feed_result)) {
1682 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1684 $fline["title"] = " + " . $fline["title"];
1686 for ($i = 0; $i < $nest_level; $i++
)
1687 $fline["title"] = " - " . $fline["title"];
1689 printf("<option $is_selected value='%d'>%s</option>",
1690 $fline["id"], htmlspecialchars($fline["title"]));
1695 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1696 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1698 while ($line = db_fetch_assoc($result)) {
1700 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1702 printf("<option $is_selected value='%d'>%s</option>",
1703 $line["id"], htmlspecialchars($line["title"]));
1712 function print_feed_cat_select($link, $id, $default_id,
1713 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1716 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1720 $parent_qpart = "parent_cat = '$root_id'";
1722 $parent_qpart = "parent_cat IS NULL";
1724 $result = db_query($link, "SELECT id,title,
1725 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1726 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1727 FROM ttrss_feed_categories
1728 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1730 while ($line = db_fetch_assoc($result)) {
1731 if ($line["id"] == $default_id) {
1732 $is_selected = "selected=\"1\"";
1737 for ($i = 0; $i < $nest_level; $i++
)
1738 $line["title"] = " - " . $line["title"];
1741 printf("<option $is_selected value='%d'>%s</option>",
1742 $line["id"], htmlspecialchars($line["title"]));
1744 if ($line["num_children"] > 0)
1745 print_feed_cat_select($link, $id, $default_id, $attributes,
1746 $include_all_cats, $line["id"], $nest_level+
1);
1750 if ($include_all_cats) {
1751 if (db_num_rows($result) > 0) {
1752 print "<option disabled=\"1\">--------</option>";
1755 if ($default_id == 0) {
1756 $is_selected = "selected=\"1\"";
1761 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1767 function checkbox_to_sql_bool($val) {
1768 return ($val == "on") ?
"true" : "false";
1771 function getFeedCatTitle($link, $id) {
1773 return __("Special");
1774 } else if ($id < LABEL_BASE_INDEX
) {
1775 return __("Labels");
1776 } else if ($id > 0) {
1777 $result = db_query($link, "SELECT ttrss_feed_categories.title
1778 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1779 cat_id = ttrss_feed_categories.id");
1780 if (db_num_rows($result) == 1) {
1781 return db_fetch_result($result, 0, "title");
1783 return __("Uncategorized");
1786 return "getFeedCatTitle($id) failed";
1791 function getFeedIcon($id) {
1794 return "images/archive.png";
1797 return "images/mark_set.svg";
1800 return "images/pub_set.svg";
1803 return "images/fresh.png";
1806 return "images/tag.png";
1809 return "images/recently_read.png";
1812 if ($id < LABEL_BASE_INDEX
) {
1813 return "images/label.png";
1815 if (file_exists(ICONS_DIR
. "/$id.ico"))
1816 return ICONS_URL
. "/$id.ico";
1822 function getFeedTitle($link, $id, $cat = false) {
1824 return getCategoryTitle($link, $id);
1825 } else if ($id == -1) {
1826 return __("Starred articles");
1827 } else if ($id == -2) {
1828 return __("Published articles");
1829 } else if ($id == -3) {
1830 return __("Fresh articles");
1831 } else if ($id == -4) {
1832 return __("All articles");
1833 } else if ($id === 0 ||
$id === "0") {
1834 return __("Archived articles");
1835 } else if ($id == -6) {
1836 return __("Recently read");
1837 } else if ($id < LABEL_BASE_INDEX
) {
1838 $label_id = feed_to_label_id($id);
1839 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1840 if (db_num_rows($result) == 1) {
1841 return db_fetch_result($result, 0, "caption");
1843 return "Unknown label ($label_id)";
1846 } else if (is_numeric($id) && $id > 0) {
1847 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1848 if (db_num_rows($result) == 1) {
1849 return db_fetch_result($result, 0, "title");
1851 return "Unknown feed ($id)";
1858 function make_init_params($link) {
1861 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1862 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1863 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1864 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1866 $params[strtolower($param)] = (int) get_pref($link, $param);
1869 $params["icons_url"] = ICONS_URL
;
1870 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1871 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1872 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1873 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1874 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1875 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1877 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1878 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1880 $max_feed_id = db_fetch_result($result, 0, "mid");
1881 $num_feeds = db_fetch_result($result, 0, "nf");
1883 $params["max_feed_id"] = (int) $max_feed_id;
1884 $params["num_feeds"] = (int) $num_feeds;
1886 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1887 $params["hotkeys"] = get_hotkeys_map($link);
1889 $params["csrf_token"] = $_SESSION["csrf_token"];
1890 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1892 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1897 function get_hotkeys_info($link) {
1899 __("Navigation") => array(
1900 "next_feed" => __("Open next feed"),
1901 "prev_feed" => __("Open previous feed"),
1902 "next_article" => __("Open next article"),
1903 "prev_article" => __("Open previous article"),
1904 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1905 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1906 "search_dialog" => __("Show search dialog")),
1907 __("Article") => array(
1908 "toggle_mark" => __("Toggle starred"),
1909 "toggle_publ" => __("Toggle published"),
1910 "toggle_unread" => __("Toggle unread"),
1911 "edit_tags" => __("Edit tags"),
1912 "dismiss_selected" => __("Dismiss selected"),
1913 "dismiss_read" => __("Dismiss read"),
1914 "open_in_new_window" => __("Open in new window"),
1915 "catchup_below" => __("Mark below as read"),
1916 "catchup_above" => __("Mark above as read"),
1917 "article_scroll_down" => __("Scroll down"),
1918 "article_scroll_up" => __("Scroll up"),
1919 "select_article_cursor" => __("Select article under cursor"),
1920 "email_article" => __("Email article"),
1921 "close_article" => __("Close/collapse article"),
1922 "toggle_widescreen" => __("Toggle widescreen mode"),
1923 "toggle_embed_original" => __("Toggle embed original")),
1924 __("Article selection") => array(
1925 "select_all" => __("Select all articles"),
1926 "select_unread" => __("Select unread"),
1927 "select_marked" => __("Select starred"),
1928 "select_published" => __("Select published"),
1929 "select_invert" => __("Invert selection"),
1930 "select_none" => __("Deselect everything")),
1931 __("Feed") => array(
1932 "feed_refresh" => __("Refresh current feed"),
1933 "feed_unhide_read" => __("Un/hide read feeds"),
1934 "feed_subscribe" => __("Subscribe to feed"),
1935 "feed_edit" => __("Edit feed"),
1936 "feed_catchup" => __("Mark as read"),
1937 "feed_reverse" => __("Reverse headlines"),
1938 "feed_debug_update" => __("Debug feed update"),
1939 "catchup_all" => __("Mark all feeds as read"),
1940 "cat_toggle_collapse" => __("Un/collapse current category"),
1941 "toggle_combined_mode" => __("Toggle combined mode"),
1942 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1943 __("Go to") => array(
1944 "goto_all" => __("All articles"),
1945 "goto_fresh" => __("Fresh"),
1946 "goto_marked" => __("Starred"),
1947 "goto_published" => __("Published"),
1948 "goto_tagcloud" => __("Tag cloud"),
1949 "goto_prefs" => __("Preferences")),
1950 __("Other") => array(
1951 "create_label" => __("Create label"),
1952 "create_filter" => __("Create filter"),
1953 "collapse_sidebar" => __("Un/collapse sidebar"),
1954 "help_dialog" => __("Show help dialog"))
1960 function get_hotkeys_map($link) {
1962 // "navigation" => array(
1965 "n" => "next_article",
1966 "p" => "prev_article",
1967 "(38)|up" => "prev_article",
1968 "(40)|down" => "next_article",
1969 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1970 // "^(40)|Ctrl-down" => "next_article_noscroll",
1971 "(191)|/" => "search_dialog",
1972 // "article" => array(
1973 "s" => "toggle_mark",
1974 "*s" => "toggle_publ",
1975 "u" => "toggle_unread",
1976 "*t" => "edit_tags",
1977 "*d" => "dismiss_selected",
1978 "*x" => "dismiss_read",
1979 "o" => "open_in_new_window",
1980 "c p" => "catchup_below",
1981 "c n" => "catchup_above",
1982 "*n" => "article_scroll_down",
1983 "*p" => "article_scroll_up",
1984 "*(38)|Shift+up" => "article_scroll_up",
1985 "*(40)|Shift+down" => "article_scroll_down",
1986 "a *w" => "toggle_widescreen",
1987 "a e" => "toggle_embed_original",
1988 "e" => "email_article",
1989 "a q" => "close_article",
1990 // "article_selection" => array(
1991 "a a" => "select_all",
1992 "a u" => "select_unread",
1993 "a *u" => "select_marked",
1994 "a p" => "select_published",
1995 "a i" => "select_invert",
1996 "a n" => "select_none",
1998 "f r" => "feed_refresh",
1999 "f a" => "feed_unhide_read",
2000 "f s" => "feed_subscribe",
2001 "f e" => "feed_edit",
2002 "f q" => "feed_catchup",
2003 "f x" => "feed_reverse",
2004 "f *d" => "feed_debug_update",
2005 "f *c" => "toggle_combined_mode",
2006 "f c" => "toggle_cdm_expanded",
2007 "*q" => "catchup_all",
2008 "x" => "cat_toggle_collapse",
2010 "g a" => "goto_all",
2011 "g f" => "goto_fresh",
2012 "g s" => "goto_marked",
2013 "g p" => "goto_published",
2014 "g t" => "goto_tagcloud",
2015 "g *p" => "goto_prefs",
2016 // "other" => array(
2017 "(9)|Tab" => "select_article_cursor", // tab
2018 "c l" => "create_label",
2019 "c f" => "create_filter",
2020 "c s" => "collapse_sidebar",
2021 "^(191)|Ctrl+/" => "help_dialog",
2024 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2025 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2026 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2030 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2031 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2034 $prefixes = array();
2036 foreach (array_keys($hotkeys) as $hotkey) {
2037 $pair = explode(" ", $hotkey, 2);
2039 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2040 array_push($prefixes, $pair[0]);
2044 return array($prefixes, $hotkeys);
2047 function make_runtime_info($link) {
2050 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2051 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2053 $max_feed_id = db_fetch_result($result, 0, "mid");
2054 $num_feeds = db_fetch_result($result, 0, "nf");
2056 $data["max_feed_id"] = (int) $max_feed_id;
2057 $data["num_feeds"] = (int) $num_feeds;
2059 $data['last_article_id'] = getLastArticleId($link);
2060 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2062 $data['dep_ts'] = calculate_dep_timestamp();
2063 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2065 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2067 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2069 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2071 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2074 $stamp_delta = time() - $stamp;
2076 if ($stamp_delta > 1800) {
2080 $_SESSION["daemon_stamp_check"] = time();
2083 $data['daemon_stamp_ok'] = $stamp_check;
2085 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2087 $data['daemon_stamp'] = $stamp_fmt;
2092 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2093 $new_version_details = @check_for_update
($link);
2095 $data['new_version_available'] = (int) ($new_version_details != false);
2097 $_SESSION["last_version_check"] = time();
2098 $_SESSION["version_data"] = $new_version_details;
2104 function search_to_sql($link, $search) {
2106 $search_query_part = "";
2108 $keywords = explode(" ", $search);
2109 $query_keywords = array();
2111 foreach ($keywords as $k) {
2112 if (strpos($k, "-") === 0) {
2119 $commandpair = explode(":", mb_strtolower($k), 2);
2121 if ($commandpair[0] == "note" && $commandpair[1]) {
2123 if ($commandpair[1] == "true")
2124 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2126 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2128 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2130 if ($commandpair[1] == "true")
2131 array_push($query_keywords, "($not (marked = true))");
2133 array_push($query_keywords, "($not (marked = false))");
2135 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2137 if ($commandpair[1] == "true")
2138 array_push($query_keywords, "($not (published = true))");
2140 array_push($query_keywords, "($not (published = false))");
2142 } else if (strpos($k, "@") === 0) {
2144 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2145 $orig_ts = strtotime(substr($k, 1));
2146 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2148 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2150 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2152 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2153 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2157 $search_query_part = implode("AND", $query_keywords);
2159 return $search_query_part;
2162 function getParentCategories($link, $cat, $owner_uid) {
2165 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2166 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2168 while ($line = db_fetch_assoc($result)) {
2169 array_push($rv, $line["parent_cat"]);
2170 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2176 function getChildCategories($link, $cat, $owner_uid) {
2179 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2180 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2182 while ($line = db_fetch_assoc($result)) {
2183 array_push($rv, $line["id"]);
2184 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2190 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) {
2192 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2194 $ext_tables_part = "";
2198 if (SPHINX_ENABLED
) {
2199 $ids = join(",", @sphinx_search
($search, 0, 500));
2202 $search_query_part = "ref_id IN ($ids) AND ";
2204 $search_query_part = "ref_id = -1 AND ";
2207 $search_query_part = search_to_sql($link, $search);
2208 $search_query_part .= " AND ";
2212 $search_query_part = "";
2217 if (DB_TYPE
== "pgsql") {
2218 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2220 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2223 $override_order = "updated DESC";
2225 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2227 // Try to check if SQL regexp implementation chokes on a valid regexp
2228 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2229 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2230 WHERE $filter_query_part LIMIT 1", false);
2233 $test = db_fetch_result($result, 0, "true_val");
2236 $filter_query_part = "false AND";
2238 $filter_query_part .= " AND";
2241 $filter_query_part = "false AND";
2245 $filter_query_part = "";
2249 $since_id_part = "ttrss_entries.id > $since_id AND ";
2251 $since_id_part = "";
2254 $view_query_part = "";
2256 if ($view_mode == "adaptive") {
2258 $view_query_part = " ";
2259 } else if ($feed != -1) {
2261 $unread = getFeedUnread($link, $feed, $cat_view);
2263 if ($cat_view && $feed > 0 && $include_children)
2264 $unread +
= getCategoryChildrenUnread($link, $feed);
2267 $view_query_part = " unread = true AND ";
2272 if ($view_mode == "marked") {
2273 $view_query_part = " marked = true AND ";
2276 if ($view_mode == "has_note") {
2277 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2280 if ($view_mode == "published") {
2281 $view_query_part = " published = true AND ";
2284 if ($view_mode == "unread" && $feed != -6) {
2285 $view_query_part = " unread = true AND ";
2289 $limit_query_part = "LIMIT " . $limit;
2292 $allow_archived = false;
2294 $vfeed_query_part = "";
2296 // override query strategy and enable feed display when searching globally
2297 if ($search && $search_mode == "all_feeds") {
2298 $query_strategy_part = "true";
2299 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2301 } else if (!is_numeric($feed)) {
2302 $query_strategy_part = "true";
2303 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2304 id = feed_id) as feed_title,";
2305 } else if ($search && $search_mode == "this_cat") {
2306 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2309 if ($include_children) {
2310 $subcats = getChildCategories($link, $feed, $owner_uid);
2311 array_push($subcats, $feed);
2312 $cats_qpart = join(",", $subcats);
2314 $cats_qpart = $feed;
2317 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2320 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2323 } else if ($feed > 0) {
2328 if ($include_children) {
2330 $subcats = getChildCategories($link, $feed, $owner_uid);
2332 array_push($subcats, $feed);
2333 $query_strategy_part = "cat_id IN (".
2334 implode(",", $subcats).")";
2337 $query_strategy_part = "cat_id = '$feed'";
2341 $query_strategy_part = "cat_id IS NULL";
2344 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2347 $query_strategy_part = "feed_id = '$feed'";
2349 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2350 $query_strategy_part = "feed_id IS NULL";
2351 $allow_archived = true;
2352 } else if ($feed == 0 && $cat_view) { // uncategorized
2353 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2354 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2355 } else if ($feed == -1) { // starred virtual feed
2356 $query_strategy_part = "marked = true";
2357 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2358 $allow_archived = true;
2360 if (!$override_order) {
2361 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2364 } else if ($feed == -2) { // published virtual feed OR labels category
2367 $query_strategy_part = "published = true";
2368 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2369 $allow_archived = true;
2371 if (!$override_order) {
2372 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2376 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2378 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2380 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2381 ttrss_user_labels2.article_id = ref_id";
2384 } else if ($feed == -6) { // recently read
2385 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2386 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2387 $allow_archived = true;
2389 if (!$override_order) $override_order = "last_read DESC";
2390 } else if ($feed == -3) { // fresh virtual feed
2391 $query_strategy_part = "unread = true AND score >= 0";
2393 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2395 if (DB_TYPE
== "pgsql") {
2396 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2398 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2401 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2402 } else if ($feed == -4) { // all articles virtual feed
2403 $query_strategy_part = "true";
2404 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2405 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2406 $label_id = feed_to_label_id($feed);
2408 $query_strategy_part = "label_id = '$label_id' AND
2409 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2410 ttrss_user_labels2.article_id = ref_id";
2412 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2413 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2414 $allow_archived = true;
2417 $query_strategy_part = "true";
2420 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2421 $date_sort_field = "updated";
2423 $date_sort_field = "date_entered";
2426 $order_by = "$date_sort_field DESC, updated DESC";
2428 if ($view_mode == "unread_first") {
2429 $order_by = "unread DESC, $order_by";
2432 if ($override_order) {
2433 $order_by = $override_order;
2439 $feed_title = T_sprintf("Search results: %s", $search);
2442 $feed_title = getCategoryTitle($link, $feed);
2444 if (is_numeric($feed) && $feed > 0) {
2445 $result = db_query($link, "SELECT title,site_url,last_error
2446 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2448 $feed_title = db_fetch_result($result, 0, "title");
2449 $feed_site_url = db_fetch_result($result, 0, "site_url");
2450 $last_error = db_fetch_result($result, 0, "last_error");
2452 $feed_title = getFeedTitle($link, $feed);
2457 $content_query_part = "content as content_preview, cached_content, ";
2459 if (is_numeric($feed)) {
2462 $feed_kind = "Feeds";
2464 $feed_kind = "Labels";
2467 if ($limit_query_part) {
2468 $offset_query_part = "OFFSET $offset";
2471 // proper override_order applied above
2472 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2473 if (!$override_order) {
2474 $order_by = "ttrss_feeds.title, $order_by";
2476 $order_by = "ttrss_feeds.title, $override_order";
2480 if (!$allow_archived) {
2481 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2482 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2485 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2486 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2489 $query = "SELECT DISTINCT
2492 ttrss_entries.id,ttrss_entries.title,
2496 always_display_enclosures,
2503 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2504 last_marked, last_published,
2512 ttrss_user_entries.ref_id = ttrss_entries.id AND
2513 ttrss_user_entries.owner_uid = '$owner_uid' AND
2518 $query_strategy_part ORDER BY $order_by
2519 $limit_query_part $offset_query_part";
2521 if ($_REQUEST["debug"]) print $query;
2523 $result = db_query($link, $query);
2528 $select_qpart = "SELECT DISTINCT " .
2532 "ttrss_entries.id as id," .
2545 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2546 "last_marked, last_published, " .
2549 $content_query_part .
2552 $feed_kind = "Tags";
2553 $all_tags = explode(",", $feed);
2554 if ($search_mode == 'any') {
2555 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2556 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2557 $where_qpart = " WHERE " .
2558 "ref_id = ttrss_entries.id AND " .
2559 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2560 "post_int_id = int_id AND $tag_sql AND " .
2562 $search_query_part .
2563 $query_strategy_part . " ORDER BY $order_by " .
2568 $sub_selects = array();
2569 $sub_ands = array();
2570 foreach ($all_tags as $term) {
2571 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");
2578 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2583 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2584 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2585 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2586 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2588 // error_log("TAG SQL: " . $tag_sql);
2589 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2591 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2592 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2595 return array($result, $feed_title, $feed_site_url, $last_error);
2599 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2600 if (!$owner) $owner = $_SESSION["uid"];
2602 $res = trim($str); if (!$res) return '';
2604 if (strpos($res, "href=") === false)
2605 $res = rewrite_urls($res);
2607 $charset_hack = '<head>
2608 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2611 $res = trim($res); if (!$res) return '';
2613 libxml_use_internal_errors(true);
2615 $doc = new DOMDocument();
2616 $doc->loadHTML($charset_hack . $res);
2617 $xpath = new DOMXPath($doc);
2619 $entries = $xpath->query('(//a[@href]|//img[@src])');
2621 foreach ($entries as $entry) {
2625 if ($entry->hasAttribute('href'))
2626 $entry->setAttribute('href',
2627 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2629 if ($entry->hasAttribute('src')) {
2630 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2632 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2634 if (file_exists($cached_filename)) {
2635 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2638 $entry->setAttribute('src', $src);
2641 if ($entry->nodeName
== 'img') {
2642 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2643 $force_remove_images ||
$_SESSION["bw_limit"]) {
2645 $p = $doc->createElement('p');
2647 $a = $doc->createElement('a');
2648 $a->setAttribute('href', $entry->getAttribute('src'));
2650 $a->appendChild(new DOMText($entry->getAttribute('src')));
2651 $a->setAttribute('target', '_blank');
2653 $p->appendChild($a);
2655 $entry->parentNode
->replaceChild($p, $entry);
2660 if (strtolower($entry->nodeName
) == "a") {
2661 $entry->setAttribute("target", "_blank");
2665 $entries = $xpath->query('//iframe');
2666 foreach ($entries as $entry) {
2667 $entry->setAttribute('sandbox', 'allow-scripts');
2671 $allowed_elements = array('a', 'address', 'audio', 'article',
2672 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2673 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2674 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2675 'header', 'html', 'i', 'img', 'ins', 'kbd',
2676 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2677 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2678 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2679 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2681 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2683 $disallowed_attributes = array('id', 'style', 'class');
2687 if (isset($pluginhost)) {
2688 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2689 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2690 if (is_array($retval)) {
2692 $allowed_elements = $retval[1];
2693 $disallowed_attributes = $retval[2];
2700 $doc->removeChild($doc->firstChild
); //remove doctype
2701 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2702 $res = $doc->saveHTML();
2706 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2707 $entries = $doc->getElementsByTagName("*");
2709 foreach ($entries as $entry) {
2710 if (!in_array($entry->nodeName
, $allowed_elements)) {
2711 $entry->parentNode
->removeChild($entry);
2714 if ($entry->hasAttributes()) {
2715 $attrs_to_remove = array();
2717 foreach ($entry->attributes
as $attr) {
2719 if (strpos($attr->nodeName
, 'on') === 0) {
2720 array_push($attrs_to_remove, $attr);
2723 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2724 array_push($attrs_to_remove, $attr);
2728 foreach ($attrs_to_remove as $attr) {
2729 $entry->removeAttributeNode($attr);
2737 function check_for_update($link) {
2738 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2739 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2740 "&iid=" . sha1(SELF_URL_PATH
);
2742 $version_data = @fetch_file_contents
($version_url);
2744 if ($version_data) {
2745 $version_data = json_decode($version_data, true);
2746 if ($version_data && $version_data['version']) {
2748 if (version_compare(VERSION
, $version_data['version']) == -1) {
2749 return $version_data;
2757 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2759 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2760 if (count($ids) == 0) return;
2764 foreach ($ids as $id) {
2765 array_push($tmp_ids, "ref_id = '$id'");
2768 $ids_qpart = join(" OR ", $tmp_ids);
2771 db_query($link, "UPDATE ttrss_user_entries SET
2772 unread = false,last_read = NOW()
2773 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2774 } else if ($cmode == 1) {
2775 db_query($link, "UPDATE ttrss_user_entries SET
2777 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2779 db_query($link, "UPDATE ttrss_user_entries SET
2780 unread = NOT unread,last_read = NOW()
2781 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2786 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2787 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2789 while ($line = db_fetch_assoc($result)) {
2790 ccache_update($link, $line["feed_id"], $owner_uid);
2794 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2796 $a_id = db_escape_string($link, $id);
2798 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2800 $query = "SELECT DISTINCT tag_name,
2801 owner_uid as owner FROM
2802 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2803 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2805 $obj_id = md5("TAGS:$owner_uid:$id");
2808 /* check cache first */
2810 if ($tag_cache === false) {
2811 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2812 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2814 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2818 $tags = explode(",", $tag_cache);
2821 /* do it the hard way */
2823 $tmp_result = db_query($link, $query);
2825 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2826 array_push($tags, $tmp_line["tag_name"]);
2829 /* update the cache */
2831 $tags_str = db_escape_string($link, join(",", $tags));
2833 db_query($link, "UPDATE ttrss_user_entries
2834 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2835 AND owner_uid = $owner_uid");
2841 function trim_array($array) {
2843 array_walk($tmp, 'trim');
2847 function tag_is_valid($tag) {
2848 if ($tag == '') return false;
2849 if (preg_match("/^[0-9]*$/", $tag)) return false;
2850 if (mb_strlen($tag) > 250) return false;
2852 if (function_exists('iconv')) {
2853 $tag = iconv("utf-8", "utf-8", $tag);
2856 if (!$tag) return false;
2861 function render_login_form($link) {
2862 require_once "login_form.php";
2866 // from http://developer.apple.com/internet/safari/faq.html
2867 function no_cache_incantation() {
2868 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2869 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2870 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2871 header("Cache-Control: post-check=0, pre-check=0", false);
2872 header("Pragma: no-cache"); // HTTP/1.0
2875 function format_warning($msg, $id = "") {
2877 return "<div class=\"warning\" id=\"$id\">
2878 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2881 function format_notice($msg, $id = "") {
2883 return "<div class=\"notice\" id=\"$id\">
2884 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2887 function format_error($msg, $id = "") {
2889 return "<div class=\"error\" id=\"$id\">
2890 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2893 function print_notice($msg) {
2894 return print format_notice($msg);
2897 function print_warning($msg) {
2898 return print format_warning($msg);
2901 function print_error($msg) {
2902 return print format_error($msg);
2906 function T_sprintf() {
2907 $args = func_get_args();
2908 return vsprintf(__(array_shift($args)), $args);
2911 function format_inline_player($link, $url, $ctype) {
2915 $url = htmlspecialchars($url);
2917 if (strpos($ctype, "audio/") === 0) {
2919 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2920 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2921 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2923 $id = 'AUDIO-' . uniqid();
2925 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2926 <source type=\"$ctype\" src=\"$url\"></source>
2929 $entry .= "<span onclick=\"player(this)\"
2930 title=\"".__("Click to play")."\" status=\"0\"
2931 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2935 $entry .= "<object type=\"application/x-shockwave-flash\"
2936 data=\"lib/button/musicplayer.swf?song_url=$url\"
2937 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2938 <param name=\"movie\"
2939 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2943 if ($entry) $entry .= " <a target=\"_blank\"
2944 href=\"$url\">" . basename($url) . "</a>";
2952 /* $filename = substr($url, strrpos($url, "/")+1);
2954 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2955 $filename . " (" . $ctype . ")" . "</a>"; */
2959 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2960 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2966 /* we can figure out feed_id from article id anyway, why do we
2967 * pass feed_id here? let's ignore the argument :( */
2969 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2970 WHERE ref_id = '$id'");
2972 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2974 $rv['feed_id'] = $feed_id;
2976 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2978 if ($mark_as_read) {
2979 $result = db_query($link, "UPDATE ttrss_user_entries
2980 SET unread = false,last_read = NOW()
2981 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2983 ccache_update($link, $feed_id, $owner_uid);
2986 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2987 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2988 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2989 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2996 FROM ttrss_entries,ttrss_user_entries
2997 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3001 $line = db_fetch_assoc($result);
3003 $tag_cache = $line["tag_cache"];
3005 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3006 unset($line["tag_cache"]);
3008 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3012 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
3013 $line = $p->hook_render_article($line);
3016 $num_comments = $line["num_comments"];
3017 $entry_comments = "";
3019 if ($num_comments > 0) {
3020 if ($line["comments"]) {
3021 $comments_url = htmlspecialchars($line["comments"]);
3023 $comments_url = htmlspecialchars($line["link"]);
3025 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3027 if ($line["comments"] && $line["link"] != $line["comments"]) {
3028 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3033 header("Content-Type: text/html");
3034 $rv['content'] .= "<html><head>
3035 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3036 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3037 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3038 </head><body id=\"ttrssZoom\">";
3041 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3043 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3045 $entry_author = $line["author"];
3047 if ($entry_author) {
3048 $entry_author = __(" - ") . $entry_author;
3051 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3054 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3056 if ($line["link"]) {
3057 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3058 title=\"".htmlspecialchars($line['title'])."\"
3060 htmlspecialchars($line["link"]) . "\">" .
3061 $line["title"] . "</a>" .
3062 "<span class='author'>$entry_author</span></div>";
3064 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3067 $tags_str = format_tags_string($line["tags"], $id);
3068 $tags_str_full = join(", ", $line["tags"]);
3070 if (!$tags_str_full) $tags_str_full = __("no tags");
3072 if (!$entry_comments) $entry_comments = " "; # placeholder
3074 $rv['content'] .= "<div class='postTags' style='float : right'>
3075 <img src='images/tag.png'
3076 class='tagsPic' alt='Tags' title='Tags'> ";
3079 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3080 <a title=\"".__('Edit tags for this article')."\"
3081 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3083 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3084 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3085 position=\"below\">$tags_str_full</div>";
3089 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3090 $rv['content'] .= $p->hook_article_button($line);
3095 $tags_str = strip_tags($tags_str);
3096 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3098 $rv['content'] .= "</div>";
3099 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3101 if ($line["orig_feed_id"]) {
3103 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3104 WHERE id = ".$line["orig_feed_id"]);
3106 if (db_num_rows($tmp_result) != 0) {
3108 $rv['content'] .= "<div clear='both'>";
3109 $rv['content'] .= __("Originally from:");
3111 $rv['content'] .= " ";
3113 $tmp_line = db_fetch_assoc($tmp_result);
3115 $rv['content'] .= "<a target='_blank'
3116 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3117 $tmp_line['title'] . "</a>";
3119 $rv['content'] .= " ";
3121 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3122 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3124 $rv['content'] .= "</div>";
3128 $rv['content'] .= "</div>";
3130 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3131 if ($line['note']) {
3132 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3134 $rv['content'] .= "</div>";
3136 $rv['content'] .= "<div class=\"postContent\">";
3138 $rv['content'] .= $line["content"];
3140 $rv['content'] .= format_article_enclosures($link, $id,
3141 $always_display_enclosures, $line["content"], $line["hide_images"]);
3143 $rv['content'] .= "</div>";
3145 $rv['content'] .= "</div>";
3151 <div class='footer'>
3152 <button onclick=\"return window.close()\">".
3153 __("Close this window")."</button></div>";
3154 $rv['content'] .= "</body></html>";
3161 function print_checkpoint($n, $s) {
3162 $ts = microtime(true);
3163 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3167 function sanitize_tag($tag) {
3170 $tag = mb_strtolower($tag, 'utf-8');
3172 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3174 // $tag = str_replace('"', "", $tag);
3175 // $tag = str_replace("+", " ", $tag);
3176 $tag = str_replace("technorati tag: ", "", $tag);
3181 function get_self_url_prefix() {
3182 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3183 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3185 return SELF_URL_PATH
;
3190 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3192 * @return string The Mozilla Firefox feed adding URL.
3194 function add_feed_url() {
3195 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3197 $url_path = get_self_url_prefix() .
3198 "/public.php?op=subscribe&feed_url=%s";
3200 } // function add_feed_url
3202 function encrypt_password($pass, $salt = '', $mode2 = false) {
3203 if ($salt && $mode2) {
3204 return "MODE2:" . hash('sha256', $salt . $pass);
3206 return "SHA1X:" . sha1("$salt:$pass");
3208 return "SHA1:" . sha1($pass);
3210 } // function encrypt_password
3212 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3215 $cat_id = (int)getFeedCategory($link, $feed_id);
3217 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3218 owner_uid = $owner_uid AND enabled = true");
3220 $check_cats = join(",", array_merge(
3221 getParentCategories($link, $cat_id, $owner_uid),
3224 while ($line = db_fetch_assoc($result)) {
3225 $filter_id = $line["id"];
3227 $result2 = db_query($link, "SELECT
3228 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3229 FROM ttrss_filters2_rules AS r,
3230 ttrss_filter_types AS t
3232 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3233 (feed_id IS NULL OR feed_id = '$feed_id') AND
3234 filter_type = t.id AND filter_id = '$filter_id'");
3239 while ($rule_line = db_fetch_assoc($result2)) {
3240 # print_r($rule_line);
3243 $rule["reg_exp"] = $rule_line["reg_exp"];
3244 $rule["type"] = $rule_line["type_name"];
3245 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3247 array_push($rules, $rule);
3250 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3251 FROM ttrss_filters2_actions AS a,
3252 ttrss_filter_actions AS t
3254 action_id = t.id AND filter_id = '$filter_id'");
3256 while ($action_line = db_fetch_assoc($result2)) {
3257 # print_r($action_line);
3260 $action["type"] = $action_line["type_name"];
3261 $action["param"] = $action_line["action_param"];
3263 array_push($actions, $action);
3268 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3269 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3270 $filter["rules"] = $rules;
3271 $filter["actions"] = $actions;
3273 if (count($rules) > 0 && count($actions) > 0) {
3274 array_push($filters, $filter);
3281 function get_score_pic($score) {
3283 return "score_high.png";
3284 } else if ($score > 0) {
3285 return "score_half_high.png";
3286 } else if ($score < -100) {
3287 return "score_low.png";
3288 } else if ($score < 0) {
3289 return "score_half_low.png";
3291 return "score_neutral.png";
3295 function feed_has_icon($id) {
3296 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3299 function init_connection($link) {
3302 if (DB_TYPE
== "pgsql") {
3303 pg_query($link, "set client_encoding = 'UTF-8'");
3304 pg_set_client_encoding("UNICODE");
3305 pg_query($link, "set datestyle = 'ISO, european'");
3306 pg_query($link, "set TIME ZONE 0");
3308 db_query($link, "SET time_zone = '+0:0'");
3310 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3311 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3317 $pluginhost = new PluginHost($link);
3318 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3322 print "Unable to connect to database:" . db_last_error();
3327 function format_tags_string($tags, $id) {
3330 $tags_nolinks_str = "";
3336 $formatted_tags = array();
3338 foreach ($tags as $tag) {
3340 $tag_escaped = str_replace("'", "\\'", $tag);
3342 if (mb_strlen($tag) > 30) {
3343 $tag = truncate_string($tag, 30);
3346 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3348 array_push($formatted_tags, $tag_str);
3350 $tmp_tags_str = implode(", ", $formatted_tags);
3352 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3357 $tags_str = implode(", ", $formatted_tags);
3359 if ($num_tags < count($tags)) {
3360 $tags_str .= ", …";
3363 if ($num_tags == 0) {
3364 $tags_str = __("no tags");
3371 function format_article_labels($labels, $id) {
3375 foreach ($labels as $l) {
3376 $labels_str .= sprintf("<span class='hlLabelRef'
3377 style='color : %s; background-color : %s'>%s</span>",
3378 $l[2], $l[3], $l[1]);
3385 function format_article_note($id, $note, $allow_edit = true) {
3387 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3388 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3389 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3395 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3396 if ($parent_cat_id) {
3397 $parent_qpart = "parent_cat = '$parent_cat_id'";
3398 $parent_insert = "'$parent_cat_id'";
3400 $parent_qpart = "parent_cat IS NULL";
3401 $parent_insert = "NULL";
3404 $result = db_query($link,
3405 "SELECT id FROM ttrss_feed_categories
3406 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3408 if (db_num_rows($result) == 0) {
3411 return db_fetch_result($result, 0, "id");
3415 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3417 if (!$feed_cat) return false;
3419 db_query($link, "BEGIN");
3421 if ($parent_cat_id) {
3422 $parent_qpart = "parent_cat = '$parent_cat_id'";
3423 $parent_insert = "'$parent_cat_id'";
3425 $parent_qpart = "parent_cat IS NULL";
3426 $parent_insert = "NULL";
3429 $result = db_query($link,
3430 "SELECT id FROM ttrss_feed_categories
3431 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3433 if (db_num_rows($result) == 0) {
3435 $result = db_query($link,
3436 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3437 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3439 db_query($link, "COMMIT");
3447 function getArticleFeed($link, $id) {
3448 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3449 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3451 if (db_num_rows($result) != 0) {
3452 return db_fetch_result($result, 0, "feed_id");
3459 * Fixes incomplete URLs by prepending "http://".
3460 * Also replaces feed:// with http://, and
3461 * prepends a trailing slash if the url is a domain name only.
3463 * @param string $url Possibly incomplete URL
3465 * @return string Fixed URL.
3467 function fix_url($url) {
3468 if (strpos($url, '://') === false) {
3469 $url = 'http://' . $url;
3470 } else if (substr($url, 0, 5) == 'feed:') {
3471 $url = 'http:' . substr($url, 5);
3474 //prepend slash if the URL has no slash in it
3475 // "http://www.example" -> "http://www.example/"
3476 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3480 if ($url != "http:///")
3486 function validate_feed_url($url) {
3487 $parts = parse_url($url);
3489 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3493 function get_article_enclosures($link, $id) {
3495 $query = "SELECT * FROM ttrss_enclosures
3496 WHERE post_id = '$id' AND content_url != ''";
3500 $result = db_query($link, $query);
3502 if (db_num_rows($result) > 0) {
3503 while ($line = db_fetch_assoc($result)) {
3504 array_push($rv, $line);
3511 function save_email_address($link, $email) {
3512 // FIXME: implement persistent storage of emails
3514 if (!$_SESSION['stored_emails'])
3515 $_SESSION['stored_emails'] = array();
3517 if (!in_array($email, $_SESSION['stored_emails']))
3518 array_push($_SESSION['stored_emails'], $email);
3522 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3524 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3526 $sql_is_cat = bool_to_sql_bool($is_cat);
3528 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3529 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3530 AND owner_uid = " . $owner_uid);
3532 if (db_num_rows($result) == 1) {
3533 return db_fetch_result($result, 0, "access_key");
3535 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3537 $result = db_query($link, "INSERT INTO ttrss_access_keys
3538 (access_key, feed_id, is_cat, owner_uid)
3539 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3546 function get_feeds_from_html($url, $content)
3548 $url = fix_url($url);
3549 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3551 libxml_use_internal_errors(true);
3553 $doc = new DOMDocument();
3554 $doc->loadHTML($content);
3555 $xpath = new DOMXPath($doc);
3556 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3557 $feedUrls = array();
3558 foreach ($entries as $entry) {
3559 if ($entry->hasAttribute('href')) {
3560 $title = $entry->getAttribute('title');
3562 $title = $entry->getAttribute('type');
3564 $feedUrl = rewrite_relative_url(
3565 $baseUrl, $entry->getAttribute('href')
3567 $feedUrls[$feedUrl] = $title;
3573 function is_html($content) {
3574 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3577 function url_is_html($url, $login = false, $pass = false) {
3578 return is_html(fetch_file_contents($url, false, $login, $pass));
3581 function print_label_select($link, $name, $value, $attributes = "") {
3583 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3584 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3586 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3587 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3589 while ($line = db_fetch_assoc($result)) {
3591 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3593 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3594 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3598 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3605 function format_article_enclosures($link, $id, $always_display_enclosures,
3606 $article_content, $hide_images = false) {
3608 $result = get_article_enclosures($link, $id);
3611 if (count($result) > 0) {
3613 $entries_html = array();
3615 $entries_inline = array();
3617 foreach ($result as $line) {
3619 $url = $line["content_url"];
3620 $ctype = $line["content_type"];
3622 if (!$ctype) $ctype = __("unknown type");
3624 $filename = substr($url, strrpos($url, "/")+
1);
3626 $player = format_inline_player($link, $url, $ctype);
3628 if ($player) array_push($entries_inline, $player);
3630 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3631 # $filename . " (" . $ctype . ")" . "</a>";
3633 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3634 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3636 array_push($entries_html, $entry);
3640 $entry["type"] = $ctype;
3641 $entry["filename"] = $filename;
3642 $entry["url"] = $url;
3644 array_push($entries, $entry);
3647 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3648 if ($always_display_enclosures ||
3649 !preg_match("/<img/i", $article_content)) {
3651 foreach ($entries as $entry) {
3653 if (preg_match("/image/", $entry["type"]) ||
3654 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3656 if (!$hide_images) {
3658 alt=\"".htmlspecialchars($entry["filename"])."\"
3659 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3661 $rv .= "<p><a target=\"_blank\"
3662 href=\"".htmlspecialchars($entry["url"])."\"
3663 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3671 if (count($entries_inline) > 0) {
3672 $rv .= "<hr clear='both'/>";
3673 foreach ($entries_inline as $entry) { $rv .= $entry; };
3674 $rv .= "<hr clear='both'/>";
3677 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3678 "<option value=''>" . __('Attachments')."</option>";
3680 foreach ($entries as $entry) {
3681 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3691 function getLastArticleId($link) {
3692 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3693 WHERE owner_uid = " . $_SESSION["uid"]);
3695 if (db_num_rows($result) == 1) {
3696 return db_fetch_result($result, 0, "id");
3702 function build_url($parts) {
3703 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3707 * Converts a (possibly) relative URL to a absolute one.
3709 * @param string $url Base URL (i.e. from where the document is)
3710 * @param string $rel_url Possibly relative URL in the document
3712 * @return string Absolute URL
3714 function rewrite_relative_url($url, $rel_url) {
3715 if (strpos($rel_url, "magnet:") === 0) {
3717 } else if (strpos($rel_url, "://") !== false) {
3719 } else if (strpos($rel_url, "//") === 0) {
3720 # protocol-relative URL (rare but they exist)
3722 } else if (strpos($rel_url, "/") === 0)
3724 $parts = parse_url($url);
3725 $parts['path'] = $rel_url;
3727 return build_url($parts);
3730 $parts = parse_url($url);
3731 if (!isset($parts['path'])) {
3732 $parts['path'] = '/';
3734 $dir = $parts['path'];
3735 if (substr($dir, -1) !== '/') {
3736 $dir = dirname($parts['path']);
3737 $dir !== '/' && $dir .= '/';
3739 $parts['path'] = $dir . $rel_url;
3741 return build_url($parts);
3745 function sphinx_search($query, $offset = 0, $limit = 30) {
3746 require_once 'lib/sphinxapi.php';
3748 $sphinxClient = new SphinxClient();
3750 $sphinxClient->SetServer('localhost', 9312);
3751 $sphinxClient->SetConnectTimeout(1);
3753 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3754 'feed_title' => 20));
3756 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3757 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3758 $sphinxClient->SetLimits($offset, $limit, 1000);
3759 $sphinxClient->SetArrayResult(false);
3760 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3762 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3766 if (is_array($result['matches'])) {
3767 foreach (array_keys($result['matches']) as $int_id) {
3768 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3769 array_push($ids, $ref_id);
3776 function cleanup_tags($link, $days = 14, $limit = 1000) {
3778 if (DB_TYPE
== "pgsql") {
3779 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3780 } else if (DB_TYPE
== "mysql") {
3781 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3786 while ($limit > 0) {
3789 $query = "SELECT ttrss_tags.id AS id
3790 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3791 WHERE post_int_id = int_id AND $interval_query AND
3792 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3794 $result = db_query($link, $query);
3798 while ($line = db_fetch_assoc($result)) {
3799 array_push($ids, $line['id']);
3802 if (count($ids) > 0) {
3803 $ids = join(",", $ids);
3805 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3806 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3811 $limit -= $limit_part;
3814 return $tags_deleted;
3817 function print_user_stylesheet($link) {
3818 $value = get_pref($link, 'USER_STYLESHEET');
3821 print "<style type=\"text/css\">";
3822 print str_replace("<br/>", "\n", $value);
3828 function rewrite_urls($html) {
3829 libxml_use_internal_errors(true);
3831 $charset_hack = '<head>
3832 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3835 $doc = new DOMDocument();
3836 $doc->loadHTML($charset_hack . $html);
3837 $xpath = new DOMXPath($doc);
3839 $entries = $xpath->query('//*/text()');
3841 foreach ($entries as $entry) {
3842 if (strstr($entry->wholeText
, "://") !== false) {
3843 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3844 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3846 if ($text != $entry->wholeText
) {
3847 $cdoc = new DOMDocument();
3848 $cdoc->loadHTML($charset_hack . $text);
3851 foreach ($cdoc->childNodes
as $cnode) {
3852 $cnode = $doc->importNode($cnode, true);
3855 $entry->parentNode
->insertBefore($cnode);
3859 $entry->parentNode
->removeChild($entry);
3865 $node = $doc->getElementsByTagName('body')->item(0);
3867 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3869 return $doc->saveXML($node);
3874 function filter_to_sql($link, $filter, $owner_uid) {
3877 if (DB_TYPE
== "pgsql")
3880 $reg_qpart = "REGEXP";
3882 foreach ($filter["rules"] AS $rule) {
3883 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3884 $rule['reg_exp']) !== FALSE;
3886 if ($regexp_valid) {
3888 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3890 switch ($rule["type"]) {
3892 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3893 $rule['reg_exp'] . "')";
3896 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3897 $rule['reg_exp'] . "')";
3900 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3901 $rule['reg_exp'] . "') OR LOWER(" .
3902 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3905 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3906 $rule['reg_exp'] . "')";
3909 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3910 $rule['reg_exp'] . "')";
3913 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3914 $rule['reg_exp'] . "')";
3918 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3920 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3921 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3924 if (isset($rule["cat_id"])) {
3926 if ($rule["cat_id"] > 0) {
3927 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3928 array_push($children, $rule["cat_id"]);
3930 $children = join(",", $children);
3932 $cat_qpart = "cat_id IN ($children)";
3934 $cat_qpart = "cat_id IS NULL";
3937 $qpart .= " AND $cat_qpart";
3940 array_push($query, "($qpart)");
3945 if (count($query) > 0) {
3946 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3948 $fullquery = "(false)";
3951 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3956 if (!function_exists('gzdecode')) {
3957 function gzdecode($string) { // no support for 2nd argument
3958 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3959 base64_encode($string));
3963 function get_random_bytes($length) {
3964 if (function_exists('openssl_random_pseudo_bytes')) {
3965 return openssl_random_pseudo_bytes($length);
3969 for ($i = 0; $i < $length; $i++
)
3970 $output .= chr(mt_rand(0, 255));
3976 function read_stdin() {
3977 $fp = fopen("php://stdin", "r");
3980 $line = trim(fgets($fp));
3988 function tmpdirname($path, $prefix) {
3989 // Use PHP's tmpfile function to create a temporary
3990 // directory name. Delete the file and keep the name.
3991 $tempname = tempnam($path,$prefix);
3995 if (!unlink($tempname))
4001 function getFeedCategory($link, $feed) {
4002 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
4003 WHERE id = '$feed'");
4005 if (db_num_rows($result) > 0) {
4006 return db_fetch_result($result, 0, "cat_id");
4013 function implements_interface($class, $interface) {
4014 return in_array($interface, class_implements($class));
4017 function geturl($url){
4019 (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');
4021 $curl = curl_init();
4022 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4023 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4024 $header[] = "Cache-Control: max-age=0";
4025 $header[] = "Connection: keep-alive";
4026 $header[] = "Keep-Alive: 300";
4027 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4028 $header[] = "Accept-Language: en-us,en;q=0.5";
4029 $header[] = "Pragma: ";
4031 curl_setopt($curl, CURLOPT_URL
, $url);
4032 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4033 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4034 curl_setopt($curl, CURLOPT_HEADER
, true);
4035 curl_setopt($curl, CURLOPT_REFERER
, $url);
4036 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4037 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4038 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4039 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4040 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4042 $html = curl_exec($curl);
4044 $status = curl_getinfo($curl);
4047 if($status['http_code']!=200){
4048 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4049 list($header) = explode("\r\n\r\n", $html, 2);
4051 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4052 $url = trim(str_replace($matches[1],"",$matches[0]));
4053 $url_parsed = parse_url($url);
4054 return (isset($url_parsed))?
geturl($url, $referer):'';
4057 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4058 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4059 # $handle = @fopen('./curl.error.log', 'a');
4060 # fwrite($handle, $line);
4066 function get_minified_js($files) {
4067 require_once 'lib/jshrink/Minifier.php';
4071 foreach ($files as $js) {
4072 if (!isset($_GET['debug'])) {
4073 $cached_file = CACHE_DIR
. "/js/$js.js";
4075 if (file_exists($cached_file) &&
4076 is_readable($cached_file) &&
4077 filemtime($cached_file) >= filemtime("js/$js.js")) {
4079 $rv .= file_get_contents($cached_file);
4082 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4083 file_put_contents($cached_file, $minified);
4087 $rv .= file_get_contents("js/$js.js");
4094 function stylesheet_tag($filename) {
4095 $timestamp = filemtime($filename);
4097 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4100 function javascript_tag($filename) {
4103 if (!(strpos($filename, "?") === FALSE)) {
4104 $query = substr($filename, strpos($filename, "?")+
1);
4105 $filename = substr($filename, 0, strpos($filename, "?"));
4108 $timestamp = filemtime($filename);
4110 if ($query) $timestamp .= "&$query";
4112 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4115 function calculate_dep_timestamp() {
4116 $files = array_merge(glob("js/*.js"), glob("*.css"));
4120 foreach ($files as $file) {
4121 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4127 function T_js_decl($s1, $s2) {
4129 $s1 = preg_replace("/\n/", "", $s1);
4130 $s2 = preg_replace("/\n/", "", $s2);
4132 $s1 = preg_replace("/\"/", "\\\"", $s1);
4133 $s2 = preg_replace("/\"/", "\\\"", $s2);
4135 return "T_messages[\"$s1\"] = \"$s2\";\n";
4139 function init_js_translations() {
4141 print 'var T_messages = new Object();
4144 if (T_messages[msg]) {
4145 return T_messages[msg];
4151 function ngettext(msg1, msg2, n) {
4152 return (parseInt(n) > 1) ? msg2 : msg1;
4155 $l10n = _get_reader();
4157 for ($i = 0; $i < $l10n->total
; $i++
) {
4158 $orig = $l10n->get_original_string($i);
4159 $translation = __($orig);
4161 print T_js_decl($orig, $translation);
4165 function label_to_feed_id($label) {
4166 return LABEL_BASE_INDEX
- 1 - abs($label);
4169 function feed_to_label_id($feed) {
4170 return LABEL_BASE_INDEX
- 1 +
abs($feed);