2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 109);
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 if (get_schema_version($link) < 63) {
552 db_query($link, "INSERT INTO ttrss_user_prefs
553 (owner_uid,pref_name,value) VALUES
554 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
557 db_query($link, "INSERT INTO ttrss_user_prefs
558 (owner_uid,pref_name,value, profile) VALUES
559 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
565 db_query($link, "COMMIT");
569 function get_ssl_certificate_id() {
570 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
571 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
572 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
573 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
574 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
579 function authenticate_user($link, $login, $password, $check_only = false) {
581 if (!SINGLE_USER_MODE
) {
586 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
588 $user_id = (int) $plugin->authenticate($login, $password);
591 $_SESSION["auth_module"] = strtolower(get_class($plugin));
596 if ($user_id && !$check_only) {
597 $_SESSION["uid"] = $user_id;
599 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
600 WHERE id = '$user_id'");
602 $_SESSION["name"] = db_fetch_result($result, 0, "login");
603 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
604 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
606 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
609 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
610 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
612 $_SESSION["last_version_check"] = time();
614 initialize_user_prefs($link, $_SESSION["uid"]);
623 $_SESSION["uid"] = 1;
624 $_SESSION["name"] = "admin";
625 $_SESSION["access_level"] = 10;
627 $_SESSION["hide_hello"] = true;
628 $_SESSION["hide_logout"] = true;
630 $_SESSION["auth_module"] = false;
632 if (!$_SESSION["csrf_token"]) {
633 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
636 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
638 initialize_user_prefs($link, $_SESSION["uid"]);
644 function make_password($length = 8) {
647 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
651 while ($i < $length) {
652 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
654 if (!strstr($password, $char)) {
662 // this is called after user is created to initialize default feeds, labels
665 // user preferences are checked on every login, not here
667 function initialize_user($link, $uid) {
669 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
670 values ('$uid', 'Tiny Tiny RSS: New Releases',
671 'http://tt-rss.org/releases.rss')");
673 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
674 values ('$uid', 'Tiny Tiny RSS: Forum',
675 'http://tt-rss.org/forum/rss.php')");
678 function logout_user() {
680 if (isset($_COOKIE[session_name()])) {
681 setcookie(session_name(), '', time()-42000, '/');
685 function validate_csrf($csrf_token) {
686 return $csrf_token == $_SESSION['csrf_token'];
689 function validate_session($link) {
690 if (SINGLE_USER_MODE
) return true;
692 $check_ip = $_SESSION['ip_address'];
694 switch (SESSION_CHECK_ADDRESS
) {
699 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
702 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.'));
703 $check_ip = substr($check_ip, 0, strrpos($check_ip, '.')+
1);
707 if ($check_ip && strpos($_SERVER['REMOTE_ADDR'], $check_ip) !== 0) {
708 $_SESSION["login_error_msg"] =
709 __("Session failed to validate (incorrect IP)");
713 if ($_SESSION["ref_schema_version"] != get_schema_version($link, true))
716 if ($_SESSION["uid"]) {
718 $result = db_query($link,
719 "SELECT pwd_hash FROM ttrss_users WHERE id = '".$_SESSION["uid"]."'");
721 $pwd_hash = db_fetch_result($result, 0, "pwd_hash");
723 if ($pwd_hash != $_SESSION["pwd_hash"]) {
728 /* if ($_SESSION["cookie_lifetime"] && $_SESSION["uid"]) {
730 //print_r($_SESSION);
732 if (time() > $_SESSION["cookie_lifetime"]) {
740 function load_user_plugins($link, $owner_uid) {
742 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
745 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
747 if (get_schema_version($link) > 100) {
748 $pluginhost->load_data();
753 function login_sequence($link) {
754 $_SESSION["prefs_cache"] = false;
756 if (SINGLE_USER_MODE
) {
758 authenticate_user($link, "admin", null);
760 load_user_plugins($link, $_SESSION["uid"]);
762 if (!$_SESSION["uid"] ||
!validate_session($link)) {
764 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
765 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
767 authenticate_user($link, null, null, true);
770 if (!$_SESSION["uid"]) render_login_form($link);
773 /* bump login timestamp */
774 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
776 $_SESSION["last_login_update"] = time();
779 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
780 setcookie("ttrss_lang", $_SESSION["language"],
781 time() + SESSION_COOKIE_LIFETIME
);
784 if ($_SESSION["uid"]) {
786 load_user_plugins($link, $_SESSION["uid"]);
790 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
791 $_SESSION["uid"] . " AND
792 (SELECT COUNT(id) FROM ttrss_feeds WHERE
793 ttrss_feeds.id = feed_id) = 0");
795 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
796 $_SESSION["uid"] . " AND
797 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
798 ttrss_feed_categories.id = feed_id) = 0");
805 function truncate_string($str, $max_len, $suffix = '…') {
806 if (mb_strlen($str, "utf-8") > $max_len - 3) {
807 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
813 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
816 $source_tz = new DateTimeZone($source_tz);
817 } catch (Exception
$e) {
818 $source_tz = new DateTimeZone('UTC');
822 $dest_tz = new DateTimeZone($dest_tz);
823 } catch (Exception
$e) {
824 $dest_tz = new DateTimeZone('UTC');
827 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
828 return $dt->format('U') +
$dest_tz->getOffset($dt);
831 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
832 $no_smart_dt = false) {
834 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
835 if (!$timestamp) $timestamp = '1970-01-01 0:00';
840 # We store date in UTC internally
841 $dt = new DateTime($timestamp, $utc_tz);
843 if ($tz_offset == -1) {
845 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
848 $user_tz = new DateTimeZone($user_tz_string);
849 } catch (Exception
$e) {
853 $tz_offset = $user_tz->getOffset($dt);
856 $user_timestamp = $dt->format('U') +
$tz_offset;
859 return smart_date_time($link, $user_timestamp,
860 $tz_offset, $owner_uid);
863 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
865 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
867 return date($format, $user_timestamp);
871 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
872 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
874 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
875 return date("G:i", $timestamp);
876 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
877 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
878 return date($format, $timestamp);
880 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
881 return date($format, $timestamp);
885 function sql_bool_to_bool($s) {
886 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
893 function bool_to_sql_bool($s) {
901 // Session caching removed due to causing wrong redirects to upgrade
902 // script when get_schema_version() is called on an obsolete session
903 // created on a previous schema version.
904 function get_schema_version($link, $nocache = false) {
905 global $schema_version;
907 if (!$schema_version) {
908 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
909 $version = db_fetch_result($result, 0, "schema_version");
910 $schema_version = $version;
913 return $schema_version;
917 function sanity_check($link) {
918 require_once 'errors.php';
921 $schema_version = get_schema_version($link, true);
923 if ($schema_version != SCHEMA_VERSION
) {
927 if (DB_TYPE
== "mysql") {
928 $result = db_query($link, "SELECT true", false);
929 if (db_num_rows($result) != 1) {
934 if (db_escape_string($link, "testTEST") != "testTEST") {
938 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
941 function file_is_locked($filename) {
942 if (function_exists('flock')) {
943 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
945 if (flock($fp, LOCK_EX | LOCK_NB
)) {
956 return true; // consider the file always locked and skip the test
959 function make_lockfile($filename) {
960 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
962 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
963 if (function_exists('posix_getpid')) {
964 fwrite($fp, posix_getpid() . "\n");
972 function make_stampfile($filename) {
973 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
975 if (flock($fp, LOCK_EX | LOCK_NB
)) {
976 fwrite($fp, time() . "\n");
985 function sql_random_function() {
986 if (DB_TYPE
== "mysql") {
993 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false) {
995 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
997 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
999 if (is_numeric($feed)) {
1005 $children = getChildCategories($link, $feed, $owner_uid);
1006 array_push($children, $feed);
1008 $children = join(",", $children);
1010 $cat_qpart = "cat_id IN ($children)";
1012 $cat_qpart = "cat_id IS NULL";
1015 db_query($link, "UPDATE ttrss_user_entries
1016 SET unread = false,last_read = NOW()
1017 WHERE feed_id IN (SELECT id FROM ttrss_feeds WHERE $cat_qpart)
1019 AND owner_uid = $owner_uid");
1021 } else if ($feed == -2) {
1023 db_query($link, "UPDATE ttrss_user_entries
1024 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1025 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1026 AND unread = true AND owner_uid = $owner_uid");
1029 } else if ($feed > 0) {
1031 db_query($link, "UPDATE ttrss_user_entries
1032 SET unread = false,last_read = NOW()
1033 WHERE feed_id = '$feed'
1035 AND owner_uid = $owner_uid");
1037 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1040 db_query($link, "UPDATE ttrss_user_entries
1041 SET unread = false,last_read = NOW()
1044 AND owner_uid = $owner_uid");
1048 db_query($link, "UPDATE ttrss_user_entries
1049 SET unread = false,last_read = NOW()
1050 WHERE published = true
1052 AND owner_uid = $owner_uid");
1057 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1059 if (DB_TYPE
== "pgsql") {
1060 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1062 $match_part = "updated > DATE_SUB(NOW(),
1063 INTERVAL $intl HOUR) ";
1066 $result = db_query($link, "SELECT id FROM ttrss_entries,
1067 ttrss_user_entries WHERE $match_part AND
1069 ttrss_user_entries.ref_id = ttrss_entries.id AND
1070 owner_uid = $owner_uid");
1072 $affected_ids = array();
1074 while ($line = db_fetch_assoc($result)) {
1075 array_push($affected_ids, $line["id"]);
1078 catchupArticlesById($link, $affected_ids, 0);
1082 db_query($link, "UPDATE ttrss_user_entries
1083 SET unread = false,last_read = NOW()
1084 WHERE unread = true AND
1085 owner_uid = $owner_uid");
1088 } else if ($feed < LABEL_BASE_INDEX
) { // label
1090 $label_id = feed_to_label_id($feed);
1092 db_query($link, "UPDATE ttrss_user_entries, ttrss_user_labels2
1093 SET unread = false, last_read = NOW()
1094 WHERE label_id = '$label_id' AND unread = true
1095 AND owner_uid = '$owner_uid' AND ref_id = article_id");
1099 ccache_update($link, $feed, $owner_uid, $cat_view);
1102 db_query($link, "BEGIN");
1104 $tag_name = db_escape_string($link, $feed);
1106 $result = db_query($link, "SELECT post_int_id FROM ttrss_tags
1107 WHERE tag_name = '$tag_name' AND owner_uid = $owner_uid");
1109 while ($line = db_fetch_assoc($result)) {
1110 db_query($link, "UPDATE ttrss_user_entries SET
1111 unread = false, last_read = NOW()
1113 AND int_id = " . $line["post_int_id"]);
1115 db_query($link, "COMMIT");
1119 function getAllCounters($link) {
1120 $data = getGlobalCounters($link);
1122 $data = array_merge($data, getVirtCounters($link));
1123 $data = array_merge($data, getLabelCounters($link));
1124 $data = array_merge($data, getFeedCounters($link, $active_feed));
1125 $data = array_merge($data, getCategoryCounters($link));
1130 function getCategoryTitle($link, $cat_id) {
1132 if ($cat_id == -1) {
1133 return __("Special");
1134 } else if ($cat_id == -2) {
1135 return __("Labels");
1138 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1141 if (db_num_rows($result) == 1) {
1142 return db_fetch_result($result, 0, "title");
1144 return __("Uncategorized");
1150 function getCategoryCounters($link) {
1153 /* Labels category */
1155 $cv = array("id" => -2, "kind" => "cat",
1156 "counter" => getCategoryUnread($link, -2));
1158 array_push($ret_arr, $cv);
1160 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1161 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1162 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1163 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1164 WHERE ttrss_cat_counters_cache.feed_id = id AND
1165 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1166 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1168 while ($line = db_fetch_assoc($result)) {
1169 $line["cat_id"] = (int) $line["cat_id"];
1171 if ($line["num_children"] > 0) {
1172 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1177 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1178 "counter" => $line["unread"] +
$child_counter);
1180 array_push($ret_arr, $cv);
1183 /* Special case: NULL category doesn't actually exist in the DB */
1185 $cv = array("id" => 0, "kind" => "cat",
1186 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1188 array_push($ret_arr, $cv);
1193 // only accepts real cats (>= 0)
1194 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1195 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1197 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1198 AND owner_uid = $owner_uid");
1202 while ($line = db_fetch_assoc($result)) {
1203 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1204 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1210 function getCategoryUnread($link, $cat, $owner_uid = false) {
1212 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1217 $cat_query = "cat_id = '$cat'";
1219 $cat_query = "cat_id IS NULL";
1222 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1223 AND owner_uid = " . $owner_uid);
1225 $cat_feeds = array();
1226 while ($line = db_fetch_assoc($result)) {
1227 array_push($cat_feeds, "feed_id = " . $line["id"]);
1230 if (count($cat_feeds) == 0) return 0;
1232 $match_part = implode(" OR ", $cat_feeds);
1234 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1235 FROM ttrss_user_entries
1236 WHERE unread = true AND ($match_part)
1237 AND owner_uid = " . $owner_uid);
1241 # this needs to be rewritten
1242 while ($line = db_fetch_assoc($result)) {
1243 $unread +
= $line["unread"];
1247 } else if ($cat == -1) {
1248 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1249 } else if ($cat == -2) {
1251 $result = db_query($link, "
1252 SELECT COUNT(unread) AS unread FROM
1253 ttrss_user_entries, ttrss_user_labels2
1254 WHERE article_id = ref_id AND unread = true
1255 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1257 $unread = db_fetch_result($result, 0, "unread");
1264 function getFeedUnread($link, $feed, $is_cat = false) {
1265 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1268 function getLabelUnread($link, $label_id, $owner_uid = false) {
1269 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1271 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1272 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1274 if (db_num_rows($result) != 0) {
1275 return db_fetch_result($result, 0, "unread");
1281 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1282 $owner_uid = false) {
1284 $n_feed = (int) $feed;
1285 $need_entries = false;
1287 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1290 $unread_qpart = "unread = true";
1292 $unread_qpart = "true";
1296 return getCategoryUnread($link, $n_feed, $owner_uid);
1297 } else if ($n_feed == -6) {
1299 } else if ($feed != "0" && $n_feed == 0) {
1301 $feed = db_escape_string($link, $feed);
1303 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1304 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1305 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1306 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1307 return db_fetch_result($result, 0, "count");
1309 } else if ($n_feed == -1) {
1310 $match_part = "marked = true";
1311 } else if ($n_feed == -2) {
1312 $match_part = "published = true";
1313 } else if ($n_feed == -3) {
1314 $match_part = "unread = true AND score >= 0";
1316 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1318 if (DB_TYPE
== "pgsql") {
1319 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1321 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1324 $need_entries = true;
1326 } else if ($n_feed == -4) {
1327 $match_part = "true";
1328 } else if ($n_feed >= 0) {
1331 $match_part = "feed_id = '$n_feed'";
1333 $match_part = "feed_id IS NULL";
1336 } else if ($feed < LABEL_BASE_INDEX
) {
1338 $label_id = feed_to_label_id($feed);
1340 return getLabelUnread($link, $label_id, $owner_uid);
1346 if ($need_entries) {
1347 $from_qpart = "ttrss_user_entries,ttrss_entries";
1348 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1350 $from_qpart = "ttrss_user_entries";
1353 $query = "SELECT count(int_id) AS unread
1354 FROM $from_qpart WHERE
1355 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1357 //echo "[$feed/$query]\n";
1359 $result = db_query($link, $query);
1363 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1364 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1365 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1366 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1369 $unread = db_fetch_result($result, 0, "unread");
1374 function getGlobalUnread($link, $user_id = false) {
1377 $user_id = $_SESSION["uid"];
1380 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1381 WHERE owner_uid = '$user_id' AND feed_id > 0");
1383 $c_id = db_fetch_result($result, 0, "c_id");
1388 function getGlobalCounters($link, $global_unread = -1) {
1391 if ($global_unread == -1) {
1392 $global_unread = getGlobalUnread($link);
1395 $cv = array("id" => "global-unread",
1396 "counter" => (int) $global_unread);
1398 array_push($ret_arr, $cv);
1400 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1401 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1403 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1405 $cv = array("id" => "subscribed-feeds",
1406 "counter" => (int) $subscribed_feeds);
1408 array_push($ret_arr, $cv);
1413 function getVirtCounters($link) {
1417 for ($i = 0; $i >= -4; $i--) {
1419 $count = getFeedUnread($link, $i);
1421 $cv = array("id" => $i,
1422 "counter" => (int) $count);
1424 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1425 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1427 array_push($ret_arr, $cv);
1433 $feeds = $pluginhost->get_feeds(-1);
1435 if (is_array($feeds)) {
1436 foreach ($feeds as $feed) {
1437 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1438 "counter" => $feed['sender']->get_unread($feed['id']));
1440 array_push($ret_arr, $cv);
1448 function getLabelCounters($link, $descriptions = false) {
1452 $owner_uid = $_SESSION["uid"];
1454 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1455 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1456 (ttrss_labels2.id = label_id)
1457 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1458 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1459 ttrss_labels2.caption");
1461 while ($line = db_fetch_assoc($result)) {
1463 $id = label_to_feed_id($line["id"]);
1465 $label_name = $line["caption"];
1466 $count = $line["unread"];
1468 $cv = array("id" => $id,
1469 "counter" => (int) $count);
1472 $cv["description"] = $label_name;
1474 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1475 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1477 array_push($ret_arr, $cv);
1483 function getFeedCounters($link, $active_feed = false) {
1487 $query = "SELECT ttrss_feeds.id,
1489 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1490 last_error, value AS count
1491 FROM ttrss_feeds, ttrss_counters_cache
1492 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1493 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1494 AND ttrss_counters_cache.feed_id = id";
1496 $result = db_query($link, $query);
1497 $fctrs_modified = false;
1499 while ($line = db_fetch_assoc($result)) {
1502 $count = $line["count"];
1503 $last_error = htmlspecialchars($line["last_error"]);
1505 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1507 $has_img = feed_has_icon($id);
1509 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1512 $cv = array("id" => $id,
1513 "updated" => $last_updated,
1514 "counter" => (int) $count,
1515 "has_img" => (int) $has_img);
1518 $cv["error"] = $last_error;
1520 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1521 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1523 if ($active_feed && $id == $active_feed)
1524 $cv["title"] = truncate_string($line["title"], 30);
1526 array_push($ret_arr, $cv);
1533 function get_pgsql_version($link) {
1534 $result = db_query($link, "SELECT version() AS version");
1535 $version = explode(" ", db_fetch_result($result, 0, "version"));
1540 * @return array (code => Status code, message => error message if available)
1542 * 0 - OK, Feed already exists
1543 * 1 - OK, Feed added
1545 * 3 - URL content is HTML, no feeds available
1546 * 4 - URL content is HTML which contains multiple feeds.
1547 * Here you should call extractfeedurls in rpc-backend
1548 * to get all possible feeds.
1549 * 5 - Couldn't download the URL content.
1551 function subscribe_to_feed($link, $url, $cat_id = 0,
1552 $auth_login = '', $auth_pass = '') {
1554 global $fetch_last_error;
1556 require_once "include/rssfuncs.php";
1558 $url = fix_url($url);
1560 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1562 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1565 return array("code" => 5, "message" => $fetch_last_error);
1568 if (is_html($contents)) {
1569 $feedUrls = get_feeds_from_html($url, $contents);
1571 if (count($feedUrls) == 0) {
1572 return array("code" => 3);
1573 } else if (count($feedUrls) > 1) {
1574 return array("code" => 4, "feeds" => $feedUrls);
1576 //use feed url as new URL
1577 $url = key($feedUrls);
1580 if ($cat_id == "0" ||
!$cat_id) {
1581 $cat_qpart = "NULL";
1583 $cat_qpart = "'$cat_id'";
1586 $result = db_query($link,
1587 "SELECT id FROM ttrss_feeds
1588 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1590 if (db_num_rows($result) == 0) {
1591 $result = db_query($link,
1592 "INSERT INTO ttrss_feeds
1593 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1594 VALUES ('".$_SESSION["uid"]."', '$url',
1595 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1597 $result = db_query($link,
1598 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1599 AND owner_uid = " . $_SESSION["uid"]);
1601 $feed_id = db_fetch_result($result, 0, "id");
1604 update_rss_feed($link, $feed_id, true);
1607 return array("code" => 1);
1609 return array("code" => 0);
1613 function print_feed_select($link, $id, $default_id = "",
1614 $attributes = "", $include_all_feeds = true,
1615 $root_id = false, $nest_level = 0) {
1618 print "<select id=\"$id\" name=\"$id\" $attributes>";
1619 if ($include_all_feeds) {
1620 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1621 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1625 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1628 $parent_qpart = "parent_cat = '$root_id'";
1630 $parent_qpart = "parent_cat IS NULL";
1632 $result = db_query($link, "SELECT id,title,
1633 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1634 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1635 FROM ttrss_feed_categories
1636 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1638 while ($line = db_fetch_assoc($result)) {
1640 for ($i = 0; $i < $nest_level; $i++
)
1641 $line["title"] = " - " . $line["title"];
1643 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1645 printf("<option $is_selected value='CAT:%d'>%s</option>",
1646 $line["id"], htmlspecialchars($line["title"]));
1648 if ($line["num_children"] > 0)
1649 print_feed_select($link, $id, $default_id, $attributes,
1650 $include_all_feeds, $line["id"], $nest_level+
1);
1652 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1653 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1655 while ($fline = db_fetch_assoc($feed_result)) {
1656 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1658 $fline["title"] = " + " . $fline["title"];
1660 for ($i = 0; $i < $nest_level; $i++
)
1661 $fline["title"] = " - " . $fline["title"];
1663 printf("<option $is_selected value='%d'>%s</option>",
1664 $fline["id"], htmlspecialchars($fline["title"]));
1669 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1671 printf("<option $is_selected value='CAT:0'>%s</option>",
1672 __("Uncategorized"));
1674 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1675 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1677 while ($fline = db_fetch_assoc($feed_result)) {
1678 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1680 $fline["title"] = " + " . $fline["title"];
1682 for ($i = 0; $i < $nest_level; $i++
)
1683 $fline["title"] = " - " . $fline["title"];
1685 printf("<option $is_selected value='%d'>%s</option>",
1686 $fline["id"], htmlspecialchars($fline["title"]));
1691 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1692 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1694 while ($line = db_fetch_assoc($result)) {
1696 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1698 printf("<option $is_selected value='%d'>%s</option>",
1699 $line["id"], htmlspecialchars($line["title"]));
1708 function print_feed_cat_select($link, $id, $default_id,
1709 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1712 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1716 $parent_qpart = "parent_cat = '$root_id'";
1718 $parent_qpart = "parent_cat IS NULL";
1720 $result = db_query($link, "SELECT id,title,
1721 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1722 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1723 FROM ttrss_feed_categories
1724 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1726 while ($line = db_fetch_assoc($result)) {
1727 if ($line["id"] == $default_id) {
1728 $is_selected = "selected=\"1\"";
1733 for ($i = 0; $i < $nest_level; $i++
)
1734 $line["title"] = " - " . $line["title"];
1737 printf("<option $is_selected value='%d'>%s</option>",
1738 $line["id"], htmlspecialchars($line["title"]));
1740 if ($line["num_children"] > 0)
1741 print_feed_cat_select($link, $id, $default_id, $attributes,
1742 $include_all_cats, $line["id"], $nest_level+
1);
1746 if ($include_all_cats) {
1747 if (db_num_rows($result) > 0) {
1748 print "<option disabled=\"1\">--------</option>";
1751 if ($default_id == 0) {
1752 $is_selected = "selected=\"1\"";
1757 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1763 function checkbox_to_sql_bool($val) {
1764 return ($val == "on") ?
"true" : "false";
1767 function getFeedCatTitle($link, $id) {
1769 return __("Special");
1770 } else if ($id < LABEL_BASE_INDEX
) {
1771 return __("Labels");
1772 } else if ($id > 0) {
1773 $result = db_query($link, "SELECT ttrss_feed_categories.title
1774 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1775 cat_id = ttrss_feed_categories.id");
1776 if (db_num_rows($result) == 1) {
1777 return db_fetch_result($result, 0, "title");
1779 return __("Uncategorized");
1782 return "getFeedCatTitle($id) failed";
1787 function getFeedIcon($id) {
1790 return "images/archive.png";
1793 return "images/mark_set.svg";
1796 return "images/pub_set.svg";
1799 return "images/fresh.png";
1802 return "images/tag.png";
1805 return "images/recently_read.png";
1808 if ($id < LABEL_BASE_INDEX
) {
1809 return "images/label.png";
1811 if (file_exists(ICONS_DIR
. "/$id.ico"))
1812 return ICONS_URL
. "/$id.ico";
1818 function getFeedTitle($link, $id, $cat = false) {
1820 return getCategoryTitle($link, $id);
1821 } else if ($id == -1) {
1822 return __("Starred articles");
1823 } else if ($id == -2) {
1824 return __("Published articles");
1825 } else if ($id == -3) {
1826 return __("Fresh articles");
1827 } else if ($id == -4) {
1828 return __("All articles");
1829 } else if ($id === 0 ||
$id === "0") {
1830 return __("Archived articles");
1831 } else if ($id == -6) {
1832 return __("Recently read");
1833 } else if ($id < LABEL_BASE_INDEX
) {
1834 $label_id = feed_to_label_id($id);
1835 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1836 if (db_num_rows($result) == 1) {
1837 return db_fetch_result($result, 0, "caption");
1839 return "Unknown label ($label_id)";
1842 } else if (is_numeric($id) && $id > 0) {
1843 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1844 if (db_num_rows($result) == 1) {
1845 return db_fetch_result($result, 0, "title");
1847 return "Unknown feed ($id)";
1854 function make_init_params($link) {
1857 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1858 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1859 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1860 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1862 $params[strtolower($param)] = (int) get_pref($link, $param);
1865 $params["icons_url"] = ICONS_URL
;
1866 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1867 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1868 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1869 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1870 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1871 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1873 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1874 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1876 $max_feed_id = db_fetch_result($result, 0, "mid");
1877 $num_feeds = db_fetch_result($result, 0, "nf");
1879 $params["max_feed_id"] = (int) $max_feed_id;
1880 $params["num_feeds"] = (int) $num_feeds;
1882 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1883 $params["hotkeys"] = get_hotkeys_map($link);
1885 $params["csrf_token"] = $_SESSION["csrf_token"];
1886 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1888 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1893 function get_hotkeys_info($link) {
1895 __("Navigation") => array(
1896 "next_feed" => __("Open next feed"),
1897 "prev_feed" => __("Open previous feed"),
1898 "next_article" => __("Open next article"),
1899 "prev_article" => __("Open previous article"),
1900 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1901 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1902 "search_dialog" => __("Show search dialog")),
1903 __("Article") => array(
1904 "toggle_mark" => __("Toggle starred"),
1905 "toggle_publ" => __("Toggle published"),
1906 "toggle_unread" => __("Toggle unread"),
1907 "edit_tags" => __("Edit tags"),
1908 "dismiss_selected" => __("Dismiss selected"),
1909 "dismiss_read" => __("Dismiss read"),
1910 "open_in_new_window" => __("Open in new window"),
1911 "catchup_below" => __("Mark below as read"),
1912 "catchup_above" => __("Mark above as read"),
1913 "article_scroll_down" => __("Scroll down"),
1914 "article_scroll_up" => __("Scroll up"),
1915 "select_article_cursor" => __("Select article under cursor"),
1916 "email_article" => __("Email article"),
1917 "close_article" => __("Close/collapse article"),
1918 "toggle_widescreen" => __("Toggle widescreen mode"),
1919 "toggle_embed_original" => __("Toggle embed original")),
1920 __("Article selection") => array(
1921 "select_all" => __("Select all articles"),
1922 "select_unread" => __("Select unread"),
1923 "select_marked" => __("Select starred"),
1924 "select_published" => __("Select published"),
1925 "select_invert" => __("Invert selection"),
1926 "select_none" => __("Deselect everything")),
1927 __("Feed") => array(
1928 "feed_refresh" => __("Refresh current feed"),
1929 "feed_unhide_read" => __("Un/hide read feeds"),
1930 "feed_subscribe" => __("Subscribe to feed"),
1931 "feed_edit" => __("Edit feed"),
1932 "feed_catchup" => __("Mark as read"),
1933 "feed_reverse" => __("Reverse headlines"),
1934 "feed_debug_update" => __("Debug feed update"),
1935 "catchup_all" => __("Mark all feeds as read"),
1936 "cat_toggle_collapse" => __("Un/collapse current category"),
1937 "toggle_combined_mode" => __("Toggle combined mode")),
1938 __("Go to") => array(
1939 "goto_all" => __("All articles"),
1940 "goto_fresh" => __("Fresh"),
1941 "goto_marked" => __("Starred"),
1942 "goto_published" => __("Published"),
1943 "goto_tagcloud" => __("Tag cloud"),
1944 "goto_prefs" => __("Preferences")),
1945 __("Other") => array(
1946 "create_label" => __("Create label"),
1947 "create_filter" => __("Create filter"),
1948 "collapse_sidebar" => __("Un/collapse sidebar"),
1949 "help_dialog" => __("Show help dialog"))
1955 function get_hotkeys_map($link) {
1957 // "navigation" => array(
1960 "n" => "next_article",
1961 "p" => "prev_article",
1962 "(38)|up" => "prev_article",
1963 "(40)|down" => "next_article",
1964 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1965 // "^(40)|Ctrl-down" => "next_article_noscroll",
1966 "(191)|/" => "search_dialog",
1967 // "article" => array(
1968 "s" => "toggle_mark",
1969 "*s" => "toggle_publ",
1970 "u" => "toggle_unread",
1971 "*t" => "edit_tags",
1972 "*d" => "dismiss_selected",
1973 "*x" => "dismiss_read",
1974 "o" => "open_in_new_window",
1975 "c p" => "catchup_below",
1976 "c n" => "catchup_above",
1977 "*n" => "article_scroll_down",
1978 "*p" => "article_scroll_up",
1979 "*(38)|Shift+up" => "article_scroll_up",
1980 "*(40)|Shift+down" => "article_scroll_down",
1981 "a *w" => "toggle_widescreen",
1982 "a e" => "toggle_embed_original",
1983 "e" => "email_article",
1984 "a q" => "close_article",
1985 // "article_selection" => array(
1986 "a a" => "select_all",
1987 "a u" => "select_unread",
1988 "a *u" => "select_marked",
1989 "a p" => "select_published",
1990 "a i" => "select_invert",
1991 "a n" => "select_none",
1993 "f r" => "feed_refresh",
1994 "f a" => "feed_unhide_read",
1995 "f s" => "feed_subscribe",
1996 "f e" => "feed_edit",
1997 "f q" => "feed_catchup",
1998 "f x" => "feed_reverse",
1999 "f *d" => "feed_debug_update",
2000 "f *c" => "toggle_combined_mode",
2001 "*q" => "catchup_all",
2002 "x" => "cat_toggle_collapse",
2004 "g a" => "goto_all",
2005 "g f" => "goto_fresh",
2006 "g s" => "goto_marked",
2007 "g p" => "goto_published",
2008 "g t" => "goto_tagcloud",
2009 "g *p" => "goto_prefs",
2010 // "other" => array(
2011 "(9)|Tab" => "select_article_cursor", // tab
2012 "c l" => "create_label",
2013 "c f" => "create_filter",
2014 "c s" => "collapse_sidebar",
2015 "^(191)|Ctrl+/" => "help_dialog",
2018 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2019 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2020 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2024 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2025 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2028 $prefixes = array();
2030 foreach (array_keys($hotkeys) as $hotkey) {
2031 $pair = explode(" ", $hotkey, 2);
2033 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2034 array_push($prefixes, $pair[0]);
2038 return array($prefixes, $hotkeys);
2041 function make_runtime_info($link) {
2044 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2045 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2047 $max_feed_id = db_fetch_result($result, 0, "mid");
2048 $num_feeds = db_fetch_result($result, 0, "nf");
2050 $data["max_feed_id"] = (int) $max_feed_id;
2051 $data["num_feeds"] = (int) $num_feeds;
2053 $data['last_article_id'] = getLastArticleId($link);
2054 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2056 $data['dep_ts'] = calculate_dep_timestamp();
2057 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2059 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2061 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2063 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2065 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2068 $stamp_delta = time() - $stamp;
2070 if ($stamp_delta > 1800) {
2074 $_SESSION["daemon_stamp_check"] = time();
2077 $data['daemon_stamp_ok'] = $stamp_check;
2079 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2081 $data['daemon_stamp'] = $stamp_fmt;
2086 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2087 $new_version_details = @check_for_update
($link);
2089 $data['new_version_available'] = (int) ($new_version_details != false);
2091 $_SESSION["last_version_check"] = time();
2092 $_SESSION["version_data"] = $new_version_details;
2098 function search_to_sql($link, $search) {
2100 $search_query_part = "";
2102 $keywords = explode(" ", $search);
2103 $query_keywords = array();
2105 foreach ($keywords as $k) {
2106 if (strpos($k, "-") === 0) {
2113 $commandpair = explode(":", mb_strtolower($k), 2);
2115 if ($commandpair[0] == "note" && $commandpair[1]) {
2117 if ($commandpair[1] == "true")
2118 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2120 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2122 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2124 if ($commandpair[1] == "true")
2125 array_push($query_keywords, "($not (marked = true))");
2127 array_push($query_keywords, "($not (marked = false))");
2129 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2131 if ($commandpair[1] == "true")
2132 array_push($query_keywords, "($not (published = true))");
2134 array_push($query_keywords, "($not (published = false))");
2136 } else if (strpos($k, "@") === 0) {
2138 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2139 $orig_ts = strtotime(substr($k, 1));
2140 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2142 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2144 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2146 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2147 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2151 $search_query_part = implode("AND", $query_keywords);
2153 return $search_query_part;
2156 function getParentCategories($link, $cat, $owner_uid) {
2159 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2160 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2162 while ($line = db_fetch_assoc($result)) {
2163 array_push($rv, $line["parent_cat"]);
2164 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2170 function getChildCategories($link, $cat, $owner_uid) {
2173 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2174 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2176 while ($line = db_fetch_assoc($result)) {
2177 array_push($rv, $line["id"]);
2178 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2184 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) {
2186 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2188 $ext_tables_part = "";
2192 if (SPHINX_ENABLED
) {
2193 $ids = join(",", @sphinx_search
($search, 0, 500));
2196 $search_query_part = "ref_id IN ($ids) AND ";
2198 $search_query_part = "ref_id = -1 AND ";
2201 $search_query_part = search_to_sql($link, $search);
2202 $search_query_part .= " AND ";
2206 $search_query_part = "";
2211 if (DB_TYPE
== "pgsql") {
2212 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2214 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2217 $override_order = "updated DESC";
2219 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2221 // Try to check if SQL regexp implementation chokes on a valid regexp
2222 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2223 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2224 WHERE $filter_query_part LIMIT 1", false);
2227 $test = db_fetch_result($result, 0, "true_val");
2230 $filter_query_part = "false AND";
2232 $filter_query_part .= " AND";
2235 $filter_query_part = "false AND";
2239 $filter_query_part = "";
2243 $since_id_part = "ttrss_entries.id > $since_id AND ";
2245 $since_id_part = "";
2248 $view_query_part = "";
2250 if ($view_mode == "adaptive" ||
$view_query_part == "noscores") {
2252 $view_query_part = " ";
2253 } else if ($feed != -1) {
2255 $unread = getFeedUnread($link, $feed, $cat_view);
2257 if ($cat_view && $feed > 0 && $include_children)
2258 $unread +
= getCategoryChildrenUnread($link, $feed);
2261 $view_query_part = " unread = true AND ";
2266 if ($view_mode == "marked") {
2267 $view_query_part = " marked = true AND ";
2270 if ($view_mode == "has_note") {
2271 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2274 if ($view_mode == "published") {
2275 $view_query_part = " published = true AND ";
2278 if ($view_mode == "unread" && $feed != -6) {
2279 $view_query_part = " unread = true AND ";
2282 if ($view_mode == "updated") {
2283 $view_query_part = " (last_read is null and unread = false) AND ";
2287 $limit_query_part = "LIMIT " . $limit;
2290 $allow_archived = false;
2292 $vfeed_query_part = "";
2294 // override query strategy and enable feed display when searching globally
2295 if ($search && $search_mode == "all_feeds") {
2296 $query_strategy_part = "true";
2297 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2299 } else if (!is_numeric($feed)) {
2300 $query_strategy_part = "true";
2301 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2302 id = feed_id) as feed_title,";
2303 } else if ($search && $search_mode == "this_cat") {
2304 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2307 if ($include_children) {
2308 $subcats = getChildCategories($link, $feed, $owner_uid);
2309 array_push($subcats, $feed);
2310 $cats_qpart = join(",", $subcats);
2312 $cats_qpart = $feed;
2315 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2318 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2321 } else if ($feed > 0) {
2326 if ($include_children) {
2328 $subcats = getChildCategories($link, $feed, $owner_uid);
2330 array_push($subcats, $feed);
2331 $query_strategy_part = "cat_id IN (".
2332 implode(",", $subcats).")";
2335 $query_strategy_part = "cat_id = '$feed'";
2339 $query_strategy_part = "cat_id IS NULL";
2342 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2345 $query_strategy_part = "feed_id = '$feed'";
2347 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2348 $query_strategy_part = "feed_id IS NULL";
2349 $allow_archived = true;
2350 } else if ($feed == 0 && $cat_view) { // uncategorized
2351 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2352 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2353 } else if ($feed == -1) { // starred virtual feed
2354 $query_strategy_part = "marked = true";
2355 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2356 $allow_archived = true;
2358 if (!$override_order) {
2359 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2362 } else if ($feed == -2) { // published virtual feed OR labels category
2365 $query_strategy_part = "published = true";
2366 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2367 $allow_archived = true;
2369 if (!$override_order) {
2370 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2374 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2376 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2378 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2379 ttrss_user_labels2.article_id = ref_id";
2382 } else if ($feed == -6) { // recently read
2383 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2384 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2385 $allow_archived = true;
2387 if (!$override_order) $override_order = "last_read DESC";
2388 } else if ($feed == -3) { // fresh virtual feed
2389 $query_strategy_part = "unread = true AND score >= 0";
2391 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2393 if (DB_TYPE
== "pgsql") {
2394 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2396 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2399 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2400 } else if ($feed == -4) { // all articles virtual feed
2401 $query_strategy_part = "true";
2402 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2403 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2404 $label_id = feed_to_label_id($feed);
2406 $query_strategy_part = "label_id = '$label_id' AND
2407 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2408 ttrss_user_labels2.article_id = ref_id";
2410 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2411 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2412 $allow_archived = true;
2415 $query_strategy_part = "true";
2418 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2419 $date_sort_field = "updated";
2421 $date_sort_field = "date_entered";
2424 $order_by = "$date_sort_field DESC, updated DESC";
2426 if ($view_mode != "noscores") {
2427 $order_by = "score DESC, $order_by";
2430 if ($view_mode == "unread_first") {
2431 $order_by = "unread DESC, $order_by";
2434 if ($override_order) {
2435 $order_by = $override_order;
2441 $feed_title = T_sprintf("Search results: %s", $search);
2444 $feed_title = getCategoryTitle($link, $feed);
2446 if (is_numeric($feed) && $feed > 0) {
2447 $result = db_query($link, "SELECT title,site_url,last_error
2448 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2450 $feed_title = db_fetch_result($result, 0, "title");
2451 $feed_site_url = db_fetch_result($result, 0, "site_url");
2452 $last_error = db_fetch_result($result, 0, "last_error");
2454 $feed_title = getFeedTitle($link, $feed);
2459 $content_query_part = "content as content_preview, cached_content, ";
2461 if (is_numeric($feed)) {
2464 $feed_kind = "Feeds";
2466 $feed_kind = "Labels";
2469 if ($limit_query_part) {
2470 $offset_query_part = "OFFSET $offset";
2473 // proper override_order applied above
2474 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2475 if (!$override_order) {
2476 $order_by = "ttrss_feeds.title, $order_by";
2478 $order_by = "ttrss_feeds.title, $override_order";
2482 if (!$allow_archived) {
2483 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2484 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2487 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2488 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2491 $query = "SELECT DISTINCT
2494 ttrss_entries.id,ttrss_entries.title,
2498 always_display_enclosures,
2505 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2506 last_marked, last_published,
2514 ttrss_user_entries.ref_id = ttrss_entries.id AND
2515 ttrss_user_entries.owner_uid = '$owner_uid' AND
2520 $query_strategy_part ORDER BY $order_by
2521 $limit_query_part $offset_query_part";
2523 if ($_REQUEST["debug"]) print $query;
2525 $result = db_query($link, $query);
2530 $select_qpart = "SELECT DISTINCT " .
2534 "ttrss_entries.id as id," .
2547 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2548 "last_marked, last_published, " .
2551 $content_query_part .
2554 $feed_kind = "Tags";
2555 $all_tags = explode(",", $feed);
2556 if ($search_mode == 'any') {
2557 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2558 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2559 $where_qpart = " WHERE " .
2560 "ref_id = ttrss_entries.id AND " .
2561 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2562 "post_int_id = int_id AND $tag_sql AND " .
2564 $search_query_part .
2565 $query_strategy_part . " ORDER BY $order_by " .
2570 $sub_selects = array();
2571 $sub_ands = array();
2572 foreach ($all_tags as $term) {
2573 array_push($sub_selects, "(SELECT post_int_id from ttrss_tags WHERE tag_name = " . db_quote($term) . " AND owner_uid = $owner_uid) as A$i");
2580 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2585 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2586 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2587 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2588 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2590 // error_log("TAG SQL: " . $tag_sql);
2591 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2593 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2594 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2597 return array($result, $feed_title, $feed_site_url, $last_error);
2601 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2602 if (!$owner) $owner = $_SESSION["uid"];
2604 $res = trim($str); if (!$res) return '';
2606 if (strpos($res, "href=") === false)
2607 $res = rewrite_urls($res);
2609 $charset_hack = '<head>
2610 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2613 $res = trim($res); if (!$res) return '';
2615 libxml_use_internal_errors(true);
2617 $doc = new DOMDocument();
2618 $doc->loadHTML($charset_hack . $res);
2619 $xpath = new DOMXPath($doc);
2621 $entries = $xpath->query('(//a[@href]|//img[@src])');
2623 foreach ($entries as $entry) {
2627 if ($entry->hasAttribute('href'))
2628 $entry->setAttribute('href',
2629 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2631 if ($entry->hasAttribute('src')) {
2632 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2634 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2636 if (file_exists($cached_filename)) {
2637 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2640 $entry->setAttribute('src', $src);
2643 if ($entry->nodeName
== 'img') {
2644 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2645 $force_remove_images ||
$_SESSION["bw_limit"]) {
2647 $p = $doc->createElement('p');
2649 $a = $doc->createElement('a');
2650 $a->setAttribute('href', $entry->getAttribute('src'));
2652 $a->appendChild(new DOMText($entry->getAttribute('src')));
2653 $a->setAttribute('target', '_blank');
2655 $p->appendChild($a);
2657 $entry->parentNode
->replaceChild($p, $entry);
2662 if (strtolower($entry->nodeName
) == "a") {
2663 $entry->setAttribute("target", "_blank");
2667 $entries = $xpath->query('//iframe');
2668 foreach ($entries as $entry) {
2669 $entry->setAttribute('sandbox', 'allow-scripts');
2673 $allowed_elements = array('a', 'address', 'audio', 'article',
2674 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2675 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2676 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2677 'header', 'html', 'i', 'img', 'ins', 'kbd',
2678 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2679 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2680 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2681 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2683 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2685 $disallowed_attributes = array('id', 'style', 'class');
2689 if (isset($pluginhost)) {
2690 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2691 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2692 if (is_array($retval)) {
2694 $allowed_elements = $retval[1];
2695 $disallowed_attributes = $retval[2];
2702 $doc->removeChild($doc->firstChild
); //remove doctype
2703 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2704 $res = $doc->saveHTML();
2708 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2709 $entries = $doc->getElementsByTagName("*");
2711 foreach ($entries as $entry) {
2712 if (!in_array($entry->nodeName
, $allowed_elements)) {
2713 $entry->parentNode
->removeChild($entry);
2716 if ($entry->hasAttributes()) {
2717 $attrs_to_remove = array();
2719 foreach ($entry->attributes
as $attr) {
2721 if (strpos($attr->nodeName
, 'on') === 0) {
2722 array_push($attrs_to_remove, $attr);
2725 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2726 array_push($attrs_to_remove, $attr);
2730 foreach ($attrs_to_remove as $attr) {
2731 $entry->removeAttributeNode($attr);
2739 function check_for_update($link) {
2740 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2741 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2742 "&iid=" . sha1(SELF_URL_PATH
);
2744 $version_data = @fetch_file_contents
($version_url);
2746 if ($version_data) {
2747 $version_data = json_decode($version_data, true);
2748 if ($version_data && $version_data['version']) {
2750 if (version_compare(VERSION
, $version_data['version']) == -1) {
2751 return $version_data;
2759 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2761 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2762 if (count($ids) == 0) return;
2766 foreach ($ids as $id) {
2767 array_push($tmp_ids, "ref_id = '$id'");
2770 $ids_qpart = join(" OR ", $tmp_ids);
2773 db_query($link, "UPDATE ttrss_user_entries SET
2774 unread = false,last_read = NOW()
2775 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2776 } else if ($cmode == 1) {
2777 db_query($link, "UPDATE ttrss_user_entries SET
2779 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2781 db_query($link, "UPDATE ttrss_user_entries SET
2782 unread = NOT unread,last_read = NOW()
2783 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2788 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2789 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2791 while ($line = db_fetch_assoc($result)) {
2792 ccache_update($link, $line["feed_id"], $owner_uid);
2796 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2798 $a_id = db_escape_string($link, $id);
2800 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2802 $query = "SELECT DISTINCT tag_name,
2803 owner_uid as owner FROM
2804 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2805 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2807 $obj_id = md5("TAGS:$owner_uid:$id");
2810 /* check cache first */
2812 if ($tag_cache === false) {
2813 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2814 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2816 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2820 $tags = explode(",", $tag_cache);
2823 /* do it the hard way */
2825 $tmp_result = db_query($link, $query);
2827 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2828 array_push($tags, $tmp_line["tag_name"]);
2831 /* update the cache */
2833 $tags_str = db_escape_string($link, join(",", $tags));
2835 db_query($link, "UPDATE ttrss_user_entries
2836 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2837 AND owner_uid = $owner_uid");
2843 function trim_array($array) {
2845 array_walk($tmp, 'trim');
2849 function tag_is_valid($tag) {
2850 if ($tag == '') return false;
2851 if (preg_match("/^[0-9]*$/", $tag)) return false;
2852 if (mb_strlen($tag) > 250) return false;
2854 if (function_exists('iconv')) {
2855 $tag = iconv("utf-8", "utf-8", $tag);
2858 if (!$tag) return false;
2863 function render_login_form($link) {
2864 require_once "login_form.php";
2868 // from http://developer.apple.com/internet/safari/faq.html
2869 function no_cache_incantation() {
2870 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2871 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2872 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2873 header("Cache-Control: post-check=0, pre-check=0", false);
2874 header("Pragma: no-cache"); // HTTP/1.0
2877 function format_warning($msg, $id = "") {
2879 return "<div class=\"warning\" id=\"$id\">
2880 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2883 function format_notice($msg, $id = "") {
2885 return "<div class=\"notice\" id=\"$id\">
2886 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2889 function format_error($msg, $id = "") {
2891 return "<div class=\"error\" id=\"$id\">
2892 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2895 function print_notice($msg) {
2896 return print format_notice($msg);
2899 function print_warning($msg) {
2900 return print format_warning($msg);
2903 function print_error($msg) {
2904 return print format_error($msg);
2908 function T_sprintf() {
2909 $args = func_get_args();
2910 return vsprintf(__(array_shift($args)), $args);
2913 function format_inline_player($link, $url, $ctype) {
2917 $url = htmlspecialchars($url);
2919 if (strpos($ctype, "audio/") === 0) {
2921 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2922 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2923 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2925 $id = 'AUDIO-' . uniqid();
2927 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2928 <source type=\"$ctype\" src=\"$url\"></source>
2931 $entry .= "<span onclick=\"player(this)\"
2932 title=\"".__("Click to play")."\" status=\"0\"
2933 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2937 $entry .= "<object type=\"application/x-shockwave-flash\"
2938 data=\"lib/button/musicplayer.swf?song_url=$url\"
2939 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2940 <param name=\"movie\"
2941 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2945 if ($entry) $entry .= " <a target=\"_blank\"
2946 href=\"$url\">" . basename($url) . "</a>";
2954 /* $filename = substr($url, strrpos($url, "/")+1);
2956 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2957 $filename . " (" . $ctype . ")" . "</a>"; */
2961 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2962 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2968 /* we can figure out feed_id from article id anyway, why do we
2969 * pass feed_id here? let's ignore the argument :( */
2971 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2972 WHERE ref_id = '$id'");
2974 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2976 $rv['feed_id'] = $feed_id;
2978 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2980 if ($mark_as_read) {
2981 $result = db_query($link, "UPDATE ttrss_user_entries
2982 SET unread = false,last_read = NOW()
2983 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2985 ccache_update($link, $feed_id, $owner_uid);
2988 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2989 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2990 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2991 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2998 FROM ttrss_entries,ttrss_user_entries
2999 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3003 $line = db_fetch_assoc($result);
3005 $tag_cache = $line["tag_cache"];
3007 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
3008 unset($line["tag_cache"]);
3010 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
3014 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
3015 $line = $p->hook_render_article($line);
3018 $num_comments = $line["num_comments"];
3019 $entry_comments = "";
3021 if ($num_comments > 0) {
3022 if ($line["comments"]) {
3023 $comments_url = htmlspecialchars($line["comments"]);
3025 $comments_url = htmlspecialchars($line["link"]);
3027 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3029 if ($line["comments"] && $line["link"] != $line["comments"]) {
3030 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3035 header("Content-Type: text/html");
3036 $rv['content'] .= "<html><head>
3037 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3038 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3039 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3040 </head><body id=\"ttrssZoom\">";
3043 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3045 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3047 $entry_author = $line["author"];
3049 if ($entry_author) {
3050 $entry_author = __(" - ") . $entry_author;
3053 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3056 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3058 if ($line["link"]) {
3059 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3060 title=\"".htmlspecialchars($line['title'])."\"
3062 htmlspecialchars($line["link"]) . "\">" .
3063 $line["title"] . "</a>" .
3064 "<span class='author'>$entry_author</span></div>";
3066 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3069 $tags_str = format_tags_string($line["tags"], $id);
3070 $tags_str_full = join(", ", $line["tags"]);
3072 if (!$tags_str_full) $tags_str_full = __("no tags");
3074 if (!$entry_comments) $entry_comments = " "; # placeholder
3076 $rv['content'] .= "<div class='postTags' style='float : right'>
3077 <img src='images/tag.png'
3078 class='tagsPic' alt='Tags' title='Tags'> ";
3081 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3082 <a title=\"".__('Edit tags for this article')."\"
3083 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3085 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3086 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3087 position=\"below\">$tags_str_full</div>";
3091 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3092 $rv['content'] .= $p->hook_article_button($line);
3097 $tags_str = strip_tags($tags_str);
3098 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3100 $rv['content'] .= "</div>";
3101 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3103 if ($line["orig_feed_id"]) {
3105 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3106 WHERE id = ".$line["orig_feed_id"]);
3108 if (db_num_rows($tmp_result) != 0) {
3110 $rv['content'] .= "<div clear='both'>";
3111 $rv['content'] .= __("Originally from:");
3113 $rv['content'] .= " ";
3115 $tmp_line = db_fetch_assoc($tmp_result);
3117 $rv['content'] .= "<a target='_blank'
3118 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3119 $tmp_line['title'] . "</a>";
3121 $rv['content'] .= " ";
3123 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3124 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3126 $rv['content'] .= "</div>";
3130 $rv['content'] .= "</div>";
3132 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3133 if ($line['note']) {
3134 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3136 $rv['content'] .= "</div>";
3138 $rv['content'] .= "<div class=\"postContent\">";
3140 $rv['content'] .= $line["content"];
3142 $rv['content'] .= format_article_enclosures($link, $id,
3143 $always_display_enclosures, $line["content"], $line["hide_images"]);
3145 $rv['content'] .= "</div>";
3147 $rv['content'] .= "</div>";
3153 <div class='footer'>
3154 <button onclick=\"return window.close()\">".
3155 __("Close this window")."</button></div>";
3156 $rv['content'] .= "</body></html>";
3163 function print_checkpoint($n, $s) {
3164 $ts = microtime(true);
3165 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3169 function sanitize_tag($tag) {
3172 $tag = mb_strtolower($tag, 'utf-8');
3174 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3176 // $tag = str_replace('"', "", $tag);
3177 // $tag = str_replace("+", " ", $tag);
3178 $tag = str_replace("technorati tag: ", "", $tag);
3183 function get_self_url_prefix() {
3184 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3185 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3187 return SELF_URL_PATH
;
3192 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3194 * @return string The Mozilla Firefox feed adding URL.
3196 function add_feed_url() {
3197 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3199 $url_path = get_self_url_prefix() .
3200 "/public.php?op=subscribe&feed_url=%s";
3202 } // function add_feed_url
3204 function encrypt_password($pass, $salt = '', $mode2 = false) {
3205 if ($salt && $mode2) {
3206 return "MODE2:" . hash('sha256', $salt . $pass);
3208 return "SHA1X:" . sha1("$salt:$pass");
3210 return "SHA1:" . sha1($pass);
3212 } // function encrypt_password
3214 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3217 $cat_id = (int)getFeedCategory($link, $feed_id);
3219 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3220 owner_uid = $owner_uid AND enabled = true");
3222 $check_cats = join(",", array_merge(
3223 getParentCategories($link, $cat_id, $owner_uid),
3226 while ($line = db_fetch_assoc($result)) {
3227 $filter_id = $line["id"];
3229 $result2 = db_query($link, "SELECT
3230 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3231 FROM ttrss_filters2_rules AS r,
3232 ttrss_filter_types AS t
3234 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3235 (feed_id IS NULL OR feed_id = '$feed_id') AND
3236 filter_type = t.id AND filter_id = '$filter_id'");
3241 while ($rule_line = db_fetch_assoc($result2)) {
3242 # print_r($rule_line);
3245 $rule["reg_exp"] = $rule_line["reg_exp"];
3246 $rule["type"] = $rule_line["type_name"];
3247 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3249 array_push($rules, $rule);
3252 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3253 FROM ttrss_filters2_actions AS a,
3254 ttrss_filter_actions AS t
3256 action_id = t.id AND filter_id = '$filter_id'");
3258 while ($action_line = db_fetch_assoc($result2)) {
3259 # print_r($action_line);
3262 $action["type"] = $action_line["type_name"];
3263 $action["param"] = $action_line["action_param"];
3265 array_push($actions, $action);
3270 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3271 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3272 $filter["rules"] = $rules;
3273 $filter["actions"] = $actions;
3275 if (count($rules) > 0 && count($actions) > 0) {
3276 array_push($filters, $filter);
3283 function get_score_pic($score) {
3285 return "score_high.png";
3286 } else if ($score > 0) {
3287 return "score_half_high.png";
3288 } else if ($score < -100) {
3289 return "score_low.png";
3290 } else if ($score < 0) {
3291 return "score_half_low.png";
3293 return "score_neutral.png";
3297 function feed_has_icon($id) {
3298 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3301 function init_connection($link) {
3304 if (DB_TYPE
== "pgsql") {
3305 pg_query($link, "set client_encoding = 'UTF-8'");
3306 pg_set_client_encoding("UNICODE");
3307 pg_query($link, "set datestyle = 'ISO, european'");
3308 pg_query($link, "set TIME ZONE 0");
3310 db_query($link, "SET time_zone = '+0:0'");
3312 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3313 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3319 $pluginhost = new PluginHost($link);
3320 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3324 print "Unable to connect to database:" . db_last_error();
3329 function format_tags_string($tags, $id) {
3332 $tags_nolinks_str = "";
3338 $formatted_tags = array();
3340 foreach ($tags as $tag) {
3342 $tag_escaped = str_replace("'", "\\'", $tag);
3344 if (mb_strlen($tag) > 30) {
3345 $tag = truncate_string($tag, 30);
3348 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3350 array_push($formatted_tags, $tag_str);
3352 $tmp_tags_str = implode(", ", $formatted_tags);
3354 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3359 $tags_str = implode(", ", $formatted_tags);
3361 if ($num_tags < count($tags)) {
3362 $tags_str .= ", …";
3365 if ($num_tags == 0) {
3366 $tags_str = __("no tags");
3373 function format_article_labels($labels, $id) {
3377 foreach ($labels as $l) {
3378 $labels_str .= sprintf("<span class='hlLabelRef'
3379 style='color : %s; background-color : %s'>%s</span>",
3380 $l[2], $l[3], $l[1]);
3387 function format_article_note($id, $note, $allow_edit = true) {
3389 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3390 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3391 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3397 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3398 if ($parent_cat_id) {
3399 $parent_qpart = "parent_cat = '$parent_cat_id'";
3400 $parent_insert = "'$parent_cat_id'";
3402 $parent_qpart = "parent_cat IS NULL";
3403 $parent_insert = "NULL";
3406 $result = db_query($link,
3407 "SELECT id FROM ttrss_feed_categories
3408 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3410 if (db_num_rows($result) == 0) {
3413 return db_fetch_result($result, 0, "id");
3417 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3419 if (!$feed_cat) return false;
3421 db_query($link, "BEGIN");
3423 if ($parent_cat_id) {
3424 $parent_qpart = "parent_cat = '$parent_cat_id'";
3425 $parent_insert = "'$parent_cat_id'";
3427 $parent_qpart = "parent_cat IS NULL";
3428 $parent_insert = "NULL";
3431 $result = db_query($link,
3432 "SELECT id FROM ttrss_feed_categories
3433 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3435 if (db_num_rows($result) == 0) {
3437 $result = db_query($link,
3438 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3439 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3441 db_query($link, "COMMIT");
3449 function getArticleFeed($link, $id) {
3450 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3451 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3453 if (db_num_rows($result) != 0) {
3454 return db_fetch_result($result, 0, "feed_id");
3461 * Fixes incomplete URLs by prepending "http://".
3462 * Also replaces feed:// with http://, and
3463 * prepends a trailing slash if the url is a domain name only.
3465 * @param string $url Possibly incomplete URL
3467 * @return string Fixed URL.
3469 function fix_url($url) {
3470 if (strpos($url, '://') === false) {
3471 $url = 'http://' . $url;
3472 } else if (substr($url, 0, 5) == 'feed:') {
3473 $url = 'http:' . substr($url, 5);
3476 //prepend slash if the URL has no slash in it
3477 // "http://www.example" -> "http://www.example/"
3478 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3482 if ($url != "http:///")
3488 function validate_feed_url($url) {
3489 $parts = parse_url($url);
3491 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3495 function get_article_enclosures($link, $id) {
3497 $query = "SELECT * FROM ttrss_enclosures
3498 WHERE post_id = '$id' AND content_url != ''";
3502 $result = db_query($link, $query);
3504 if (db_num_rows($result) > 0) {
3505 while ($line = db_fetch_assoc($result)) {
3506 array_push($rv, $line);
3513 function save_email_address($link, $email) {
3514 // FIXME: implement persistent storage of emails
3516 if (!$_SESSION['stored_emails'])
3517 $_SESSION['stored_emails'] = array();
3519 if (!in_array($email, $_SESSION['stored_emails']))
3520 array_push($_SESSION['stored_emails'], $email);
3524 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3526 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3528 $sql_is_cat = bool_to_sql_bool($is_cat);
3530 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3531 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3532 AND owner_uid = " . $owner_uid);
3534 if (db_num_rows($result) == 1) {
3535 return db_fetch_result($result, 0, "access_key");
3537 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3539 $result = db_query($link, "INSERT INTO ttrss_access_keys
3540 (access_key, feed_id, is_cat, owner_uid)
3541 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3548 function get_feeds_from_html($url, $content)
3550 $url = fix_url($url);
3551 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3553 libxml_use_internal_errors(true);
3555 $doc = new DOMDocument();
3556 $doc->loadHTML($content);
3557 $xpath = new DOMXPath($doc);
3558 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3559 $feedUrls = array();
3560 foreach ($entries as $entry) {
3561 if ($entry->hasAttribute('href')) {
3562 $title = $entry->getAttribute('title');
3564 $title = $entry->getAttribute('type');
3566 $feedUrl = rewrite_relative_url(
3567 $baseUrl, $entry->getAttribute('href')
3569 $feedUrls[$feedUrl] = $title;
3575 function is_html($content) {
3576 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3579 function url_is_html($url, $login = false, $pass = false) {
3580 return is_html(fetch_file_contents($url, false, $login, $pass));
3583 function print_label_select($link, $name, $value, $attributes = "") {
3585 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3586 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3588 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3589 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3591 while ($line = db_fetch_assoc($result)) {
3593 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3595 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3596 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3600 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3607 function format_article_enclosures($link, $id, $always_display_enclosures,
3608 $article_content, $hide_images = false) {
3610 $result = get_article_enclosures($link, $id);
3613 if (count($result) > 0) {
3615 $entries_html = array();
3617 $entries_inline = array();
3619 foreach ($result as $line) {
3621 $url = $line["content_url"];
3622 $ctype = $line["content_type"];
3624 if (!$ctype) $ctype = __("unknown type");
3626 $filename = substr($url, strrpos($url, "/")+
1);
3628 $player = format_inline_player($link, $url, $ctype);
3630 if ($player) array_push($entries_inline, $player);
3632 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3633 # $filename . " (" . $ctype . ")" . "</a>";
3635 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3636 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3638 array_push($entries_html, $entry);
3642 $entry["type"] = $ctype;
3643 $entry["filename"] = $filename;
3644 $entry["url"] = $url;
3646 array_push($entries, $entry);
3649 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3650 if ($always_display_enclosures ||
3651 !preg_match("/<img/i", $article_content)) {
3653 foreach ($entries as $entry) {
3655 if (preg_match("/image/", $entry["type"]) ||
3656 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3658 if (!$hide_images) {
3660 alt=\"".htmlspecialchars($entry["filename"])."\"
3661 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3663 $rv .= "<p><a target=\"_blank\"
3664 href=\"".htmlspecialchars($entry["url"])."\"
3665 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3673 if (count($entries_inline) > 0) {
3674 $rv .= "<hr clear='both'/>";
3675 foreach ($entries_inline as $entry) { $rv .= $entry; };
3676 $rv .= "<hr clear='both'/>";
3679 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3680 "<option value=''>" . __('Attachments')."</option>";
3682 foreach ($entries as $entry) {
3683 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3693 function getLastArticleId($link) {
3694 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3695 WHERE owner_uid = " . $_SESSION["uid"]);
3697 if (db_num_rows($result) == 1) {
3698 return db_fetch_result($result, 0, "id");
3704 function build_url($parts) {
3705 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3709 * Converts a (possibly) relative URL to a absolute one.
3711 * @param string $url Base URL (i.e. from where the document is)
3712 * @param string $rel_url Possibly relative URL in the document
3714 * @return string Absolute URL
3716 function rewrite_relative_url($url, $rel_url) {
3717 if (strpos($rel_url, "magnet:") === 0) {
3719 } else if (strpos($rel_url, "://") !== false) {
3721 } else if (strpos($rel_url, "//") === 0) {
3722 # protocol-relative URL (rare but they exist)
3724 } else if (strpos($rel_url, "/") === 0)
3726 $parts = parse_url($url);
3727 $parts['path'] = $rel_url;
3729 return build_url($parts);
3732 $parts = parse_url($url);
3733 if (!isset($parts['path'])) {
3734 $parts['path'] = '/';
3736 $dir = $parts['path'];
3737 if (substr($dir, -1) !== '/') {
3738 $dir = dirname($parts['path']);
3739 $dir !== '/' && $dir .= '/';
3741 $parts['path'] = $dir . $rel_url;
3743 return build_url($parts);
3747 function sphinx_search($query, $offset = 0, $limit = 30) {
3748 require_once 'lib/sphinxapi.php';
3750 $sphinxClient = new SphinxClient();
3752 $sphinxClient->SetServer('localhost', 9312);
3753 $sphinxClient->SetConnectTimeout(1);
3755 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3756 'feed_title' => 20));
3758 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3759 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3760 $sphinxClient->SetLimits($offset, $limit, 1000);
3761 $sphinxClient->SetArrayResult(false);
3762 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3764 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3768 if (is_array($result['matches'])) {
3769 foreach (array_keys($result['matches']) as $int_id) {
3770 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3771 array_push($ids, $ref_id);
3778 function cleanup_tags($link, $days = 14, $limit = 1000) {
3780 if (DB_TYPE
== "pgsql") {
3781 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3782 } else if (DB_TYPE
== "mysql") {
3783 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3788 while ($limit > 0) {
3791 $query = "SELECT ttrss_tags.id AS id
3792 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3793 WHERE post_int_id = int_id AND $interval_query AND
3794 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3796 $result = db_query($link, $query);
3800 while ($line = db_fetch_assoc($result)) {
3801 array_push($ids, $line['id']);
3804 if (count($ids) > 0) {
3805 $ids = join(",", $ids);
3808 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3809 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3814 $limit -= $limit_part;
3819 return $tags_deleted;
3822 function print_user_stylesheet($link) {
3823 $value = get_pref($link, 'USER_STYLESHEET');
3826 print "<style type=\"text/css\">";
3827 print str_replace("<br/>", "\n", $value);
3833 function rewrite_urls($html) {
3834 libxml_use_internal_errors(true);
3836 $charset_hack = '<head>
3837 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3840 $doc = new DOMDocument();
3841 $doc->loadHTML($charset_hack . $html);
3842 $xpath = new DOMXPath($doc);
3844 $entries = $xpath->query('//*/text()');
3846 foreach ($entries as $entry) {
3847 if (strstr($entry->wholeText
, "://") !== false) {
3848 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3849 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3851 if ($text != $entry->wholeText
) {
3852 $cdoc = new DOMDocument();
3853 $cdoc->loadHTML($charset_hack . $text);
3856 foreach ($cdoc->childNodes
as $cnode) {
3857 $cnode = $doc->importNode($cnode, true);
3860 $entry->parentNode
->insertBefore($cnode);
3864 $entry->parentNode
->removeChild($entry);
3870 $node = $doc->getElementsByTagName('body')->item(0);
3872 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3874 return $doc->saveXML($node);
3879 function filter_to_sql($link, $filter, $owner_uid) {
3882 if (DB_TYPE
== "pgsql")
3885 $reg_qpart = "REGEXP";
3887 foreach ($filter["rules"] AS $rule) {
3888 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3889 $rule['reg_exp']) !== FALSE;
3891 if ($regexp_valid) {
3893 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3895 switch ($rule["type"]) {
3897 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3898 $rule['reg_exp'] . "')";
3901 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3902 $rule['reg_exp'] . "')";
3905 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3906 $rule['reg_exp'] . "') OR LOWER(" .
3907 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3910 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3911 $rule['reg_exp'] . "')";
3914 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3915 $rule['reg_exp'] . "')";
3918 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3919 $rule['reg_exp'] . "')";
3923 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3925 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3926 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3929 if (isset($rule["cat_id"])) {
3931 if ($rule["cat_id"] > 0) {
3932 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3933 array_push($children, $rule["cat_id"]);
3935 $children = join(",", $children);
3937 $cat_qpart = "cat_id IN ($children)";
3939 $cat_qpart = "cat_id IS NULL";
3942 $qpart .= " AND $cat_qpart";
3945 array_push($query, "($qpart)");
3950 if (count($query) > 0) {
3951 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3953 $fullquery = "(false)";
3956 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3961 if (!function_exists('gzdecode')) {
3962 function gzdecode($string) { // no support for 2nd argument
3963 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3964 base64_encode($string));
3968 function get_random_bytes($length) {
3969 if (function_exists('openssl_random_pseudo_bytes')) {
3970 return openssl_random_pseudo_bytes($length);
3974 for ($i = 0; $i < $length; $i++
)
3975 $output .= chr(mt_rand(0, 255));
3981 function read_stdin() {
3982 $fp = fopen("php://stdin", "r");
3985 $line = trim(fgets($fp));
3993 function tmpdirname($path, $prefix) {
3994 // Use PHP's tmpfile function to create a temporary
3995 // directory name. Delete the file and keep the name.
3996 $tempname = tempnam($path,$prefix);
4000 if (!unlink($tempname))
4006 function getFeedCategory($link, $feed) {
4007 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
4008 WHERE id = '$feed'");
4010 if (db_num_rows($result) > 0) {
4011 return db_fetch_result($result, 0, "cat_id");
4018 function implements_interface($class, $interface) {
4019 return in_array($interface, class_implements($class));
4022 function geturl($url){
4024 (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');
4026 $curl = curl_init();
4027 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4028 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4029 $header[] = "Cache-Control: max-age=0";
4030 $header[] = "Connection: keep-alive";
4031 $header[] = "Keep-Alive: 300";
4032 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4033 $header[] = "Accept-Language: en-us,en;q=0.5";
4034 $header[] = "Pragma: ";
4036 curl_setopt($curl, CURLOPT_URL
, $url);
4037 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4038 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4039 curl_setopt($curl, CURLOPT_HEADER
, true);
4040 curl_setopt($curl, CURLOPT_REFERER
, $url);
4041 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4042 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4043 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4044 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4045 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4047 $html = curl_exec($curl);
4049 $status = curl_getinfo($curl);
4052 if($status['http_code']!=200){
4053 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4054 list($header) = explode("\r\n\r\n", $html, 2);
4056 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4057 $url = trim(str_replace($matches[1],"",$matches[0]));
4058 $url_parsed = parse_url($url);
4059 return (isset($url_parsed))?
geturl($url, $referer):'';
4062 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4063 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4064 # $handle = @fopen('./curl.error.log', 'a');
4065 # fwrite($handle, $line);
4071 function get_minified_js($files) {
4072 require_once 'lib/jshrink/Minifier.php';
4076 foreach ($files as $js) {
4077 if (!isset($_GET['debug'])) {
4078 $cached_file = CACHE_DIR
. "/js/$js.js";
4080 if (file_exists($cached_file) &&
4081 is_readable($cached_file) &&
4082 filemtime($cached_file) >= filemtime("js/$js.js")) {
4084 $rv .= file_get_contents($cached_file);
4087 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4088 file_put_contents($cached_file, $minified);
4092 $rv .= file_get_contents("js/$js.js");
4099 function stylesheet_tag($filename) {
4100 $timestamp = filemtime($filename);
4102 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4105 function javascript_tag($filename) {
4108 if (!(strpos($filename, "?") === FALSE)) {
4109 $query = substr($filename, strpos($filename, "?")+
1);
4110 $filename = substr($filename, 0, strpos($filename, "?"));
4113 $timestamp = filemtime($filename);
4115 if ($query) $timestamp .= "&$query";
4117 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4120 function calculate_dep_timestamp() {
4121 $files = array_merge(glob("js/*.js"), glob("*.css"));
4125 foreach ($files as $file) {
4126 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4132 function T_js_decl($s1, $s2) {
4134 $s1 = preg_replace("/\n/", "", $s1);
4135 $s2 = preg_replace("/\n/", "", $s2);
4137 $s1 = preg_replace("/\"/", "\\\"", $s1);
4138 $s2 = preg_replace("/\"/", "\\\"", $s2);
4140 return "T_messages[\"$s1\"] = \"$s2\";\n";
4144 function init_js_translations() {
4146 print 'var T_messages = new Object();
4149 if (T_messages[msg]) {
4150 return T_messages[msg];
4156 function ngettext(msg1, msg2, n) {
4157 return (parseInt(n) > 1) ? msg2 : msg1;
4160 $l10n = _get_reader();
4162 for ($i = 0; $i < $l10n->total
; $i++
) {
4163 $orig = $l10n->get_original_string($i);
4164 $translation = __($orig);
4166 print T_js_decl($orig, $translation);
4170 function label_to_feed_id($label) {
4171 return LABEL_BASE_INDEX
- 1 - abs($label);
4174 function feed_to_label_id($feed) {
4175 return LABEL_BASE_INDEX
- 1 +
abs($feed);