2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 110);
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
) {
589 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
591 $user_id = (int) $plugin->authenticate($login, $password);
594 $_SESSION["auth_module"] = strtolower(get_class($plugin));
599 if ($user_id && !$check_only) {
600 $_SESSION["uid"] = $user_id;
602 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
603 WHERE id = '$user_id'");
605 $_SESSION["name"] = db_fetch_result($result, 0, "login");
606 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
607 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
609 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
612 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
613 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
615 $_SESSION["last_version_check"] = time();
617 initialize_user_prefs($link, $_SESSION["uid"]);
626 $_SESSION["uid"] = 1;
627 $_SESSION["name"] = "admin";
628 $_SESSION["access_level"] = 10;
630 $_SESSION["hide_hello"] = true;
631 $_SESSION["hide_logout"] = true;
633 $_SESSION["auth_module"] = false;
635 if (!$_SESSION["csrf_token"]) {
636 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
639 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
641 initialize_user_prefs($link, $_SESSION["uid"]);
647 function make_password($length = 8) {
650 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
654 while ($i < $length) {
655 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
657 if (!strstr($password, $char)) {
665 // this is called after user is created to initialize default feeds, labels
668 // user preferences are checked on every login, not here
670 function initialize_user($link, $uid) {
672 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
673 values ('$uid', 'Tiny Tiny RSS: New Releases',
674 'http://tt-rss.org/releases.rss')");
676 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
677 values ('$uid', 'Tiny Tiny RSS: Forum',
678 'http://tt-rss.org/forum/rss.php')");
681 function logout_user() {
683 if (isset($_COOKIE[session_name()])) {
684 setcookie(session_name(), '', time()-42000, '/');
688 function validate_csrf($csrf_token) {
689 return $csrf_token == $_SESSION['csrf_token'];
692 function validate_session($link) {
693 if (SINGLE_USER_MODE
) return true;
695 $check_ip = $_SESSION['ip_address'];
697 switch (SESSION_CHECK_ADDRESS
) {
702 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
705 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.'));
706 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
710 if ($check_ip && strpos($_SERVER['REMOTE_ADDR'], $check_ip) !== 0) {
711 $_SESSION["login_error_msg"] =
712 __("Session failed to validate (incorrect IP)");
716 if ($_SESSION["ref_schema_version"] != get_schema_version($link, true))
719 if ($_SESSION["uid"]) {
721 $result = db_query($link,
722 "SELECT pwd_hash FROM ttrss_users WHERE id = '".$_SESSION["uid"]."'");
724 $pwd_hash = db_fetch_result($result, 0, "pwd_hash");
726 if ($pwd_hash != $_SESSION["pwd_hash"]) {
731 /* if ($_SESSION["cookie_lifetime"] && $_SESSION["uid"]) {
733 //print_r($_SESSION);
735 if (time() > $_SESSION["cookie_lifetime"]) {
743 function load_user_plugins($link, $owner_uid) {
745 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
748 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
750 if (get_schema_version($link) > 100) {
751 $pluginhost->load_data();
756 function login_sequence($link) {
757 $_SESSION["prefs_cache"] = false;
759 if (SINGLE_USER_MODE
) {
761 authenticate_user($link, "admin", null);
763 load_user_plugins($link, $_SESSION["uid"]);
765 if (!$_SESSION["uid"] ||
!validate_session($link)) {
767 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
768 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
770 authenticate_user($link, null, null, true);
773 if (!$_SESSION["uid"]) render_login_form($link);
776 /* bump login timestamp */
777 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
779 $_SESSION["last_login_update"] = time();
782 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
783 setcookie("ttrss_lang", $_SESSION["language"],
784 time() + SESSION_COOKIE_LIFETIME
);
787 if ($_SESSION["uid"]) {
789 load_user_plugins($link, $_SESSION["uid"]);
793 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
794 $_SESSION["uid"] . " AND
795 (SELECT COUNT(id) FROM ttrss_feeds WHERE
796 ttrss_feeds.id = feed_id) = 0");
798 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
799 $_SESSION["uid"] . " AND
800 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
801 ttrss_feed_categories.id = feed_id) = 0");
808 function truncate_string($str, $max_len, $suffix = '…') {
809 if (mb_strlen($str, "utf-8") > $max_len - 3) {
810 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
816 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
819 $source_tz = new DateTimeZone($source_tz);
820 } catch (Exception
$e) {
821 $source_tz = new DateTimeZone('UTC');
825 $dest_tz = new DateTimeZone($dest_tz);
826 } catch (Exception
$e) {
827 $dest_tz = new DateTimeZone('UTC');
830 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
831 return $dt->format('U') +
$dest_tz->getOffset($dt);
834 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
835 $no_smart_dt = false) {
837 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
838 if (!$timestamp) $timestamp = '1970-01-01 0:00';
843 # We store date in UTC internally
844 $dt = new DateTime($timestamp, $utc_tz);
846 if ($tz_offset == -1) {
848 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
851 $user_tz = new DateTimeZone($user_tz_string);
852 } catch (Exception
$e) {
856 $tz_offset = $user_tz->getOffset($dt);
859 $user_timestamp = $dt->format('U') +
$tz_offset;
862 return smart_date_time($link, $user_timestamp,
863 $tz_offset, $owner_uid);
866 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
868 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
870 return date($format, $user_timestamp);
874 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
875 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
877 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
878 return date("G:i", $timestamp);
879 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
880 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
881 return date($format, $timestamp);
883 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
884 return date($format, $timestamp);
888 function sql_bool_to_bool($s) {
889 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
896 function bool_to_sql_bool($s) {
904 // Session caching removed due to causing wrong redirects to upgrade
905 // script when get_schema_version() is called on an obsolete session
906 // created on a previous schema version.
907 function get_schema_version($link, $nocache = false) {
908 global $schema_version;
910 if (!$schema_version) {
911 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
912 $version = db_fetch_result($result, 0, "schema_version");
913 $schema_version = $version;
916 return $schema_version;
920 function sanity_check($link) {
921 require_once 'errors.php';
924 $schema_version = get_schema_version($link, true);
926 if ($schema_version != SCHEMA_VERSION
) {
930 if (DB_TYPE
== "mysql") {
931 $result = db_query($link, "SELECT true", false);
932 if (db_num_rows($result) != 1) {
937 if (db_escape_string($link, "testTEST") != "testTEST") {
941 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
944 function file_is_locked($filename) {
945 if (function_exists('flock')) {
946 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
948 if (flock($fp, LOCK_EX | LOCK_NB
)) {
959 return true; // consider the file always locked and skip the test
962 function make_lockfile($filename) {
963 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
965 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
966 if (function_exists('posix_getpid')) {
967 fwrite($fp, posix_getpid() . "\n");
975 function make_stampfile($filename) {
976 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
978 if (flock($fp, LOCK_EX | LOCK_NB
)) {
979 fwrite($fp, time() . "\n");
988 function sql_random_function() {
989 if (DB_TYPE
== "mysql") {
996 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false) {
998 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
1000 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
1002 if (is_numeric($feed)) {
1008 $children = getChildCategories($link, $feed, $owner_uid);
1009 array_push($children, $feed);
1011 $children = join(",", $children);
1013 $cat_qpart = "cat_id IN ($children)";
1015 $cat_qpart = "cat_id IS NULL";
1018 db_query($link, "UPDATE ttrss_user_entries
1019 SET unread = false,last_read = NOW()
1020 WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart)
1022 AND owner_uid = $owner_uid");
1024 } else if ($feed == -2) {
1026 db_query($link, "UPDATE ttrss_user_entries
1027 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1028 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1029 AND unread = true AND owner_uid = $owner_uid");
1032 } else if ($feed > 0) {
1034 db_query($link, "UPDATE ttrss_user_entries
1035 SET unread = false,last_read = NOW()
1036 WHERE feed_id = '$feed'
1038 AND owner_uid = $owner_uid");
1040 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1043 db_query($link, "UPDATE ttrss_user_entries
1044 SET unread = false,last_read = NOW()
1047 AND owner_uid = $owner_uid");
1051 db_query($link, "UPDATE ttrss_user_entries
1052 SET unread = false,last_read = NOW()
1053 WHERE published = true
1055 AND owner_uid = $owner_uid");
1060 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1062 if (DB_TYPE
== "pgsql") {
1063 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1065 $match_part = "updated > DATE_SUB(NOW(),
1066 INTERVAL $intl HOUR) ";
1069 $result = db_query($link, "SELECT id FROM ttrss_entries,
1070 ttrss_user_entries WHERE $match_part AND
1072 ttrss_user_entries.ref_id = ttrss_entries.id AND
1073 owner_uid = $owner_uid");
1075 $affected_ids = array();
1077 while ($line = db_fetch_assoc($result)) {
1078 array_push($affected_ids, $line["id"]);
1081 catchupArticlesById($link, $affected_ids, 0);
1085 db_query($link, "UPDATE ttrss_user_entries
1086 SET unread = false,last_read = NOW()
1087 WHERE unread = true AND
1088 owner_uid = $owner_uid");
1091 } else if ($feed < LABEL_BASE_INDEX
) { // label
1093 $label_id = feed_to_label_id($feed);
1095 db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
1096 SET unread = false, last_read = NOW()
1097 WHERE label_id = '$label_id' AND unread = true
1098 AND owner_uid = '$owner_uid' AND ref_id = article_id");
1102 ccache_update($link, $feed, $owner_uid, $cat_view);
1105 db_query($link, "BEGIN");
1107 $tag_name = db_escape_string($link, $feed);
1109 $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
1110 WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
1112 while ($line = db_fetch_assoc($result)) {
1113 db_query($link, "UPDATE ttrss_user_entries SET
1114 unread = false, last_read = NOW()
1116 AND int_id = " . $line["post_int_id"]);
1118 db_query($link, "COMMIT");
1122 function getAllCounters($link) {
1123 $data = getGlobalCounters($link);
1125 $data = array_merge($data, getVirtCounters($link));
1126 $data = array_merge($data, getLabelCounters($link));
1127 $data = array_merge($data, getFeedCounters($link, $active_feed));
1128 $data = array_merge($data, getCategoryCounters($link));
1133 function getCategoryTitle($link, $cat_id) {
1135 if ($cat_id == -1) {
1136 return __("Special");
1137 } else if ($cat_id == -2) {
1138 return __("Labels");
1141 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1144 if (db_num_rows($result) == 1) {
1145 return db_fetch_result($result, 0, "title");
1147 return __("Uncategorized");
1153 function getCategoryCounters($link) {
1156 /* Labels category */
1158 $cv = array("id" => -2, "kind" => "cat",
1159 "counter" => getCategoryUnread($link, -2));
1161 array_push($ret_arr, $cv);
1163 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1164 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1165 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1166 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1167 WHERE ttrss_cat_counters_cache.feed_id = id AND
1168 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1169 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1171 while ($line = db_fetch_assoc($result)) {
1172 $line["cat_id"] = (int) $line["cat_id"];
1174 if ($line["num_children"] > 0) {
1175 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1180 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1181 "counter" => $line["unread"] +
$child_counter);
1183 array_push($ret_arr, $cv);
1186 /* Special case: NULL category doesn't actually exist in the DB */
1188 $cv = array("id" => 0, "kind" => "cat",
1189 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1191 array_push($ret_arr, $cv);
1196 // only accepts real cats (>= 0)
1197 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1198 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1200 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1201 AND owner_uid = $owner_uid");
1205 while ($line = db_fetch_assoc($result)) {
1206 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1207 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1213 function getCategoryUnread($link, $cat, $owner_uid = false) {
1215 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1220 $cat_query = "cat_id = '$cat'";
1222 $cat_query = "cat_id IS NULL";
1225 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1226 AND owner_uid = " . $owner_uid);
1228 $cat_feeds = array();
1229 while ($line = db_fetch_assoc($result)) {
1230 array_push($cat_feeds, "feed_id = " . $line["id"]);
1233 if (count($cat_feeds) == 0) return 0;
1235 $match_part = implode(" OR ", $cat_feeds);
1237 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1238 FROM ttrss_user_entries
1239 WHERE unread = true AND ($match_part)
1240 AND owner_uid = " . $owner_uid);
1244 # this needs to be rewritten
1245 while ($line = db_fetch_assoc($result)) {
1246 $unread +
= $line["unread"];
1250 } else if ($cat == -1) {
1251 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1252 } else if ($cat == -2) {
1254 $result = db_query($link, "
1255 SELECT COUNT(unread) AS unread FROM
1256 ttrss_user_entries, ttrss_user_labels2
1257 WHERE article_id = ref_id AND unread = true
1258 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1260 $unread = db_fetch_result($result, 0, "unread");
1267 function getFeedUnread($link, $feed, $is_cat = false) {
1268 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1271 function getLabelUnread($link, $label_id, $owner_uid = false) {
1272 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1274 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1275 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1277 if (db_num_rows($result) != 0) {
1278 return db_fetch_result($result, 0, "unread");
1284 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1285 $owner_uid = false) {
1287 $n_feed = (int) $feed;
1288 $need_entries = false;
1290 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1293 $unread_qpart = "unread = true";
1295 $unread_qpart = "true";
1299 return getCategoryUnread($link, $n_feed, $owner_uid);
1300 } else if ($n_feed == -6) {
1302 } else if ($feed != "0" && $n_feed == 0) {
1304 $feed = db_escape_string($link, $feed);
1306 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1307 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1308 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1309 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1310 return db_fetch_result($result, 0, "count");
1312 } else if ($n_feed == -1) {
1313 $match_part = "marked = true";
1314 } else if ($n_feed == -2) {
1315 $match_part = "published = true";
1316 } else if ($n_feed == -3) {
1317 $match_part = "unread = true AND score >= 0";
1319 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1321 if (DB_TYPE
== "pgsql") {
1322 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1324 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1327 $need_entries = true;
1329 } else if ($n_feed == -4) {
1330 $match_part = "true";
1331 } else if ($n_feed >= 0) {
1334 $match_part = "feed_id = '$n_feed'";
1336 $match_part = "feed_id IS NULL";
1339 } else if ($feed < LABEL_BASE_INDEX
) {
1341 $label_id = feed_to_label_id($feed);
1343 return getLabelUnread($link, $label_id, $owner_uid);
1349 if ($need_entries) {
1350 $from_qpart = "ttrss_user_entries,ttrss_entries";
1351 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1353 $from_qpart = "ttrss_user_entries";
1356 $query = "SELECT count(int_id) AS unread
1357 FROM $from_qpart WHERE
1358 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1360 //echo "[$feed/$query]\n";
1362 $result = db_query($link, $query);
1366 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1367 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1368 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1369 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1372 $unread = db_fetch_result($result, 0, "unread");
1377 function getGlobalUnread($link, $user_id = false) {
1380 $user_id = $_SESSION["uid"];
1383 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1384 WHERE owner_uid = '$user_id' AND feed_id > 0");
1386 $c_id = db_fetch_result($result, 0, "c_id");
1391 function getGlobalCounters($link, $global_unread = -1) {
1394 if ($global_unread == -1) {
1395 $global_unread = getGlobalUnread($link);
1398 $cv = array("id" => "global-unread",
1399 "counter" => (int) $global_unread);
1401 array_push($ret_arr, $cv);
1403 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1404 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1406 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1408 $cv = array("id" => "subscribed-feeds",
1409 "counter" => (int) $subscribed_feeds);
1411 array_push($ret_arr, $cv);
1416 function getVirtCounters($link) {
1420 for ($i = 0; $i >= -4; $i--) {
1422 $count = getFeedUnread($link, $i);
1424 $cv = array("id" => $i,
1425 "counter" => (int) $count);
1427 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1428 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1430 array_push($ret_arr, $cv);
1436 $feeds = $pluginhost->get_feeds(-1);
1438 if (is_array($feeds)) {
1439 foreach ($feeds as $feed) {
1440 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1441 "counter" => $feed['sender']->get_unread($feed['id']));
1443 array_push($ret_arr, $cv);
1451 function getLabelCounters($link, $descriptions = false) {
1455 $owner_uid = $_SESSION["uid"];
1457 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1458 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1459 (ttrss_labels2.id = label_id)
1460 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1461 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1462 ttrss_labels2.caption");
1464 while ($line = db_fetch_assoc($result)) {
1466 $id = label_to_feed_id($line["id"]);
1468 $label_name = $line["caption"];
1469 $count = $line["unread"];
1471 $cv = array("id" => $id,
1472 "counter" => (int) $count);
1475 $cv["description"] = $label_name;
1477 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1478 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1480 array_push($ret_arr, $cv);
1486 function getFeedCounters($link, $active_feed = false) {
1490 $query = "SELECT ttrss_feeds.id,
1492 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1493 last_error, value AS count
1494 FROM ttrss_feeds, ttrss_counters_cache
1495 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1496 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1497 AND ttrss_counters_cache.feed_id = id";
1499 $result = db_query($link, $query);
1500 $fctrs_modified = false;
1502 while ($line = db_fetch_assoc($result)) {
1505 $count = $line["count"];
1506 $last_error = htmlspecialchars($line["last_error"]);
1508 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1510 $has_img = feed_has_icon($id);
1512 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1515 $cv = array("id" => $id,
1516 "updated" => $last_updated,
1517 "counter" => (int) $count,
1518 "has_img" => (int) $has_img);
1521 $cv["error"] = $last_error;
1523 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1524 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1526 if ($active_feed && $id == $active_feed)
1527 $cv["title"] = truncate_string($line["title"], 30);
1529 array_push($ret_arr, $cv);
1536 function get_pgsql_version($link) {
1537 $result = db_query($link, "SELECT version() AS version");
1538 $version = explode(" ", db_fetch_result($result, 0, "version"));
1543 * @return array (code => Status code, message => error message if available)
1545 * 0 - OK, Feed already exists
1546 * 1 - OK, Feed added
1548 * 3 - URL content is HTML, no feeds available
1549 * 4 - URL content is HTML which contains multiple feeds.
1550 * Here you should call extractfeedurls in rpc-backend
1551 * to get all possible feeds.
1552 * 5 - Couldn't download the URL content.
1554 function subscribe_to_feed($link, $url, $cat_id = 0,
1555 $auth_login = '', $auth_pass = '') {
1557 global $fetch_last_error;
1559 require_once "include/rssfuncs.php";
1561 $url = fix_url($url);
1563 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1565 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1568 return array("code" => 5, "message" => $fetch_last_error);
1571 if (is_html($contents)) {
1572 $feedUrls = get_feeds_from_html($url, $contents);
1574 if (count($feedUrls) == 0) {
1575 return array("code" => 3);
1576 } else if (count($feedUrls) > 1) {
1577 return array("code" => 4, "feeds" => $feedUrls);
1579 //use feed url as new URL
1580 $url = key($feedUrls);
1583 if ($cat_id == "0" ||
!$cat_id) {
1584 $cat_qpart = "NULL";
1586 $cat_qpart = "'$cat_id'";
1589 $result = db_query($link,
1590 "SELECT id FROM ttrss_feeds
1591 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1593 if (db_num_rows($result) == 0) {
1594 $result = db_query($link,
1595 "INSERT INTO ttrss_feeds
1596 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1597 VALUES ('".$_SESSION["uid"]."', '$url',
1598 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1600 $result = db_query($link,
1601 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1602 AND owner_uid = " . $_SESSION["uid"]);
1604 $feed_id = db_fetch_result($result, 0, "id");
1607 update_rss_feed($link, $feed_id, true);
1610 return array("code" => 1);
1612 return array("code" => 0);
1616 function print_feed_select($link, $id, $default_id = "",
1617 $attributes = "", $include_all_feeds = true,
1618 $root_id = false, $nest_level = 0) {
1621 print "<select id=\"$id\" name=\"$id\" $attributes>";
1622 if ($include_all_feeds) {
1623 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1624 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1628 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1631 $parent_qpart = "parent_cat = '$root_id'";
1633 $parent_qpart = "parent_cat IS NULL";
1635 $result = db_query($link, "SELECT id,title,
1636 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1637 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1638 FROM ttrss_feed_categories
1639 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1641 while ($line = db_fetch_assoc($result)) {
1643 for ($i = 0; $i < $nest_level; $i++
)
1644 $line["title"] = " - " . $line["title"];
1646 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1648 printf("<option $is_selected value='CAT:%d'>%s</option>",
1649 $line["id"], htmlspecialchars($line["title"]));
1651 if ($line["num_children"] > 0)
1652 print_feed_select($link, $id, $default_id, $attributes,
1653 $include_all_feeds, $line["id"], $nest_level+
1);
1655 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1656 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1658 while ($fline = db_fetch_assoc($feed_result)) {
1659 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1661 $fline["title"] = " + " . $fline["title"];
1663 for ($i = 0; $i < $nest_level; $i++
)
1664 $fline["title"] = " - " . $fline["title"];
1666 printf("<option $is_selected value='%d'>%s</option>",
1667 $fline["id"], htmlspecialchars($fline["title"]));
1672 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1674 printf("<option $is_selected value='CAT:0'>%s</option>",
1675 __("Uncategorized"));
1677 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1678 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1680 while ($fline = db_fetch_assoc($feed_result)) {
1681 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1683 $fline["title"] = " + " . $fline["title"];
1685 for ($i = 0; $i < $nest_level; $i++
)
1686 $fline["title"] = " - " . $fline["title"];
1688 printf("<option $is_selected value='%d'>%s</option>",
1689 $fline["id"], htmlspecialchars($fline["title"]));
1694 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1695 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1697 while ($line = db_fetch_assoc($result)) {
1699 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1701 printf("<option $is_selected value='%d'>%s</option>",
1702 $line["id"], htmlspecialchars($line["title"]));
1711 function print_feed_cat_select($link, $id, $default_id,
1712 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1715 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1719 $parent_qpart = "parent_cat = '$root_id'";
1721 $parent_qpart = "parent_cat IS NULL";
1723 $result = db_query($link, "SELECT id,title,
1724 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1725 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1726 FROM ttrss_feed_categories
1727 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1729 while ($line = db_fetch_assoc($result)) {
1730 if ($line["id"] == $default_id) {
1731 $is_selected = "selected=\"1\"";
1736 for ($i = 0; $i < $nest_level; $i++
)
1737 $line["title"] = " - " . $line["title"];
1740 printf("<option $is_selected value='%d'>%s</option>",
1741 $line["id"], htmlspecialchars($line["title"]));
1743 if ($line["num_children"] > 0)
1744 print_feed_cat_select($link, $id, $default_id, $attributes,
1745 $include_all_cats, $line["id"], $nest_level+
1);
1749 if ($include_all_cats) {
1750 if (db_num_rows($result) > 0) {
1751 print "<option disabled=\"1\">--------</option>";
1754 if ($default_id == 0) {
1755 $is_selected = "selected=\"1\"";
1760 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1766 function checkbox_to_sql_bool($val) {
1767 return ($val == "on") ?
"true" : "false";
1770 function getFeedCatTitle($link, $id) {
1772 return __("Special");
1773 } else if ($id < LABEL_BASE_INDEX
) {
1774 return __("Labels");
1775 } else if ($id > 0) {
1776 $result = db_query($link, "SELECT ttrss_feed_categories.title
1777 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1778 cat_id = ttrss_feed_categories.id");
1779 if (db_num_rows($result) == 1) {
1780 return db_fetch_result($result, 0, "title");
1782 return __("Uncategorized");
1785 return "getFeedCatTitle($id) failed";
1790 function getFeedIcon($id) {
1793 return "images/archive.png";
1796 return "images/mark_set.svg";
1799 return "images/pub_set.svg";
1802 return "images/fresh.png";
1805 return "images/tag.png";
1808 return "images/recently_read.png";
1811 if ($id < LABEL_BASE_INDEX
) {
1812 return "images/label.png";
1814 if (file_exists(ICONS_DIR
. "/$id.ico"))
1815 return ICONS_URL
. "/$id.ico";
1821 function getFeedTitle($link, $id, $cat = false) {
1823 return getCategoryTitle($link, $id);
1824 } else if ($id == -1) {
1825 return __("Starred articles");
1826 } else if ($id == -2) {
1827 return __("Published articles");
1828 } else if ($id == -3) {
1829 return __("Fresh articles");
1830 } else if ($id == -4) {
1831 return __("All articles");
1832 } else if ($id === 0 ||
$id === "0") {
1833 return __("Archived articles");
1834 } else if ($id == -6) {
1835 return __("Recently read");
1836 } else if ($id < LABEL_BASE_INDEX
) {
1837 $label_id = feed_to_label_id($id);
1838 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1839 if (db_num_rows($result) == 1) {
1840 return db_fetch_result($result, 0, "caption");
1842 return "Unknown label ($label_id)";
1845 } else if (is_numeric($id) && $id > 0) {
1846 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1847 if (db_num_rows($result) == 1) {
1848 return db_fetch_result($result, 0, "title");
1850 return "Unknown feed ($id)";
1857 function make_init_params($link) {
1860 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1861 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1862 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1863 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1865 $params[strtolower($param)] = (int) get_pref($link, $param);
1868 $params["icons_url"] = ICONS_URL
;
1869 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1870 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1871 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1872 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1873 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1874 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1876 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1877 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1879 $max_feed_id = db_fetch_result($result, 0, "mid");
1880 $num_feeds = db_fetch_result($result, 0, "nf");
1882 $params["max_feed_id"] = (int) $max_feed_id;
1883 $params["num_feeds"] = (int) $num_feeds;
1885 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1886 $params["hotkeys"] = get_hotkeys_map($link);
1888 $params["csrf_token"] = $_SESSION["csrf_token"];
1889 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1891 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1896 function get_hotkeys_info($link) {
1898 __("Navigation") => array(
1899 "next_feed" => __("Open next feed"),
1900 "prev_feed" => __("Open previous feed"),
1901 "next_article" => __("Open next article"),
1902 "prev_article" => __("Open previous article"),
1903 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1904 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1905 "search_dialog" => __("Show search dialog")),
1906 __("Article") => array(
1907 "toggle_mark" => __("Toggle starred"),
1908 "toggle_publ" => __("Toggle published"),
1909 "toggle_unread" => __("Toggle unread"),
1910 "edit_tags" => __("Edit tags"),
1911 "dismiss_selected" => __("Dismiss selected"),
1912 "dismiss_read" => __("Dismiss read"),
1913 "open_in_new_window" => __("Open in new window"),
1914 "catchup_below" => __("Mark below as read"),
1915 "catchup_above" => __("Mark above as read"),
1916 "article_scroll_down" => __("Scroll down"),
1917 "article_scroll_up" => __("Scroll up"),
1918 "select_article_cursor" => __("Select article under cursor"),
1919 "email_article" => __("Email article"),
1920 "close_article" => __("Close/collapse article"),
1921 "toggle_widescreen" => __("Toggle widescreen mode"),
1922 "toggle_embed_original" => __("Toggle embed original")),
1923 __("Article selection") => array(
1924 "select_all" => __("Select all articles"),
1925 "select_unread" => __("Select unread"),
1926 "select_marked" => __("Select starred"),
1927 "select_published" => __("Select published"),
1928 "select_invert" => __("Invert selection"),
1929 "select_none" => __("Deselect everything")),
1930 __("Feed") => array(
1931 "feed_refresh" => __("Refresh current feed"),
1932 "feed_unhide_read" => __("Un/hide read feeds"),
1933 "feed_subscribe" => __("Subscribe to feed"),
1934 "feed_edit" => __("Edit feed"),
1935 "feed_catchup" => __("Mark as read"),
1936 "feed_reverse" => __("Reverse headlines"),
1937 "feed_debug_update" => __("Debug feed update"),
1938 "catchup_all" => __("Mark all feeds as read"),
1939 "cat_toggle_collapse" => __("Un/collapse current category"),
1940 "toggle_combined_mode" => __("Toggle combined mode")),
1941 __("Go to") => array(
1942 "goto_all" => __("All articles"),
1943 "goto_fresh" => __("Fresh"),
1944 "goto_marked" => __("Starred"),
1945 "goto_published" => __("Published"),
1946 "goto_tagcloud" => __("Tag cloud"),
1947 "goto_prefs" => __("Preferences")),
1948 __("Other") => array(
1949 "create_label" => __("Create label"),
1950 "create_filter" => __("Create filter"),
1951 "collapse_sidebar" => __("Un/collapse sidebar"),
1952 "help_dialog" => __("Show help dialog"))
1958 function get_hotkeys_map($link) {
1960 // "navigation" => array(
1963 "n" => "next_article",
1964 "p" => "prev_article",
1965 "(38)|up" => "prev_article",
1966 "(40)|down" => "next_article",
1967 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1968 // "^(40)|Ctrl-down" => "next_article_noscroll",
1969 "(191)|/" => "search_dialog",
1970 // "article" => array(
1971 "s" => "toggle_mark",
1972 "*s" => "toggle_publ",
1973 "u" => "toggle_unread",
1974 "*t" => "edit_tags",
1975 "*d" => "dismiss_selected",
1976 "*x" => "dismiss_read",
1977 "o" => "open_in_new_window",
1978 "c p" => "catchup_below",
1979 "c n" => "catchup_above",
1980 "*n" => "article_scroll_down",
1981 "*p" => "article_scroll_up",
1982 "*(38)|Shift+up" => "article_scroll_up",
1983 "*(40)|Shift+down" => "article_scroll_down",
1984 "a *w" => "toggle_widescreen",
1985 "a e" => "toggle_embed_original",
1986 "e" => "email_article",
1987 "a q" => "close_article",
1988 // "article_selection" => array(
1989 "a a" => "select_all",
1990 "a u" => "select_unread",
1991 "a *u" => "select_marked",
1992 "a p" => "select_published",
1993 "a i" => "select_invert",
1994 "a n" => "select_none",
1996 "f r" => "feed_refresh",
1997 "f a" => "feed_unhide_read",
1998 "f s" => "feed_subscribe",
1999 "f e" => "feed_edit",
2000 "f q" => "feed_catchup",
2001 "f x" => "feed_reverse",
2002 "f *d" => "feed_debug_update",
2003 "f *c" => "toggle_combined_mode",
2004 "*q" => "catchup_all",
2005 "x" => "cat_toggle_collapse",
2007 "g a" => "goto_all",
2008 "g f" => "goto_fresh",
2009 "g s" => "goto_marked",
2010 "g p" => "goto_published",
2011 "g t" => "goto_tagcloud",
2012 "g *p" => "goto_prefs",
2013 // "other" => array(
2014 "(9)|Tab" => "select_article_cursor", // tab
2015 "c l" => "create_label",
2016 "c f" => "create_filter",
2017 "c s" => "collapse_sidebar",
2018 "^(191)|Ctrl+/" => "help_dialog",
2021 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2022 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2023 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2027 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2028 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2031 $prefixes = array();
2033 foreach (array_keys($hotkeys) as $hotkey) {
2034 $pair = explode(" ", $hotkey, 2);
2036 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2037 array_push($prefixes, $pair[0]);
2041 return array($prefixes, $hotkeys);
2044 function make_runtime_info($link) {
2047 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2048 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2050 $max_feed_id = db_fetch_result($result, 0, "mid");
2051 $num_feeds = db_fetch_result($result, 0, "nf");
2053 $data["max_feed_id"] = (int) $max_feed_id;
2054 $data["num_feeds"] = (int) $num_feeds;
2056 $data['last_article_id'] = getLastArticleId($link);
2057 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2059 $data['dep_ts'] = calculate_dep_timestamp();
2060 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2062 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2064 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2066 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2068 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2071 $stamp_delta = time() - $stamp;
2073 if ($stamp_delta > 1800) {
2077 $_SESSION["daemon_stamp_check"] = time();
2080 $data['daemon_stamp_ok'] = $stamp_check;
2082 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2084 $data['daemon_stamp'] = $stamp_fmt;
2089 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2090 $new_version_details = @check_for_update
($link);
2092 $data['new_version_available'] = (int) ($new_version_details != false);
2094 $_SESSION["last_version_check"] = time();
2095 $_SESSION["version_data"] = $new_version_details;
2101 function search_to_sql($link, $search) {
2103 $search_query_part = "";
2105 $keywords = explode(" ", $search);
2106 $query_keywords = array();
2108 foreach ($keywords as $k) {
2109 if (strpos($k, "-") === 0) {
2116 $commandpair = explode(":", mb_strtolower($k), 2);
2118 if ($commandpair[0] == "note" && $commandpair[1]) {
2120 if ($commandpair[1] == "true")
2121 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2123 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2125 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2127 if ($commandpair[1] == "true")
2128 array_push($query_keywords, "($not (marked = true))");
2130 array_push($query_keywords, "($not (marked = false))");
2132 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2134 if ($commandpair[1] == "true")
2135 array_push($query_keywords, "($not (published = true))");
2137 array_push($query_keywords, "($not (published = false))");
2139 } else if (strpos($k, "@") === 0) {
2141 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2142 $orig_ts = strtotime(substr($k, 1));
2143 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2145 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2147 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2149 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2150 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2154 $search_query_part = implode("AND", $query_keywords);
2156 return $search_query_part;
2159 function getParentCategories($link, $cat, $owner_uid) {
2162 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2163 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2165 while ($line = db_fetch_assoc($result)) {
2166 array_push($rv, $line["parent_cat"]);
2167 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2173 function getChildCategories($link, $cat, $owner_uid) {
2176 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2177 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2179 while ($line = db_fetch_assoc($result)) {
2180 array_push($rv, $line["id"]);
2181 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2187 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) {
2189 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2191 $ext_tables_part = "";
2195 if (SPHINX_ENABLED
) {
2196 $ids = join(",", @sphinx_search
($search, 0, 500));
2199 $search_query_part = "ref_id IN ($ids) AND ";
2201 $search_query_part = "ref_id = -1 AND ";
2204 $search_query_part = search_to_sql($link, $search);
2205 $search_query_part .= " AND ";
2209 $search_query_part = "";
2214 if (DB_TYPE
== "pgsql") {
2215 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2217 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2220 $override_order = "updated DESC";
2222 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2224 // Try to check if SQL regexp implementation chokes on a valid regexp
2225 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2226 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2227 WHERE $filter_query_part LIMIT 1", false);
2230 $test = db_fetch_result($result, 0, "true_val");
2233 $filter_query_part = "false AND";
2235 $filter_query_part .= " AND";
2238 $filter_query_part = "false AND";
2242 $filter_query_part = "";
2246 $since_id_part = "ttrss_entries.id > $since_id AND ";
2248 $since_id_part = "";
2251 $view_query_part = "";
2253 if ($view_mode == "adaptive") {
2255 $view_query_part = " ";
2256 } else if ($feed != -1) {
2258 $unread = getFeedUnread($link, $feed, $cat_view);
2260 if ($cat_view && $feed > 0 && $include_children)
2261 $unread +
= getCategoryChildrenUnread($link, $feed);
2264 $view_query_part = " unread = true AND ";
2269 if ($view_mode == "marked") {
2270 $view_query_part = " marked = true AND ";
2273 if ($view_mode == "has_note") {
2274 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2277 if ($view_mode == "published") {
2278 $view_query_part = " published = true AND ";
2281 if ($view_mode == "unread" && $feed != -6) {
2282 $view_query_part = " unread = true AND ";
2286 $limit_query_part = "LIMIT " . $limit;
2289 $allow_archived = false;
2291 $vfeed_query_part = "";
2293 // override query strategy and enable feed display when searching globally
2294 if ($search && $search_mode == "all_feeds") {
2295 $query_strategy_part = "true";
2296 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2298 } else if (!is_numeric($feed)) {
2299 $query_strategy_part = "true";
2300 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2301 id = feed_id) as feed_title,";
2302 } else if ($search && $search_mode == "this_cat") {
2303 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2306 if ($include_children) {
2307 $subcats = getChildCategories($link, $feed, $owner_uid);
2308 array_push($subcats, $feed);
2309 $cats_qpart = join(",", $subcats);
2311 $cats_qpart = $feed;
2314 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2317 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2320 } else if ($feed > 0) {
2325 if ($include_children) {
2327 $subcats = getChildCategories($link, $feed, $owner_uid);
2329 array_push($subcats, $feed);
2330 $query_strategy_part = "cat_id IN (".
2331 implode(",", $subcats).")";
2334 $query_strategy_part = "cat_id = '$feed'";
2338 $query_strategy_part = "cat_id IS NULL";
2341 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2344 $query_strategy_part = "feed_id = '$feed'";
2346 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2347 $query_strategy_part = "feed_id IS NULL";
2348 $allow_archived = true;
2349 } else if ($feed == 0 && $cat_view) { // uncategorized
2350 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2351 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2352 } else if ($feed == -1) { // starred virtual feed
2353 $query_strategy_part = "marked = true";
2354 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2355 $allow_archived = true;
2357 if (!$override_order) {
2358 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2361 } else if ($feed == -2) { // published virtual feed OR labels category
2364 $query_strategy_part = "published = true";
2365 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2366 $allow_archived = true;
2368 if (!$override_order) {
2369 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2373 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2375 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2377 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2378 ttrss_user_labels2.article_id = ref_id";
2381 } else if ($feed == -6) { // recently read
2382 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2383 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2384 $allow_archived = true;
2386 if (!$override_order) $override_order = "last_read DESC";
2387 } else if ($feed == -3) { // fresh virtual feed
2388 $query_strategy_part = "unread = true AND score >= 0";
2390 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2392 if (DB_TYPE
== "pgsql") {
2393 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2395 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2398 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2399 } else if ($feed == -4) { // all articles virtual feed
2400 $query_strategy_part = "true";
2401 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2402 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2403 $label_id = feed_to_label_id($feed);
2405 $query_strategy_part = "label_id = '$label_id' AND
2406 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2407 ttrss_user_labels2.article_id = ref_id";
2409 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2410 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2411 $allow_archived = true;
2414 $query_strategy_part = "true";
2417 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2418 $date_sort_field = "updated";
2420 $date_sort_field = "date_entered";
2423 $order_by = "$date_sort_field DESC, updated DESC";
2425 if ($view_mode == "unread_first") {
2426 $order_by = "unread DESC, $order_by";
2429 if ($override_order) {
2430 $order_by = $override_order;
2436 $feed_title = T_sprintf("Search results: %s", $search);
2439 $feed_title = getCategoryTitle($link, $feed);
2441 if (is_numeric($feed) && $feed > 0) {
2442 $result = db_query($link, "SELECT title,site_url,last_error
2443 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2445 $feed_title = db_fetch_result($result, 0, "title");
2446 $feed_site_url = db_fetch_result($result, 0, "site_url");
2447 $last_error = db_fetch_result($result, 0, "last_error");
2449 $feed_title = getFeedTitle($link, $feed);
2454 $content_query_part = "content as content_preview, cached_content, ";
2456 if (is_numeric($feed)) {
2459 $feed_kind = "Feeds";
2461 $feed_kind = "Labels";
2464 if ($limit_query_part) {
2465 $offset_query_part = "OFFSET $offset";
2468 // proper override_order applied above
2469 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2470 if (!$override_order) {
2471 $order_by = "ttrss_feeds.title, $order_by";
2473 $order_by = "ttrss_feeds.title, $override_order";
2477 if (!$allow_archived) {
2478 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2479 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2482 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2483 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2486 $query = "SELECT DISTINCT
2489 ttrss_entries.id,ttrss_entries.title,
2493 always_display_enclosures,
2500 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2501 last_marked, last_published,
2509 ttrss_user_entries.ref_id = ttrss_entries.id AND
2510 ttrss_user_entries.owner_uid = '$owner_uid' AND
2515 $query_strategy_part ORDER BY $order_by
2516 $limit_query_part $offset_query_part";
2518 if ($_REQUEST["debug"]) print $query;
2520 $result = db_query($link, $query);
2525 $select_qpart = "SELECT DISTINCT " .
2529 "ttrss_entries.id as id," .
2542 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2543 "last_marked, last_published, " .
2546 $content_query_part .
2549 $feed_kind = "Tags";
2550 $all_tags = explode(",", $feed);
2551 if ($search_mode == 'any') {
2552 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2553 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2554 $where_qpart = " WHERE " .
2555 "ref_id = ttrss_entries.id AND " .
2556 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2557 "post_int_id = int_id AND $tag_sql AND " .
2559 $search_query_part .
2560 $query_strategy_part . " ORDER BY $order_by " .
2565 $sub_selects = array();
2566 $sub_ands = array();
2567 foreach ($all_tags as $term) {
2568 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");
2575 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2580 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2581 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2582 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2583 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2585 // error_log("TAG SQL: " . $tag_sql);
2586 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2588 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2589 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2592 return array($result, $feed_title, $feed_site_url, $last_error);
2596 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2597 if (!$owner) $owner = $_SESSION["uid"];
2599 $res = trim($str); if (!$res) return '';
2601 if (strpos($res, "href=") === false)
2602 $res = rewrite_urls($res);
2604 $charset_hack = '<head>
2605 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2608 $res = trim($res); if (!$res) return '';
2610 libxml_use_internal_errors(true);
2612 $doc = new DOMDocument();
2613 $doc->loadHTML($charset_hack . $res);
2614 $xpath = new DOMXPath($doc);
2616 $entries = $xpath->query('(//a[@href]|//img[@src])');
2618 foreach ($entries as $entry) {
2622 if ($entry->hasAttribute('href'))
2623 $entry->setAttribute('href',
2624 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2626 if ($entry->hasAttribute('src')) {
2627 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2629 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2631 if (file_exists($cached_filename)) {
2632 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2635 $entry->setAttribute('src', $src);
2638 if ($entry->nodeName
== 'img') {
2639 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2640 $force_remove_images ||
$_SESSION["bw_limit"]) {
2642 $p = $doc->createElement('p');
2644 $a = $doc->createElement('a');
2645 $a->setAttribute('href', $entry->getAttribute('src'));
2647 $a->appendChild(new DOMText($entry->getAttribute('src')));
2648 $a->setAttribute('target', '_blank');
2650 $p->appendChild($a);
2652 $entry->parentNode
->replaceChild($p, $entry);
2657 if (strtolower($entry->nodeName
) == "a") {
2658 $entry->setAttribute("target", "_blank");
2662 $entries = $xpath->query('//iframe');
2663 foreach ($entries as $entry) {
2664 $entry->setAttribute('sandbox', 'allow-scripts');
2668 $allowed_elements = array('a', 'address', 'audio', 'article',
2669 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2670 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2671 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2672 'header', 'html', 'i', 'img', 'ins', 'kbd',
2673 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2674 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2675 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2676 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2678 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2680 $disallowed_attributes = array('id', 'style', 'class');
2684 if (isset($pluginhost)) {
2685 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2686 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2687 if (is_array($retval)) {
2689 $allowed_elements = $retval[1];
2690 $disallowed_attributes = $retval[2];
2697 $doc->removeChild($doc->firstChild
); //remove doctype
2698 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2699 $res = $doc->saveHTML();
2703 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2704 $entries = $doc->getElementsByTagName("*");
2706 foreach ($entries as $entry) {
2707 if (!in_array($entry->nodeName
, $allowed_elements)) {
2708 $entry->parentNode
->removeChild($entry);
2711 if ($entry->hasAttributes()) {
2712 $attrs_to_remove = array();
2714 foreach ($entry->attributes
as $attr) {
2716 if (strpos($attr->nodeName
, 'on') === 0) {
2717 array_push($attrs_to_remove, $attr);
2720 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2721 array_push($attrs_to_remove, $attr);
2725 foreach ($attrs_to_remove as $attr) {
2726 $entry->removeAttributeNode($attr);
2734 function check_for_update($link) {
2735 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2736 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2737 "&iid=" . sha1(SELF_URL_PATH
);
2739 $version_data = @fetch_file_contents
($version_url);
2741 if ($version_data) {
2742 $version_data = json_decode($version_data, true);
2743 if ($version_data && $version_data['version']) {
2745 if (version_compare(VERSION
, $version_data['version']) == -1) {
2746 return $version_data;
2754 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2756 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2757 if (count($ids) == 0) return;
2761 foreach ($ids as $id) {
2762 array_push($tmp_ids, "ref_id = '$id'");
2765 $ids_qpart = join(" OR ", $tmp_ids);
2768 db_query($link, "UPDATE ttrss_user_entries SET
2769 unread = false,last_read = NOW()
2770 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2771 } else if ($cmode == 1) {
2772 db_query($link, "UPDATE ttrss_user_entries SET
2774 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2776 db_query($link, "UPDATE ttrss_user_entries SET
2777 unread = NOT unread,last_read = NOW()
2778 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2783 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2784 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2786 while ($line = db_fetch_assoc($result)) {
2787 ccache_update($link, $line["feed_id"], $owner_uid);
2791 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2793 $a_id = db_escape_string($link, $id);
2795 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2797 $query = "SELECT DISTINCT tag_name,
2798 owner_uid as owner FROM
2799 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2800 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2802 $obj_id = md5("TAGS:$owner_uid:$id");
2805 /* check cache first */
2807 if ($tag_cache === false) {
2808 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2809 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2811 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2815 $tags = explode(",", $tag_cache);
2818 /* do it the hard way */
2820 $tmp_result = db_query($link, $query);
2822 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2823 array_push($tags, $tmp_line["tag_name"]);
2826 /* update the cache */
2828 $tags_str = db_escape_string($link, join(",", $tags));
2830 db_query($link, "UPDATE ttrss_user_entries
2831 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2832 AND owner_uid = $owner_uid");
2838 function trim_array($array) {
2840 array_walk($tmp, 'trim');
2844 function tag_is_valid($tag) {
2845 if ($tag == '') return false;
2846 if (preg_match("/^[0-9]*$/", $tag)) return false;
2847 if (mb_strlen($tag) > 250) return false;
2849 if (function_exists('iconv')) {
2850 $tag = iconv("utf-8", "utf-8", $tag);
2853 if (!$tag) return false;
2858 function render_login_form($link) {
2859 require_once "login_form.php";
2863 // from http://developer.apple.com/internet/safari/faq.html
2864 function no_cache_incantation() {
2865 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2866 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2867 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2868 header("Cache-Control: post-check=0, pre-check=0", false);
2869 header("Pragma: no-cache"); // HTTP/1.0
2872 function format_warning($msg, $id = "") {
2874 return "<div class=\"warning\" id=\"$id\">
2875 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2878 function format_notice($msg, $id = "") {
2880 return "<div class=\"notice\" id=\"$id\">
2881 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2884 function format_error($msg, $id = "") {
2886 return "<div class=\"error\" id=\"$id\">
2887 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2890 function print_notice($msg) {
2891 return print format_notice($msg);
2894 function print_warning($msg) {
2895 return print format_warning($msg);
2898 function print_error($msg) {
2899 return print format_error($msg);
2903 function T_sprintf() {
2904 $args = func_get_args();
2905 return vsprintf(__(array_shift($args)), $args);
2908 function format_inline_player($link, $url, $ctype) {
2912 $url = htmlspecialchars($url);
2914 if (strpos($ctype, "audio/") === 0) {
2916 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2917 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2918 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2920 $id = 'AUDIO-' . uniqid();
2922 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2923 <source type=\"$ctype\" src=\"$url\"></source>
2926 $entry .= "<span onclick=\"player(this)\"
2927 title=\"".__("Click to play")."\" status=\"0\"
2928 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2932 $entry .= "<object type=\"application/x-shockwave-flash\"
2933 data=\"lib/button/musicplayer.swf?song_url=$url\"
2934 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2935 <param name=\"movie\"
2936 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2940 if ($entry) $entry .= " <a target=\"_blank\"
2941 href=\"$url\">" . basename($url) . "</a>";
2949 /* $filename = substr($url, strrpos($url, "/")+1);
2951 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2952 $filename . " (" . $ctype . ")" . "</a>"; */
2956 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2957 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2963 /* we can figure out feed_id from article id anyway, why do we
2964 * pass feed_id here? let's ignore the argument :( */
2966 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2967 WHERE ref_id = '$id'");
2969 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2971 $rv['feed_id'] = $feed_id;
2973 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2975 if ($mark_as_read) {
2976 $result = db_query($link, "UPDATE ttrss_user_entries
2977 SET unread = false,last_read = NOW()
2978 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2980 ccache_update($link, $feed_id, $owner_uid);
2983 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2984 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2985 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2986 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2993 FROM ttrss_entries,ttrss_user_entries
2994 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2998 $line = db_fetch_assoc($result);
3000 $tag_cache = $line["tag_cache"];
3002 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3003 unset($line["tag_cache"]);
3005 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3009 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
3010 $line = $p->hook_render_article($line);
3013 $num_comments = $line["num_comments"];
3014 $entry_comments = "";
3016 if ($num_comments > 0) {
3017 if ($line["comments"]) {
3018 $comments_url = htmlspecialchars($line["comments"]);
3020 $comments_url = htmlspecialchars($line["link"]);
3022 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3024 if ($line["comments"] && $line["link"] != $line["comments"]) {
3025 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3030 header("Content-Type: text/html");
3031 $rv['content'] .= "<html><head>
3032 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3033 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3034 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3035 </head><body id=\"ttrssZoom\">";
3038 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3040 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3042 $entry_author = $line["author"];
3044 if ($entry_author) {
3045 $entry_author = __(" - ") . $entry_author;
3048 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3051 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3053 if ($line["link"]) {
3054 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3055 title=\"".htmlspecialchars($line['title'])."\"
3057 htmlspecialchars($line["link"]) . "\">" .
3058 $line["title"] . "</a>" .
3059 "<span class='author'>$entry_author</span></div>";
3061 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3064 $tags_str = format_tags_string($line["tags"], $id);
3065 $tags_str_full = join(", ", $line["tags"]);
3067 if (!$tags_str_full) $tags_str_full = __("no tags");
3069 if (!$entry_comments) $entry_comments = " "; # placeholder
3071 $rv['content'] .= "<div class='postTags' style='float : right'>
3072 <img src='images/tag.png'
3073 class='tagsPic' alt='Tags' title='Tags'> ";
3076 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3077 <a title=\"".__('Edit tags for this article')."\"
3078 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3080 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3081 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3082 position=\"below\">$tags_str_full</div>";
3086 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3087 $rv['content'] .= $p->hook_article_button($line);
3092 $tags_str = strip_tags($tags_str);
3093 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3095 $rv['content'] .= "</div>";
3096 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3098 if ($line["orig_feed_id"]) {
3100 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3101 WHERE id = ".$line["orig_feed_id"]);
3103 if (db_num_rows($tmp_result) != 0) {
3105 $rv['content'] .= "<div clear='both'>";
3106 $rv['content'] .= __("Originally from:");
3108 $rv['content'] .= " ";
3110 $tmp_line = db_fetch_assoc($tmp_result);
3112 $rv['content'] .= "<a target='_blank'
3113 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3114 $tmp_line['title'] . "</a>";
3116 $rv['content'] .= " ";
3118 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3119 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3121 $rv['content'] .= "</div>";
3125 $rv['content'] .= "</div>";
3127 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3128 if ($line['note']) {
3129 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3131 $rv['content'] .= "</div>";
3133 $rv['content'] .= "<div class=\"postContent\">";
3135 $rv['content'] .= $line["content"];
3137 $rv['content'] .= format_article_enclosures($link, $id,
3138 $always_display_enclosures, $line["content"], $line["hide_images"]);
3140 $rv['content'] .= "</div>";
3142 $rv['content'] .= "</div>";
3148 <div class='footer'>
3149 <button onclick=\"return window.close()\">".
3150 __("Close this window")."</button></div>";
3151 $rv['content'] .= "</body></html>";
3158 function print_checkpoint($n, $s) {
3159 $ts = microtime(true);
3160 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3164 function sanitize_tag($tag) {
3167 $tag = mb_strtolower($tag, 'utf-8');
3169 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3171 // $tag = str_replace('"', "", $tag);
3172 // $tag = str_replace("+", " ", $tag);
3173 $tag = str_replace("technorati tag: ", "", $tag);
3178 function get_self_url_prefix() {
3179 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3180 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3182 return SELF_URL_PATH
;
3187 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3189 * @return string The Mozilla Firefox feed adding URL.
3191 function add_feed_url() {
3192 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3194 $url_path = get_self_url_prefix() .
3195 "/public.php?op=subscribe&feed_url=%s";
3197 } // function add_feed_url
3199 function encrypt_password($pass, $salt = '', $mode2 = false) {
3200 if ($salt && $mode2) {
3201 return "MODE2:" . hash('sha256', $salt . $pass);
3203 return "SHA1X:" . sha1("$salt:$pass");
3205 return "SHA1:" . sha1($pass);
3207 } // function encrypt_password
3209 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3212 $cat_id = (int)getFeedCategory($link, $feed_id);
3214 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3215 owner_uid = $owner_uid AND enabled = true");
3217 $check_cats = join(",", array_merge(
3218 getParentCategories($link, $cat_id, $owner_uid),
3221 while ($line = db_fetch_assoc($result)) {
3222 $filter_id = $line["id"];
3224 $result2 = db_query($link, "SELECT
3225 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3226 FROM ttrss_filters2_rules AS r,
3227 ttrss_filter_types AS t
3229 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3230 (feed_id IS NULL OR feed_id = '$feed_id') AND
3231 filter_type = t.id AND filter_id = '$filter_id'");
3236 while ($rule_line = db_fetch_assoc($result2)) {
3237 # print_r($rule_line);
3240 $rule["reg_exp"] = $rule_line["reg_exp"];
3241 $rule["type"] = $rule_line["type_name"];
3242 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3244 array_push($rules, $rule);
3247 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3248 FROM ttrss_filters2_actions AS a,
3249 ttrss_filter_actions AS t
3251 action_id = t.id AND filter_id = '$filter_id'");
3253 while ($action_line = db_fetch_assoc($result2)) {
3254 # print_r($action_line);
3257 $action["type"] = $action_line["type_name"];
3258 $action["param"] = $action_line["action_param"];
3260 array_push($actions, $action);
3265 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3266 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3267 $filter["rules"] = $rules;
3268 $filter["actions"] = $actions;
3270 if (count($rules) > 0 && count($actions) > 0) {
3271 array_push($filters, $filter);
3278 function get_score_pic($score) {
3280 return "score_high.png";
3281 } else if ($score > 0) {
3282 return "score_half_high.png";
3283 } else if ($score < -100) {
3284 return "score_low.png";
3285 } else if ($score < 0) {
3286 return "score_half_low.png";
3288 return "score_neutral.png";
3292 function feed_has_icon($id) {
3293 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3296 function init_connection($link) {
3299 if (DB_TYPE
== "pgsql") {
3300 pg_query($link, "set client_encoding = 'UTF-8'");
3301 pg_set_client_encoding("UNICODE");
3302 pg_query($link, "set datestyle = 'ISO, european'");
3303 pg_query($link, "set TIME ZONE 0");
3305 db_query($link, "SET time_zone = '+0:0'");
3307 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3308 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3314 $pluginhost = new PluginHost($link);
3315 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3319 print "Unable to connect to database:" . db_last_error();
3324 function format_tags_string($tags, $id) {
3327 $tags_nolinks_str = "";
3333 $formatted_tags = array();
3335 foreach ($tags as $tag) {
3337 $tag_escaped = str_replace("'", "\\'", $tag);
3339 if (mb_strlen($tag) > 30) {
3340 $tag = truncate_string($tag, 30);
3343 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3345 array_push($formatted_tags, $tag_str);
3347 $tmp_tags_str = implode(", ", $formatted_tags);
3349 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3354 $tags_str = implode(", ", $formatted_tags);
3356 if ($num_tags < count($tags)) {
3357 $tags_str .= ", …";
3360 if ($num_tags == 0) {
3361 $tags_str = __("no tags");
3368 function format_article_labels($labels, $id) {
3372 foreach ($labels as $l) {
3373 $labels_str .= sprintf("<span class='hlLabelRef'
3374 style='color : %s; background-color : %s'>%s</span>",
3375 $l[2], $l[3], $l[1]);
3382 function format_article_note($id, $note, $allow_edit = true) {
3384 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3385 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3386 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3392 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3393 if ($parent_cat_id) {
3394 $parent_qpart = "parent_cat = '$parent_cat_id'";
3395 $parent_insert = "'$parent_cat_id'";
3397 $parent_qpart = "parent_cat IS NULL";
3398 $parent_insert = "NULL";
3401 $result = db_query($link,
3402 "SELECT id FROM ttrss_feed_categories
3403 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3405 if (db_num_rows($result) == 0) {
3408 return db_fetch_result($result, 0, "id");
3412 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3414 if (!$feed_cat) return false;
3416 db_query($link, "BEGIN");
3418 if ($parent_cat_id) {
3419 $parent_qpart = "parent_cat = '$parent_cat_id'";
3420 $parent_insert = "'$parent_cat_id'";
3422 $parent_qpart = "parent_cat IS NULL";
3423 $parent_insert = "NULL";
3426 $result = db_query($link,
3427 "SELECT id FROM ttrss_feed_categories
3428 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3430 if (db_num_rows($result) == 0) {
3432 $result = db_query($link,
3433 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3434 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3436 db_query($link, "COMMIT");
3444 function getArticleFeed($link, $id) {
3445 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3446 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3448 if (db_num_rows($result) != 0) {
3449 return db_fetch_result($result, 0, "feed_id");
3456 * Fixes incomplete URLs by prepending "http://".
3457 * Also replaces feed:// with http://, and
3458 * prepends a trailing slash if the url is a domain name only.
3460 * @param string $url Possibly incomplete URL
3462 * @return string Fixed URL.
3464 function fix_url($url) {
3465 if (strpos($url, '://') === false) {
3466 $url = 'http://' . $url;
3467 } else if (substr($url, 0, 5) == 'feed:') {
3468 $url = 'http:' . substr($url, 5);
3471 //prepend slash if the URL has no slash in it
3472 // "http://www.example" -> "http://www.example/"
3473 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3477 if ($url != "http:///")
3483 function validate_feed_url($url) {
3484 $parts = parse_url($url);
3486 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3490 function get_article_enclosures($link, $id) {
3492 $query = "SELECT * FROM ttrss_enclosures
3493 WHERE post_id = '$id' AND content_url != ''";
3497 $result = db_query($link, $query);
3499 if (db_num_rows($result) > 0) {
3500 while ($line = db_fetch_assoc($result)) {
3501 array_push($rv, $line);
3508 function save_email_address($link, $email) {
3509 // FIXME: implement persistent storage of emails
3511 if (!$_SESSION['stored_emails'])
3512 $_SESSION['stored_emails'] = array();
3514 if (!in_array($email, $_SESSION['stored_emails']))
3515 array_push($_SESSION['stored_emails'], $email);
3519 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3521 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3523 $sql_is_cat = bool_to_sql_bool($is_cat);
3525 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3526 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3527 AND owner_uid = " . $owner_uid);
3529 if (db_num_rows($result) == 1) {
3530 return db_fetch_result($result, 0, "access_key");
3532 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3534 $result = db_query($link, "INSERT INTO ttrss_access_keys
3535 (access_key, feed_id, is_cat, owner_uid)
3536 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3543 function get_feeds_from_html($url, $content)
3545 $url = fix_url($url);
3546 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3548 libxml_use_internal_errors(true);
3550 $doc = new DOMDocument();
3551 $doc->loadHTML($content);
3552 $xpath = new DOMXPath($doc);
3553 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3554 $feedUrls = array();
3555 foreach ($entries as $entry) {
3556 if ($entry->hasAttribute('href')) {
3557 $title = $entry->getAttribute('title');
3559 $title = $entry->getAttribute('type');
3561 $feedUrl = rewrite_relative_url(
3562 $baseUrl, $entry->getAttribute('href')
3564 $feedUrls[$feedUrl] = $title;
3570 function is_html($content) {
3571 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3574 function url_is_html($url, $login = false, $pass = false) {
3575 return is_html(fetch_file_contents($url, false, $login, $pass));
3578 function print_label_select($link, $name, $value, $attributes = "") {
3580 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3581 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3583 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3584 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3586 while ($line = db_fetch_assoc($result)) {
3588 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3590 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3591 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3595 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3602 function format_article_enclosures($link, $id, $always_display_enclosures,
3603 $article_content, $hide_images = false) {
3605 $result = get_article_enclosures($link, $id);
3608 if (count($result) > 0) {
3610 $entries_html = array();
3612 $entries_inline = array();
3614 foreach ($result as $line) {
3616 $url = $line["content_url"];
3617 $ctype = $line["content_type"];
3619 if (!$ctype) $ctype = __("unknown type");
3621 $filename = substr($url, strrpos($url, "/")+
1);
3623 $player = format_inline_player($link, $url, $ctype);
3625 if ($player) array_push($entries_inline, $player);
3627 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3628 # $filename . " (" . $ctype . ")" . "</a>";
3630 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3631 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3633 array_push($entries_html, $entry);
3637 $entry["type"] = $ctype;
3638 $entry["filename"] = $filename;
3639 $entry["url"] = $url;
3641 array_push($entries, $entry);
3644 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3645 if ($always_display_enclosures ||
3646 !preg_match("/<img/i", $article_content)) {
3648 foreach ($entries as $entry) {
3650 if (preg_match("/image/", $entry["type"]) ||
3651 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3653 if (!$hide_images) {
3655 alt=\"".htmlspecialchars($entry["filename"])."\"
3656 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3658 $rv .= "<p><a target=\"_blank\"
3659 href=\"".htmlspecialchars($entry["url"])."\"
3660 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3668 if (count($entries_inline) > 0) {
3669 $rv .= "<hr clear='both'/>";
3670 foreach ($entries_inline as $entry) { $rv .= $entry; };
3671 $rv .= "<hr clear='both'/>";
3674 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3675 "<option value=''>" . __('Attachments')."</option>";
3677 foreach ($entries as $entry) {
3678 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3688 function getLastArticleId($link) {
3689 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3690 WHERE owner_uid = " . $_SESSION["uid"]);
3692 if (db_num_rows($result) == 1) {
3693 return db_fetch_result($result, 0, "id");
3699 function build_url($parts) {
3700 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3704 * Converts a (possibly) relative URL to a absolute one.
3706 * @param string $url Base URL (i.e. from where the document is)
3707 * @param string $rel_url Possibly relative URL in the document
3709 * @return string Absolute URL
3711 function rewrite_relative_url($url, $rel_url) {
3712 if (strpos($rel_url, "magnet:") === 0) {
3714 } else if (strpos($rel_url, "://") !== false) {
3716 } else if (strpos($rel_url, "//") === 0) {
3717 # protocol-relative URL (rare but they exist)
3719 } else if (strpos($rel_url, "/") === 0)
3721 $parts = parse_url($url);
3722 $parts['path'] = $rel_url;
3724 return build_url($parts);
3727 $parts = parse_url($url);
3728 if (!isset($parts['path'])) {
3729 $parts['path'] = '/';
3731 $dir = $parts['path'];
3732 if (substr($dir, -1) !== '/') {
3733 $dir = dirname($parts['path']);
3734 $dir !== '/' && $dir .= '/';
3736 $parts['path'] = $dir . $rel_url;
3738 return build_url($parts);
3742 function sphinx_search($query, $offset = 0, $limit = 30) {
3743 require_once 'lib/sphinxapi.php';
3745 $sphinxClient = new SphinxClient();
3747 $sphinxClient->SetServer('localhost', 9312);
3748 $sphinxClient->SetConnectTimeout(1);
3750 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3751 'feed_title' => 20));
3753 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3754 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3755 $sphinxClient->SetLimits($offset, $limit, 1000);
3756 $sphinxClient->SetArrayResult(false);
3757 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3759 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3763 if (is_array($result['matches'])) {
3764 foreach (array_keys($result['matches']) as $int_id) {
3765 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3766 array_push($ids, $ref_id);
3773 function cleanup_tags($link, $days = 14, $limit = 1000) {
3775 if (DB_TYPE
== "pgsql") {
3776 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3777 } else if (DB_TYPE
== "mysql") {
3778 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3783 while ($limit > 0) {
3786 $query = "SELECT ttrss_tags.id AS id
3787 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3788 WHERE post_int_id = int_id AND $interval_query AND
3789 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3791 $result = db_query($link, $query);
3795 while ($line = db_fetch_assoc($result)) {
3796 array_push($ids, $line['id']);
3799 if (count($ids) > 0) {
3800 $ids = join(",", $ids);
3803 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3804 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3809 $limit -= $limit_part;
3812 return $tags_deleted;
3815 function print_user_stylesheet($link) {
3816 $value = get_pref($link, 'USER_STYLESHEET');
3819 print "<style type=\"text/css\">";
3820 print str_replace("<br/>", "\n", $value);
3826 function rewrite_urls($html) {
3827 libxml_use_internal_errors(true);
3829 $charset_hack = '<head>
3830 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3833 $doc = new DOMDocument();
3834 $doc->loadHTML($charset_hack . $html);
3835 $xpath = new DOMXPath($doc);
3837 $entries = $xpath->query('//*/text()');
3839 foreach ($entries as $entry) {
3840 if (strstr($entry->wholeText
, "://") !== false) {
3841 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3842 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3844 if ($text != $entry->wholeText
) {
3845 $cdoc = new DOMDocument();
3846 $cdoc->loadHTML($charset_hack . $text);
3849 foreach ($cdoc->childNodes
as $cnode) {
3850 $cnode = $doc->importNode($cnode, true);
3853 $entry->parentNode
->insertBefore($cnode);
3857 $entry->parentNode
->removeChild($entry);
3863 $node = $doc->getElementsByTagName('body')->item(0);
3865 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3867 return $doc->saveXML($node);
3872 function filter_to_sql($link, $filter, $owner_uid) {
3875 if (DB_TYPE
== "pgsql")
3878 $reg_qpart = "REGEXP";
3880 foreach ($filter["rules"] AS $rule) {
3881 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3882 $rule['reg_exp']) !== FALSE;
3884 if ($regexp_valid) {
3886 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3888 switch ($rule["type"]) {
3890 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3891 $rule['reg_exp'] . "')";
3894 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3895 $rule['reg_exp'] . "')";
3898 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3899 $rule['reg_exp'] . "') OR LOWER(" .
3900 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3903 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3904 $rule['reg_exp'] . "')";
3907 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3908 $rule['reg_exp'] . "')";
3911 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3912 $rule['reg_exp'] . "')";
3916 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3918 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3919 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3922 if (isset($rule["cat_id"])) {
3924 if ($rule["cat_id"] > 0) {
3925 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3926 array_push($children, $rule["cat_id"]);
3928 $children = join(",", $children);
3930 $cat_qpart = "cat_id IN ($children)";
3932 $cat_qpart = "cat_id IS NULL";
3935 $qpart .= " AND $cat_qpart";
3938 array_push($query, "($qpart)");
3943 if (count($query) > 0) {
3944 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3946 $fullquery = "(false)";
3949 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3954 if (!function_exists('gzdecode')) {
3955 function gzdecode($string) { // no support for 2nd argument
3956 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3957 base64_encode($string));
3961 function get_random_bytes($length) {
3962 if (function_exists('openssl_random_pseudo_bytes')) {
3963 return openssl_random_pseudo_bytes($length);
3967 for ($i = 0; $i < $length; $i++
)
3968 $output .= chr(mt_rand(0, 255));
3974 function read_stdin() {
3975 $fp = fopen("php://stdin", "r");
3978 $line = trim(fgets($fp));
3986 function tmpdirname($path, $prefix) {
3987 // Use PHP's tmpfile function to create a temporary
3988 // directory name. Delete the file and keep the name.
3989 $tempname = tempnam($path,$prefix);
3993 if (!unlink($tempname))
3999 function getFeedCategory($link, $feed) {
4000 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
4001 WHERE id = '$feed'");
4003 if (db_num_rows($result) > 0) {
4004 return db_fetch_result($result, 0, "cat_id");
4011 function implements_interface($class, $interface) {
4012 return in_array($interface, class_implements($class));
4015 function geturl($url){
4017 (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');
4019 $curl = curl_init();
4020 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4021 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4022 $header[] = "Cache-Control: max-age=0";
4023 $header[] = "Connection: keep-alive";
4024 $header[] = "Keep-Alive: 300";
4025 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4026 $header[] = "Accept-Language: en-us,en;q=0.5";
4027 $header[] = "Pragma: ";
4029 curl_setopt($curl, CURLOPT_URL
, $url);
4030 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4031 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4032 curl_setopt($curl, CURLOPT_HEADER
, true);
4033 curl_setopt($curl, CURLOPT_REFERER
, $url);
4034 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4035 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4036 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4037 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4038 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4040 $html = curl_exec($curl);
4042 $status = curl_getinfo($curl);
4045 if($status['http_code']!=200){
4046 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4047 list($header) = explode("\r\n\r\n", $html, 2);
4049 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4050 $url = trim(str_replace($matches[1],"",$matches[0]));
4051 $url_parsed = parse_url($url);
4052 return (isset($url_parsed))?
geturl($url, $referer):'';
4055 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4056 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4057 # $handle = @fopen('./curl.error.log', 'a');
4058 # fwrite($handle, $line);
4064 function get_minified_js($files) {
4065 require_once 'lib/jshrink/Minifier.php';
4069 foreach ($files as $js) {
4070 if (!isset($_GET['debug'])) {
4071 $cached_file = CACHE_DIR
. "/js/$js.js";
4073 if (file_exists($cached_file) &&
4074 is_readable($cached_file) &&
4075 filemtime($cached_file) >= filemtime("js/$js.js")) {
4077 $rv .= file_get_contents($cached_file);
4080 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4081 file_put_contents($cached_file, $minified);
4085 $rv .= file_get_contents("js/$js.js");
4092 function stylesheet_tag($filename) {
4093 $timestamp = filemtime($filename);
4095 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4098 function javascript_tag($filename) {
4101 if (!(strpos($filename, "?") === FALSE)) {
4102 $query = substr($filename, strpos($filename, "?")+
1);
4103 $filename = substr($filename, 0, strpos($filename, "?"));
4106 $timestamp = filemtime($filename);
4108 if ($query) $timestamp .= "&$query";
4110 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4113 function calculate_dep_timestamp() {
4114 $files = array_merge(glob("js/*.js"), glob("*.css"));
4118 foreach ($files as $file) {
4119 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4125 function T_js_decl($s1, $s2) {
4127 $s1 = preg_replace("/\n/", "", $s1);
4128 $s2 = preg_replace("/\n/", "", $s2);
4130 $s1 = preg_replace("/\"/", "\\\"", $s1);
4131 $s2 = preg_replace("/\"/", "\\\"", $s2);
4133 return "T_messages[\"$s1\"] = \"$s2\";\n";
4137 function init_js_translations() {
4139 print 'var T_messages = new Object();
4142 if (T_messages[msg]) {
4143 return T_messages[msg];
4149 function ngettext(msg1, msg2, n) {
4150 return (parseInt(n) > 1) ? msg2 : msg1;
4153 $l10n = _get_reader();
4155 for ($i = 0; $i < $l10n->total
; $i++
) {
4156 $orig = $l10n->get_original_string($i);
4157 $translation = __($orig);
4159 print T_js_decl($orig, $translation);
4163 function label_to_feed_id($label) {
4164 return LABEL_BASE_INDEX
- 1 - abs($label);
4167 function feed_to_label_id($feed) {
4168 return LABEL_BASE_INDEX
- 1 +
abs($feed);