2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 115);
5 define('LABEL_BASE_INDEX', -1024);
6 define('PLUGIN_FEED_BASE_INDEX', -128);
8 $fetch_last_error = false;
9 $fetch_last_error_code = false;
12 function __autoload($class) {
13 $class_file = str_replace("_", "/", strtolower(basename($class)));
15 $file = dirname(__FILE__
)."/../classes/$class_file.php";
17 if (file_exists($file)) {
23 mb_internal_encoding("UTF-8");
24 date_default_timezone_set('UTC');
25 if (defined('E_DEPRECATED')) {
26 error_reporting(E_ALL
& ~E_NOTICE
& ~E_DEPRECATED
);
28 error_reporting(E_ALL
& ~E_NOTICE
);
31 require_once 'config.php';
33 if (DB_TYPE
== "pgsql") {
34 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
36 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
39 define('THEME_VERSION_REQUIRED', 1.1);
42 * Return available translations names.
45 * @return array A array of available translations.
47 function get_translations() {
49 "auto" => "Detect automatically",
55 "fr_FR" => "Français",
56 "hu_HU" => "Magyar (Hungarian)",
57 "it_IT" => "Italiano",
58 "ja_JP" => "日本語 (Japanese)",
59 "lv_LV" => "Latviešu",
60 "nb_NO" => "Norwegian bokmål",
64 "pt_BR" => "Portuguese/Brazil",
65 "zh_CN" => "Simplified Chinese",
72 require_once "lib/accept-to-gettext.php";
73 require_once "lib/gettext/gettext.inc";
76 function startup_gettext() {
78 # Get locale from Accept-Language header
79 $lang = al2gt(array_keys(get_translations()), "text/html");
81 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
82 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
85 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
86 $lang = $_SESSION["language"];
90 if (defined('LC_MESSAGES')) {
91 _setlocale(LC_MESSAGES
, $lang);
92 } else if (defined('LC_ALL')) {
93 _setlocale(LC_ALL
, $lang);
96 _bindtextdomain("messages", "locale");
98 _textdomain("messages");
99 _bind_textdomain_codeset("messages", "UTF-8");
105 require_once 'db-prefs.php';
106 require_once 'version.php';
107 require_once 'ccache.php';
108 require_once 'labels.php';
110 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
111 ini_set('user_agent', SELF_USER_AGENT
);
113 require_once 'lib/pubsubhubbub/publisher.php';
116 $utc_tz = new DateTimeZone('UTC');
117 $schema_version = false;
120 * Print a timestamped debug message.
122 * @param string $msg The debug message.
125 function _debug($msg) {
126 $ts = strftime("%H:%M:%S", time());
127 if (function_exists('posix_getpid')) {
128 $ts = "$ts/" . posix_getpid();
131 if (!(defined('QUIET') && QUIET
)) {
132 print "[$ts] $msg\n";
135 if (defined('LOGFILE')) {
136 $fp = fopen(LOGFILE
, 'a+');
139 fputs($fp, "[$ts] $msg\n");
147 * Purge a feed old posts.
149 * @param mixed $link A database connection.
150 * @param mixed $feed_id The id of the purged feed.
151 * @param mixed $purge_interval Olderness of purged posts.
152 * @param boolean $debug Set to True to enable the debug. False by default.
156 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
158 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
162 $result = db_query($link,
163 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
167 if (db_num_rows($result) == 1) {
168 $owner_uid = db_fetch_result($result, 0, "owner_uid");
171 if ($purge_interval == -1 ||
!$purge_interval) {
173 ccache_update($link, $feed_id, $owner_uid);
178 if (!$owner_uid) return;
180 if (FORCE_ARTICLE_PURGE
== 0) {
181 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
184 $purge_unread = true;
185 $purge_interval = FORCE_ARTICLE_PURGE
;
188 if (!$purge_unread) $query_limit = " unread = false AND ";
190 if (DB_TYPE
== "pgsql") {
191 $pg_version = get_pgsql_version($link);
193 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
195 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
196 ttrss_entries.id = ref_id AND
198 feed_id = '$feed_id' AND
200 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
204 $result = db_query($link, "DELETE FROM ttrss_user_entries
206 WHERE ttrss_entries.id = ref_id AND
208 feed_id = '$feed_id' AND
210 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
213 $rows = pg_affected_rows($result);
217 /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
218 marked = false AND feed_id = '$feed_id' AND
219 (SELECT date_updated FROM ttrss_entries WHERE
220 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
222 $result = db_query($link, "DELETE FROM ttrss_user_entries
223 USING ttrss_user_entries, ttrss_entries
224 WHERE ttrss_entries.id = ref_id AND
226 feed_id = '$feed_id' AND
228 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
230 $rows = mysql_affected_rows($link);
234 ccache_update($link, $feed_id, $owner_uid);
237 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
241 } // function purge_feed
243 function feed_purge_interval($link, $feed_id) {
245 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
246 WHERE id = '$feed_id'");
248 if (db_num_rows($result) == 1) {
249 $purge_interval = db_fetch_result($result, 0, "purge_interval");
250 $owner_uid = db_fetch_result($result, 0, "owner_uid");
252 if ($purge_interval == 0) $purge_interval = get_pref($link,
253 'PURGE_OLD_DAYS', $owner_uid);
255 return $purge_interval;
262 function purge_orphans($link, $do_output = false) {
264 // purge orphaned posts in main content table
265 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
266 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
269 $rows = db_affected_rows($link, $result);
270 _debug("Purged $rows orphaned posts.");
274 function get_feed_update_interval($link, $feed_id) {
275 $result = db_query($link, "SELECT owner_uid, update_interval FROM
276 ttrss_feeds WHERE id = '$feed_id'");
278 if (db_num_rows($result) == 1) {
279 $update_interval = db_fetch_result($result, 0, "update_interval");
280 $owner_uid = db_fetch_result($result, 0, "owner_uid");
282 if ($update_interval != 0) {
283 return $update_interval;
285 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
293 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
295 global $fetch_last_error;
296 global $fetch_last_error_code;
298 if (function_exists('curl_init') && !ini_get("open_basedir")) {
300 if (ini_get("safe_mode")) {
301 $ch = curl_init(geturl($url));
303 $ch = curl_init($url);
307 curl_setopt($ch, CURLOPT_HTTPHEADER
,
308 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
311 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : 15);
312 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : 45);
313 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode"));
314 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
315 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
316 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
317 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
318 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
319 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
320 curl_setopt($ch, CURLOPT_ENCODING
, "gzip");
321 curl_setopt($ch, CURLOPT_REFERER
, $url);
324 curl_setopt($ch, CURLOPT_POST
, true);
325 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
329 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
331 $contents = @curl_exec
($ch);
333 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
334 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
335 $contents = @curl_exec
($ch);
338 if ($contents === false) {
339 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
344 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
345 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
347 $fetch_last_error_code = $http_code;
349 if ($http_code != 200 ||
$type && strpos($content_type, "$type") === false) {
350 if (curl_errno($ch) != 0) {
351 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
353 $fetch_last_error = "HTTP Code: $http_code";
363 if ($login && $pass){
364 $url_parts = array();
366 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
368 $pass = urlencode($pass);
370 if ($url_parts[1] && $url_parts[2]) {
371 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
375 $data = @file_get_contents
($url);
377 @$gzdecoded = gzdecode($data);
378 if ($gzdecoded) $data = $gzdecoded;
380 if (!$data && function_exists('error_get_last')) {
381 $error = error_get_last();
382 $fetch_last_error = $error["message"];
390 * Try to determine the favicon URL for a feed.
391 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
392 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
394 * @param string $url A feed or page URL
396 * @return mixed The favicon URL, or false if none was found.
398 function get_favicon_url($url) {
400 $favicon_url = false;
402 if ($html = @fetch_file_contents
($url)) {
404 libxml_use_internal_errors(true);
406 $doc = new DOMDocument();
407 $doc->loadHTML($html);
408 $xpath = new DOMXPath($doc);
410 $base = $xpath->query('/html/head/base');
411 foreach ($base as $b) {
412 $url = $b->getAttribute("href");
416 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
417 if (count($entries) > 0) {
418 foreach ($entries as $entry) {
419 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
426 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
429 } // function get_favicon_url
431 function check_feed_favicon($site_url, $feed, $link) {
432 # print "FAVICON [$site_url]: $favicon_url\n";
434 $icon_file = ICONS_DIR
. "/$feed.ico";
436 if (!file_exists($icon_file)) {
437 $favicon_url = get_favicon_url($site_url);
440 // Limiting to "image" type misses those served with text/plain
441 $contents = fetch_file_contents($favicon_url); // , "image");
444 // Crude image type matching.
445 // Patterns gleaned from the file(1) source code.
446 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
447 // 0 string \000\000\001\000 MS Windows icon resource
448 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
450 elseif (preg_match('/^GIF8/', $contents)) {
451 // 0 string GIF8 GIF image data
452 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
454 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
455 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
456 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
458 elseif (preg_match('/^\xff\xd8/', $contents)) {
459 // 0 beshort 0xffd8 JPEG image data
460 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
463 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
469 $fp = @fopen
($icon_file, "w");
472 fwrite($fp, $contents);
474 chmod($icon_file, 0644);
481 function print_select($id, $default, $values, $attributes = "") {
482 print "<select name=\"$id\" id=\"$id\" $attributes>";
483 foreach ($values as $v) {
485 $sel = "selected=\"1\"";
491 print "<option value=\"$v\" $sel>$v</option>";
496 function print_select_hash($id, $default, $values, $attributes = "") {
497 print "<select name=\"$id\" id='$id' $attributes>";
498 foreach (array_keys($values) as $v) {
500 $sel = 'selected="selected"';
506 print "<option $sel value=\"$v\">".$values[$v]."</option>";
512 function print_radio($id, $default, $true_is, $values, $attributes = "") {
513 foreach ($values as $v) {
520 if ($v == $true_is) {
521 $sel .= " value=\"1\"";
523 $sel .= " value=\"0\"";
526 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
527 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
532 function initialize_user_prefs($link, $uid, $profile = false) {
534 $uid = db_escape_string($link, $uid);
538 $profile_qpart = "AND profile IS NULL";
540 $profile_qpart = "AND profile = '$profile'";
543 if (get_schema_version($link) < 63) $profile_qpart = "";
545 db_query($link, "BEGIN");
547 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
549 $u_result = db_query($link, "SELECT pref_name
550 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
552 $active_prefs = array();
554 while ($line = db_fetch_assoc($u_result)) {
555 array_push($active_prefs, $line["pref_name"]);
558 while ($line = db_fetch_assoc($result)) {
559 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
560 // print "adding " . $line["pref_name"] . "<br>";
562 $line["def_value"] = db_escape_string($link, $line["def_value"]);
563 $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
565 if (get_schema_version($link) < 63) {
566 db_query($link, "INSERT INTO ttrss_user_prefs
567 (owner_uid,pref_name,value) VALUES
568 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
571 db_query($link, "INSERT INTO ttrss_user_prefs
572 (owner_uid,pref_name,value, profile) VALUES
573 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
579 db_query($link, "COMMIT");
583 function get_ssl_certificate_id() {
584 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
585 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
586 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
587 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
588 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
593 function authenticate_user($link, $login, $password, $check_only = false) {
595 if (!SINGLE_USER_MODE
) {
599 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
601 $user_id = (int) $plugin->authenticate($login, $password);
604 $_SESSION["auth_module"] = strtolower(get_class($plugin));
609 if ($user_id && !$check_only) {
612 $_SESSION["uid"] = $user_id;
614 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
615 WHERE id = '$user_id'");
617 $_SESSION["name"] = db_fetch_result($result, 0, "login");
618 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
619 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
621 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
624 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
625 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
626 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
628 $_SESSION["last_version_check"] = time();
630 initialize_user_prefs($link, $_SESSION["uid"]);
639 $_SESSION["uid"] = 1;
640 $_SESSION["name"] = "admin";
641 $_SESSION["access_level"] = 10;
643 $_SESSION["hide_hello"] = true;
644 $_SESSION["hide_logout"] = true;
646 $_SESSION["auth_module"] = false;
648 if (!$_SESSION["csrf_token"]) {
649 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
652 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
654 initialize_user_prefs($link, $_SESSION["uid"]);
660 function make_password($length = 8) {
663 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
667 while ($i < $length) {
668 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
670 if (!strstr($password, $char)) {
678 // this is called after user is created to initialize default feeds, labels
681 // user preferences are checked on every login, not here
683 function initialize_user($link, $uid) {
685 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
686 values ('$uid', 'Tiny Tiny RSS: New Releases',
687 'http://tt-rss.org/releases.rss')");
689 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
690 values ('$uid', 'Tiny Tiny RSS: Forum',
691 'http://tt-rss.org/forum/rss.php')");
694 function logout_user() {
696 if (isset($_COOKIE[session_name()])) {
697 setcookie(session_name(), '', time()-42000, '/');
701 function validate_csrf($csrf_token) {
702 return $csrf_token == $_SESSION['csrf_token'];
705 function load_user_plugins($link, $owner_uid) {
707 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
710 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
712 if (get_schema_version($link) > 100) {
713 $pluginhost->load_data();
718 function login_sequence($link) {
719 $_SESSION["prefs_cache"] = false;
721 if (SINGLE_USER_MODE
) {
723 authenticate_user($link, "admin", null);
725 load_user_plugins($link, $_SESSION["uid"]);
727 if (!$_SESSION["uid"] ||
!validate_session($link)) {
729 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
730 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
732 authenticate_user($link, null, null, true);
735 if (!$_SESSION["uid"]) render_login_form($link);
738 /* bump login timestamp */
739 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
741 $_SESSION["last_login_update"] = time();
744 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
745 setcookie("ttrss_lang", $_SESSION["language"],
746 time() + SESSION_COOKIE_LIFETIME
);
749 if ($_SESSION["uid"]) {
751 load_user_plugins($link, $_SESSION["uid"]);
755 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
756 $_SESSION["uid"] . " AND
757 (SELECT COUNT(id) FROM ttrss_feeds WHERE
758 ttrss_feeds.id = feed_id) = 0");
760 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
761 $_SESSION["uid"] . " AND
762 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
763 ttrss_feed_categories.id = feed_id) = 0");
770 function truncate_string($str, $max_len, $suffix = '…') {
771 if (mb_strlen($str, "utf-8") > $max_len - 3) {
772 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
778 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
781 $source_tz = new DateTimeZone($source_tz);
782 } catch (Exception
$e) {
783 $source_tz = new DateTimeZone('UTC');
787 $dest_tz = new DateTimeZone($dest_tz);
788 } catch (Exception
$e) {
789 $dest_tz = new DateTimeZone('UTC');
792 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
793 return $dt->format('U') +
$dest_tz->getOffset($dt);
796 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
797 $no_smart_dt = false) {
799 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
800 if (!$timestamp) $timestamp = '1970-01-01 0:00';
805 # We store date in UTC internally
806 $dt = new DateTime($timestamp, $utc_tz);
808 if ($tz_offset == -1) {
810 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
813 $user_tz = new DateTimeZone($user_tz_string);
814 } catch (Exception
$e) {
818 $tz_offset = $user_tz->getOffset($dt);
821 $user_timestamp = $dt->format('U') +
$tz_offset;
824 return smart_date_time($link, $user_timestamp,
825 $tz_offset, $owner_uid);
828 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
830 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
832 return date($format, $user_timestamp);
836 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
837 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
839 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
840 return date("G:i", $timestamp);
841 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
842 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
843 return date($format, $timestamp);
845 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
846 return date($format, $timestamp);
850 function sql_bool_to_bool($s) {
851 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
858 function bool_to_sql_bool($s) {
866 // Session caching removed due to causing wrong redirects to upgrade
867 // script when get_schema_version() is called on an obsolete session
868 // created on a previous schema version.
869 function get_schema_version($link, $nocache = false) {
870 global $schema_version;
872 if (!$schema_version) {
873 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
874 $version = db_fetch_result($result, 0, "schema_version");
875 $schema_version = $version;
878 return $schema_version;
882 function sanity_check($link) {
883 require_once 'errors.php';
886 $schema_version = get_schema_version($link, true);
888 if ($schema_version != SCHEMA_VERSION
) {
892 if (DB_TYPE
== "mysql") {
893 $result = db_query($link, "SELECT true", false);
894 if (db_num_rows($result) != 1) {
899 if (db_escape_string($link, "testTEST") != "testTEST") {
903 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
906 function file_is_locked($filename) {
907 if (function_exists('flock')) {
908 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
910 if (flock($fp, LOCK_EX | LOCK_NB
)) {
921 return true; // consider the file always locked and skip the test
924 function make_lockfile($filename) {
925 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
927 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
928 if (function_exists('posix_getpid')) {
929 fwrite($fp, posix_getpid() . "\n");
937 function make_stampfile($filename) {
938 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
940 if (flock($fp, LOCK_EX | LOCK_NB
)) {
941 fwrite($fp, time() . "\n");
950 function sql_random_function() {
951 if (DB_TYPE
== "mysql") {
958 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
960 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
962 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
964 // Todo: all this interval stuff needs some generic generator function
966 $date_qpart = "false";
970 if (DB_TYPE
== "pgsql") {
971 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
973 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
977 if (DB_TYPE
== "pgsql") {
978 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
980 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
984 if (DB_TYPE
== "pgsql") {
985 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
987 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
991 $date_qpart = "true";
994 if (is_numeric($feed)) {
1000 $children = getChildCategories($link, $feed, $owner_uid);
1001 array_push($children, $feed);
1003 $children = join(",", $children);
1005 $cat_qpart = "cat_id IN ($children)";
1007 $cat_qpart = "cat_id IS NULL";
1010 db_query($link, "UPDATE ttrss_user_entries
1011 SET unread = false, last_read = NOW() WHERE ref_id IN
1013 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1014 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1015 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1017 } else if ($feed == -2) {
1019 db_query($link, "UPDATE ttrss_user_entries
1020 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1021 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1022 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1025 } else if ($feed > 0) {
1027 db_query($link, "UPDATE ttrss_user_entries
1028 SET unread = false, last_read = NOW() WHERE ref_id IN
1030 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1031 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1033 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1036 db_query($link, "UPDATE ttrss_user_entries
1037 SET unread = false, last_read = NOW() WHERE ref_id IN
1039 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1040 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1044 db_query($link, "UPDATE ttrss_user_entries
1045 SET unread = false, last_read = NOW() WHERE ref_id IN
1047 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1048 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1053 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1055 if (DB_TYPE
== "pgsql") {
1056 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1058 $match_part = "updated > DATE_SUB(NOW(),
1059 INTERVAL $intl HOUR) ";
1062 db_query($link, "UPDATE ttrss_user_entries
1063 SET unread = false, last_read = NOW() WHERE ref_id IN
1065 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1066 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart AND $match_part) as tmp)");
1070 db_query($link, "UPDATE ttrss_user_entries
1071 SET unread = false, last_read = NOW() WHERE ref_id IN
1073 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1074 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1077 } else if ($feed < LABEL_BASE_INDEX
) { // label
1079 $label_id = feed_to_label_id($feed);
1081 db_query($link, "UPDATE ttrss_user_entries
1082 SET unread = false, last_read = NOW() WHERE ref_id IN
1084 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1085 AND label_id = '$label_id' AND ref_id = article_id
1086 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1090 ccache_update($link, $feed, $owner_uid, $cat_view);
1093 db_query($link, "UPDATE ttrss_user_entries
1094 SET unread = false, last_read = NOW() WHERE ref_id IN
1096 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1097 AND post_int_id = int_id AND tag_name = '$feed'
1098 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1103 function getAllCounters($link) {
1104 $data = getGlobalCounters($link);
1106 $data = array_merge($data, getVirtCounters($link));
1107 $data = array_merge($data, getLabelCounters($link));
1108 $data = array_merge($data, getFeedCounters($link, $active_feed));
1109 $data = array_merge($data, getCategoryCounters($link));
1114 function getCategoryTitle($link, $cat_id) {
1116 if ($cat_id == -1) {
1117 return __("Special");
1118 } else if ($cat_id == -2) {
1119 return __("Labels");
1122 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1125 if (db_num_rows($result) == 1) {
1126 return db_fetch_result($result, 0, "title");
1128 return __("Uncategorized");
1134 function getCategoryCounters($link) {
1137 /* Labels category */
1139 $cv = array("id" => -2, "kind" => "cat",
1140 "counter" => getCategoryUnread($link, -2));
1142 array_push($ret_arr, $cv);
1144 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1145 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1146 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1147 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1148 WHERE ttrss_cat_counters_cache.feed_id = id AND
1149 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1150 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1152 while ($line = db_fetch_assoc($result)) {
1153 $line["cat_id"] = (int) $line["cat_id"];
1155 if ($line["num_children"] > 0) {
1156 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1161 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1162 "counter" => $line["unread"] +
$child_counter);
1164 array_push($ret_arr, $cv);
1167 /* Special case: NULL category doesn't actually exist in the DB */
1169 $cv = array("id" => 0, "kind" => "cat",
1170 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1172 array_push($ret_arr, $cv);
1177 // only accepts real cats (>= 0)
1178 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1179 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1181 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1182 AND owner_uid = $owner_uid");
1186 while ($line = db_fetch_assoc($result)) {
1187 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1188 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1194 function getCategoryUnread($link, $cat, $owner_uid = false) {
1196 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1201 $cat_query = "cat_id = '$cat'";
1203 $cat_query = "cat_id IS NULL";
1206 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1207 AND owner_uid = " . $owner_uid);
1209 $cat_feeds = array();
1210 while ($line = db_fetch_assoc($result)) {
1211 array_push($cat_feeds, "feed_id = " . $line["id"]);
1214 if (count($cat_feeds) == 0) return 0;
1216 $match_part = implode(" OR ", $cat_feeds);
1218 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1219 FROM ttrss_user_entries
1220 WHERE unread = true AND ($match_part)
1221 AND owner_uid = " . $owner_uid);
1225 # this needs to be rewritten
1226 while ($line = db_fetch_assoc($result)) {
1227 $unread +
= $line["unread"];
1231 } else if ($cat == -1) {
1232 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1233 } else if ($cat == -2) {
1235 $result = db_query($link, "
1236 SELECT COUNT(unread) AS unread FROM
1237 ttrss_user_entries, ttrss_user_labels2
1238 WHERE article_id = ref_id AND unread = true
1239 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1241 $unread = db_fetch_result($result, 0, "unread");
1248 function getFeedUnread($link, $feed, $is_cat = false) {
1249 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1252 function getLabelUnread($link, $label_id, $owner_uid = false) {
1253 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1255 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1256 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1258 if (db_num_rows($result) != 0) {
1259 return db_fetch_result($result, 0, "unread");
1265 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1266 $owner_uid = false) {
1268 $n_feed = (int) $feed;
1269 $need_entries = false;
1271 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1274 $unread_qpart = "unread = true";
1276 $unread_qpart = "true";
1280 return getCategoryUnread($link, $n_feed, $owner_uid);
1281 } else if ($n_feed == -6) {
1283 } else if ($feed != "0" && $n_feed == 0) {
1285 $feed = db_escape_string($link, $feed);
1287 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1288 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1289 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1290 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1291 return db_fetch_result($result, 0, "count");
1293 } else if ($n_feed == -1) {
1294 $match_part = "marked = true";
1295 } else if ($n_feed == -2) {
1296 $match_part = "published = true";
1297 } else if ($n_feed == -3) {
1298 $match_part = "unread = true AND score >= 0";
1300 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1302 if (DB_TYPE
== "pgsql") {
1303 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1305 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1308 $need_entries = true;
1310 } else if ($n_feed == -4) {
1311 $match_part = "true";
1312 } else if ($n_feed >= 0) {
1315 $match_part = "feed_id = '$n_feed'";
1317 $match_part = "feed_id IS NULL";
1320 } else if ($feed < LABEL_BASE_INDEX
) {
1322 $label_id = feed_to_label_id($feed);
1324 return getLabelUnread($link, $label_id, $owner_uid);
1330 if ($need_entries) {
1331 $from_qpart = "ttrss_user_entries,ttrss_entries";
1332 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1334 $from_qpart = "ttrss_user_entries";
1337 $query = "SELECT count(int_id) AS unread
1338 FROM $from_qpart WHERE
1339 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1341 //echo "[$feed/$query]\n";
1343 $result = db_query($link, $query);
1347 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1348 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1349 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1350 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1353 $unread = db_fetch_result($result, 0, "unread");
1358 function getGlobalUnread($link, $user_id = false) {
1361 $user_id = $_SESSION["uid"];
1364 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1365 WHERE owner_uid = '$user_id' AND feed_id > 0");
1367 $c_id = db_fetch_result($result, 0, "c_id");
1372 function getGlobalCounters($link, $global_unread = -1) {
1375 if ($global_unread == -1) {
1376 $global_unread = getGlobalUnread($link);
1379 $cv = array("id" => "global-unread",
1380 "counter" => (int) $global_unread);
1382 array_push($ret_arr, $cv);
1384 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1385 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1387 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1389 $cv = array("id" => "subscribed-feeds",
1390 "counter" => (int) $subscribed_feeds);
1392 array_push($ret_arr, $cv);
1397 function getVirtCounters($link) {
1401 for ($i = 0; $i >= -4; $i--) {
1403 $count = getFeedUnread($link, $i);
1405 $cv = array("id" => $i,
1406 "counter" => (int) $count);
1408 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1409 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1411 array_push($ret_arr, $cv);
1417 $feeds = $pluginhost->get_feeds(-1);
1419 if (is_array($feeds)) {
1420 foreach ($feeds as $feed) {
1421 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1422 "counter" => $feed['sender']->get_unread($feed['id']));
1424 array_push($ret_arr, $cv);
1432 function getLabelCounters($link, $descriptions = false) {
1436 $owner_uid = $_SESSION["uid"];
1438 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1439 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1440 (ttrss_labels2.id = label_id)
1441 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1442 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1443 ttrss_labels2.caption");
1445 while ($line = db_fetch_assoc($result)) {
1447 $id = label_to_feed_id($line["id"]);
1449 $label_name = $line["caption"];
1450 $count = $line["unread"];
1452 $cv = array("id" => $id,
1453 "counter" => (int) $count);
1456 $cv["description"] = $label_name;
1458 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1459 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1461 array_push($ret_arr, $cv);
1467 function getFeedCounters($link, $active_feed = false) {
1471 $query = "SELECT ttrss_feeds.id,
1473 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1474 last_error, value AS count
1475 FROM ttrss_feeds, ttrss_counters_cache
1476 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1477 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1478 AND ttrss_counters_cache.feed_id = id";
1480 $result = db_query($link, $query);
1481 $fctrs_modified = false;
1483 while ($line = db_fetch_assoc($result)) {
1486 $count = $line["count"];
1487 $last_error = htmlspecialchars($line["last_error"]);
1489 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1491 $has_img = feed_has_icon($id);
1493 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1496 $cv = array("id" => $id,
1497 "updated" => $last_updated,
1498 "counter" => (int) $count,
1499 "has_img" => (int) $has_img);
1502 $cv["error"] = $last_error;
1504 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1505 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1507 if ($active_feed && $id == $active_feed)
1508 $cv["title"] = truncate_string($line["title"], 30);
1510 array_push($ret_arr, $cv);
1517 function get_pgsql_version($link) {
1518 $result = db_query($link, "SELECT version() AS version");
1519 $version = explode(" ", db_fetch_result($result, 0, "version"));
1524 * @return array (code => Status code, message => error message if available)
1526 * 0 - OK, Feed already exists
1527 * 1 - OK, Feed added
1529 * 3 - URL content is HTML, no feeds available
1530 * 4 - URL content is HTML which contains multiple feeds.
1531 * Here you should call extractfeedurls in rpc-backend
1532 * to get all possible feeds.
1533 * 5 - Couldn't download the URL content.
1535 function subscribe_to_feed($link, $url, $cat_id = 0,
1536 $auth_login = '', $auth_pass = '') {
1538 global $fetch_last_error;
1540 require_once "include/rssfuncs.php";
1542 $url = fix_url($url);
1544 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1546 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1549 return array("code" => 5, "message" => $fetch_last_error);
1552 if (is_html($contents)) {
1553 $feedUrls = get_feeds_from_html($url, $contents);
1555 if (count($feedUrls) == 0) {
1556 return array("code" => 3);
1557 } else if (count($feedUrls) > 1) {
1558 return array("code" => 4, "feeds" => $feedUrls);
1560 //use feed url as new URL
1561 $url = key($feedUrls);
1564 if ($cat_id == "0" ||
!$cat_id) {
1565 $cat_qpart = "NULL";
1567 $cat_qpart = "'$cat_id'";
1570 $result = db_query($link,
1571 "SELECT id FROM ttrss_feeds
1572 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1574 if (db_num_rows($result) == 0) {
1575 $result = db_query($link,
1576 "INSERT INTO ttrss_feeds
1577 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1578 VALUES ('".$_SESSION["uid"]."', '$url',
1579 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1581 $result = db_query($link,
1582 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1583 AND owner_uid = " . $_SESSION["uid"]);
1585 $feed_id = db_fetch_result($result, 0, "id");
1588 update_rss_feed($link, $feed_id, true);
1591 return array("code" => 1);
1593 return array("code" => 0);
1597 function print_feed_select($link, $id, $default_id = "",
1598 $attributes = "", $include_all_feeds = true,
1599 $root_id = false, $nest_level = 0) {
1602 print "<select id=\"$id\" name=\"$id\" $attributes>";
1603 if ($include_all_feeds) {
1604 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1605 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1609 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1612 $parent_qpart = "parent_cat = '$root_id'";
1614 $parent_qpart = "parent_cat IS NULL";
1616 $result = db_query($link, "SELECT id,title,
1617 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1618 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1619 FROM ttrss_feed_categories
1620 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1622 while ($line = db_fetch_assoc($result)) {
1624 for ($i = 0; $i < $nest_level; $i++
)
1625 $line["title"] = " - " . $line["title"];
1627 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1629 printf("<option $is_selected value='CAT:%d'>%s</option>",
1630 $line["id"], htmlspecialchars($line["title"]));
1632 if ($line["num_children"] > 0)
1633 print_feed_select($link, $id, $default_id, $attributes,
1634 $include_all_feeds, $line["id"], $nest_level+
1);
1636 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1637 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1639 while ($fline = db_fetch_assoc($feed_result)) {
1640 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1642 $fline["title"] = " + " . $fline["title"];
1644 for ($i = 0; $i < $nest_level; $i++
)
1645 $fline["title"] = " - " . $fline["title"];
1647 printf("<option $is_selected value='%d'>%s</option>",
1648 $fline["id"], htmlspecialchars($fline["title"]));
1653 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1655 printf("<option $is_selected value='CAT:0'>%s</option>",
1656 __("Uncategorized"));
1658 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1659 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1661 while ($fline = db_fetch_assoc($feed_result)) {
1662 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1664 $fline["title"] = " + " . $fline["title"];
1666 for ($i = 0; $i < $nest_level; $i++
)
1667 $fline["title"] = " - " . $fline["title"];
1669 printf("<option $is_selected value='%d'>%s</option>",
1670 $fline["id"], htmlspecialchars($fline["title"]));
1675 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1676 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1678 while ($line = db_fetch_assoc($result)) {
1680 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1682 printf("<option $is_selected value='%d'>%s</option>",
1683 $line["id"], htmlspecialchars($line["title"]));
1692 function print_feed_cat_select($link, $id, $default_id,
1693 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1696 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1700 $parent_qpart = "parent_cat = '$root_id'";
1702 $parent_qpart = "parent_cat IS NULL";
1704 $result = db_query($link, "SELECT id,title,
1705 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1706 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1707 FROM ttrss_feed_categories
1708 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1710 while ($line = db_fetch_assoc($result)) {
1711 if ($line["id"] == $default_id) {
1712 $is_selected = "selected=\"1\"";
1717 for ($i = 0; $i < $nest_level; $i++
)
1718 $line["title"] = " - " . $line["title"];
1721 printf("<option $is_selected value='%d'>%s</option>",
1722 $line["id"], htmlspecialchars($line["title"]));
1724 if ($line["num_children"] > 0)
1725 print_feed_cat_select($link, $id, $default_id, $attributes,
1726 $include_all_cats, $line["id"], $nest_level+
1);
1730 if ($include_all_cats) {
1731 if (db_num_rows($result) > 0) {
1732 print "<option disabled=\"1\">--------</option>";
1735 if ($default_id == 0) {
1736 $is_selected = "selected=\"1\"";
1741 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1747 function checkbox_to_sql_bool($val) {
1748 return ($val == "on") ?
"true" : "false";
1751 function getFeedCatTitle($link, $id) {
1753 return __("Special");
1754 } else if ($id < LABEL_BASE_INDEX
) {
1755 return __("Labels");
1756 } else if ($id > 0) {
1757 $result = db_query($link, "SELECT ttrss_feed_categories.title
1758 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1759 cat_id = ttrss_feed_categories.id");
1760 if (db_num_rows($result) == 1) {
1761 return db_fetch_result($result, 0, "title");
1763 return __("Uncategorized");
1766 return "getFeedCatTitle($id) failed";
1771 function getFeedIcon($id) {
1774 return "images/archive.png";
1777 return "images/mark_set.svg";
1780 return "images/pub_set.svg";
1783 return "images/fresh.png";
1786 return "images/tag.png";
1789 return "images/recently_read.png";
1792 if ($id < LABEL_BASE_INDEX
) {
1793 return "images/label.png";
1795 if (file_exists(ICONS_DIR
. "/$id.ico"))
1796 return ICONS_URL
. "/$id.ico";
1802 function getFeedTitle($link, $id, $cat = false) {
1804 return getCategoryTitle($link, $id);
1805 } else if ($id == -1) {
1806 return __("Starred articles");
1807 } else if ($id == -2) {
1808 return __("Published articles");
1809 } else if ($id == -3) {
1810 return __("Fresh articles");
1811 } else if ($id == -4) {
1812 return __("All articles");
1813 } else if ($id === 0 ||
$id === "0") {
1814 return __("Archived articles");
1815 } else if ($id == -6) {
1816 return __("Recently read");
1817 } else if ($id < LABEL_BASE_INDEX
) {
1818 $label_id = feed_to_label_id($id);
1819 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1820 if (db_num_rows($result) == 1) {
1821 return db_fetch_result($result, 0, "caption");
1823 return "Unknown label ($label_id)";
1826 } else if (is_numeric($id) && $id > 0) {
1827 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1828 if (db_num_rows($result) == 1) {
1829 return db_fetch_result($result, 0, "title");
1831 return "Unknown feed ($id)";
1838 function make_init_params($link) {
1841 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1842 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1843 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
1844 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1846 $params[strtolower($param)] = (int) get_pref($link, $param);
1849 $params["icons_url"] = ICONS_URL
;
1850 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1851 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1852 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1853 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1854 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1855 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1857 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1858 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1860 $max_feed_id = db_fetch_result($result, 0, "mid");
1861 $num_feeds = db_fetch_result($result, 0, "nf");
1863 $params["max_feed_id"] = (int) $max_feed_id;
1864 $params["num_feeds"] = (int) $num_feeds;
1866 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1867 $params["hotkeys"] = get_hotkeys_map($link);
1869 $params["csrf_token"] = $_SESSION["csrf_token"];
1870 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1872 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1877 function get_hotkeys_info($link) {
1879 __("Navigation") => array(
1880 "next_feed" => __("Open next feed"),
1881 "prev_feed" => __("Open previous feed"),
1882 "next_article" => __("Open next article"),
1883 "prev_article" => __("Open previous article"),
1884 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1885 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1886 "search_dialog" => __("Show search dialog")),
1887 __("Article") => array(
1888 "toggle_mark" => __("Toggle starred"),
1889 "toggle_publ" => __("Toggle published"),
1890 "toggle_unread" => __("Toggle unread"),
1891 "edit_tags" => __("Edit tags"),
1892 "dismiss_selected" => __("Dismiss selected"),
1893 "dismiss_read" => __("Dismiss read"),
1894 "open_in_new_window" => __("Open in new window"),
1895 "catchup_below" => __("Mark below as read"),
1896 "catchup_above" => __("Mark above as read"),
1897 "article_scroll_down" => __("Scroll down"),
1898 "article_scroll_up" => __("Scroll up"),
1899 "select_article_cursor" => __("Select article under cursor"),
1900 "email_article" => __("Email article"),
1901 "close_article" => __("Close/collapse article"),
1902 "toggle_widescreen" => __("Toggle widescreen mode"),
1903 "toggle_embed_original" => __("Toggle embed original")),
1904 __("Article selection") => array(
1905 "select_all" => __("Select all articles"),
1906 "select_unread" => __("Select unread"),
1907 "select_marked" => __("Select starred"),
1908 "select_published" => __("Select published"),
1909 "select_invert" => __("Invert selection"),
1910 "select_none" => __("Deselect everything")),
1911 __("Feed") => array(
1912 "feed_refresh" => __("Refresh current feed"),
1913 "feed_unhide_read" => __("Un/hide read feeds"),
1914 "feed_subscribe" => __("Subscribe to feed"),
1915 "feed_edit" => __("Edit feed"),
1916 "feed_catchup" => __("Mark as read"),
1917 "feed_reverse" => __("Reverse headlines"),
1918 "feed_debug_update" => __("Debug feed update"),
1919 "catchup_all" => __("Mark all feeds as read"),
1920 "cat_toggle_collapse" => __("Un/collapse current category"),
1921 "toggle_combined_mode" => __("Toggle combined mode"),
1922 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1923 __("Go to") => array(
1924 "goto_all" => __("All articles"),
1925 "goto_fresh" => __("Fresh"),
1926 "goto_marked" => __("Starred"),
1927 "goto_published" => __("Published"),
1928 "goto_tagcloud" => __("Tag cloud"),
1929 "goto_prefs" => __("Preferences")),
1930 __("Other") => array(
1931 "create_label" => __("Create label"),
1932 "create_filter" => __("Create filter"),
1933 "collapse_sidebar" => __("Un/collapse sidebar"),
1934 "help_dialog" => __("Show help dialog"))
1940 function get_hotkeys_map($link) {
1942 // "navigation" => array(
1945 "n" => "next_article",
1946 "p" => "prev_article",
1947 "(38)|up" => "prev_article",
1948 "(40)|down" => "next_article",
1949 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1950 // "^(40)|Ctrl-down" => "next_article_noscroll",
1951 "(191)|/" => "search_dialog",
1952 // "article" => array(
1953 "s" => "toggle_mark",
1954 "*s" => "toggle_publ",
1955 "u" => "toggle_unread",
1956 "*t" => "edit_tags",
1957 "*d" => "dismiss_selected",
1958 "*x" => "dismiss_read",
1959 "o" => "open_in_new_window",
1960 "c p" => "catchup_below",
1961 "c n" => "catchup_above",
1962 "*n" => "article_scroll_down",
1963 "*p" => "article_scroll_up",
1964 "*(38)|Shift+up" => "article_scroll_up",
1965 "*(40)|Shift+down" => "article_scroll_down",
1966 "a *w" => "toggle_widescreen",
1967 "a e" => "toggle_embed_original",
1968 "e" => "email_article",
1969 "a q" => "close_article",
1970 // "article_selection" => array(
1971 "a a" => "select_all",
1972 "a u" => "select_unread",
1973 "a *u" => "select_marked",
1974 "a p" => "select_published",
1975 "a i" => "select_invert",
1976 "a n" => "select_none",
1978 "f r" => "feed_refresh",
1979 "f a" => "feed_unhide_read",
1980 "f s" => "feed_subscribe",
1981 "f e" => "feed_edit",
1982 "f q" => "feed_catchup",
1983 "f x" => "feed_reverse",
1984 "f *d" => "feed_debug_update",
1985 "f *c" => "toggle_combined_mode",
1986 "f c" => "toggle_cdm_expanded",
1987 "*q" => "catchup_all",
1988 "x" => "cat_toggle_collapse",
1990 "g a" => "goto_all",
1991 "g f" => "goto_fresh",
1992 "g s" => "goto_marked",
1993 "g p" => "goto_published",
1994 "g t" => "goto_tagcloud",
1995 "g *p" => "goto_prefs",
1996 // "other" => array(
1997 "(9)|Tab" => "select_article_cursor", // tab
1998 "c l" => "create_label",
1999 "c f" => "create_filter",
2000 "c s" => "collapse_sidebar",
2001 "^(191)|Ctrl+/" => "help_dialog",
2004 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2005 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2006 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2010 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2011 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2014 $prefixes = array();
2016 foreach (array_keys($hotkeys) as $hotkey) {
2017 $pair = explode(" ", $hotkey, 2);
2019 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2020 array_push($prefixes, $pair[0]);
2024 return array($prefixes, $hotkeys);
2027 function make_runtime_info($link) {
2030 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2031 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2033 $max_feed_id = db_fetch_result($result, 0, "mid");
2034 $num_feeds = db_fetch_result($result, 0, "nf");
2036 $data["max_feed_id"] = (int) $max_feed_id;
2037 $data["num_feeds"] = (int) $num_feeds;
2039 $data['last_article_id'] = getLastArticleId($link);
2040 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2042 $data['dep_ts'] = calculate_dep_timestamp();
2043 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2045 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2047 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2049 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2051 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2054 $stamp_delta = time() - $stamp;
2056 if ($stamp_delta > 1800) {
2060 $_SESSION["daemon_stamp_check"] = time();
2063 $data['daemon_stamp_ok'] = $stamp_check;
2065 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2067 $data['daemon_stamp'] = $stamp_fmt;
2072 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2073 $new_version_details = @check_for_update
($link);
2075 $data['new_version_available'] = (int) ($new_version_details != false);
2077 $_SESSION["last_version_check"] = time();
2078 $_SESSION["version_data"] = $new_version_details;
2084 function search_to_sql($link, $search) {
2086 $search_query_part = "";
2088 $keywords = explode(" ", $search);
2089 $query_keywords = array();
2091 foreach ($keywords as $k) {
2092 if (strpos($k, "-") === 0) {
2099 $commandpair = explode(":", mb_strtolower($k), 2);
2101 if ($commandpair[0] == "note" && $commandpair[1]) {
2103 if ($commandpair[1] == "true")
2104 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2106 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2108 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2110 if ($commandpair[1] == "true")
2111 array_push($query_keywords, "($not (marked = true))");
2113 array_push($query_keywords, "($not (marked = false))");
2115 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2117 if ($commandpair[1] == "true")
2118 array_push($query_keywords, "($not (published = true))");
2120 array_push($query_keywords, "($not (published = false))");
2122 } else if (strpos($k, "@") === 0) {
2124 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2125 $orig_ts = strtotime(substr($k, 1));
2126 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2128 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2130 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2132 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2133 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2137 $search_query_part = implode("AND", $query_keywords);
2139 return $search_query_part;
2142 function getParentCategories($link, $cat, $owner_uid) {
2145 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2146 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2148 while ($line = db_fetch_assoc($result)) {
2149 array_push($rv, $line["parent_cat"]);
2150 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2156 function getChildCategories($link, $cat, $owner_uid) {
2159 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2160 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2162 while ($line = db_fetch_assoc($result)) {
2163 array_push($rv, $line["id"]);
2164 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2170 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) {
2172 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2174 $ext_tables_part = "";
2178 if (SPHINX_ENABLED
) {
2179 $ids = join(",", @sphinx_search
($search, 0, 500));
2182 $search_query_part = "ref_id IN ($ids) AND ";
2184 $search_query_part = "ref_id = -1 AND ";
2187 $search_query_part = search_to_sql($link, $search);
2188 $search_query_part .= " AND ";
2192 $search_query_part = "";
2197 if (DB_TYPE
== "pgsql") {
2198 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2200 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2203 $override_order = "updated DESC";
2205 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2207 // Try to check if SQL regexp implementation chokes on a valid regexp
2208 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2209 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2210 WHERE $filter_query_part LIMIT 1", false);
2213 $test = db_fetch_result($result, 0, "true_val");
2216 $filter_query_part = "false AND";
2218 $filter_query_part .= " AND";
2221 $filter_query_part = "false AND";
2225 $filter_query_part = "";
2229 $since_id_part = "ttrss_entries.id > $since_id AND ";
2231 $since_id_part = "";
2234 $view_query_part = "";
2236 if ($view_mode == "adaptive") {
2238 $view_query_part = " ";
2239 } else if ($feed != -1) {
2241 $unread = getFeedUnread($link, $feed, $cat_view);
2243 if ($cat_view && $feed > 0 && $include_children)
2244 $unread +
= getCategoryChildrenUnread($link, $feed);
2247 $view_query_part = " unread = true AND ";
2252 if ($view_mode == "marked") {
2253 $view_query_part = " marked = true AND ";
2256 if ($view_mode == "has_note") {
2257 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2260 if ($view_mode == "published") {
2261 $view_query_part = " published = true AND ";
2264 if ($view_mode == "unread" && $feed != -6) {
2265 $view_query_part = " unread = true AND ";
2269 $limit_query_part = "LIMIT " . $limit;
2272 $allow_archived = false;
2274 $vfeed_query_part = "";
2276 // override query strategy and enable feed display when searching globally
2277 if ($search && $search_mode == "all_feeds") {
2278 $query_strategy_part = "true";
2279 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2281 } else if (!is_numeric($feed)) {
2282 $query_strategy_part = "true";
2283 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2284 id = feed_id) as feed_title,";
2285 } else if ($search && $search_mode == "this_cat") {
2286 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2289 if ($include_children) {
2290 $subcats = getChildCategories($link, $feed, $owner_uid);
2291 array_push($subcats, $feed);
2292 $cats_qpart = join(",", $subcats);
2294 $cats_qpart = $feed;
2297 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2300 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2303 } else if ($feed > 0) {
2308 if ($include_children) {
2310 $subcats = getChildCategories($link, $feed, $owner_uid);
2312 array_push($subcats, $feed);
2313 $query_strategy_part = "cat_id IN (".
2314 implode(",", $subcats).")";
2317 $query_strategy_part = "cat_id = '$feed'";
2321 $query_strategy_part = "cat_id IS NULL";
2324 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2327 $query_strategy_part = "feed_id = '$feed'";
2329 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2330 $query_strategy_part = "feed_id IS NULL";
2331 $allow_archived = true;
2332 } else if ($feed == 0 && $cat_view) { // uncategorized
2333 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2334 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2335 } else if ($feed == -1) { // starred virtual feed
2336 $query_strategy_part = "marked = true";
2337 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2338 $allow_archived = true;
2340 if (!$override_order) {
2341 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2344 } else if ($feed == -2) { // published virtual feed OR labels category
2347 $query_strategy_part = "published = true";
2348 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2349 $allow_archived = true;
2351 if (!$override_order) {
2352 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2356 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2358 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2360 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2361 ttrss_user_labels2.article_id = ref_id";
2364 } else if ($feed == -6) { // recently read
2365 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2366 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2367 $allow_archived = true;
2369 if (!$override_order) $override_order = "last_read DESC";
2370 } else if ($feed == -3) { // fresh virtual feed
2371 $query_strategy_part = "unread = true AND score >= 0";
2373 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2375 if (DB_TYPE
== "pgsql") {
2376 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2378 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2381 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2382 } else if ($feed == -4) { // all articles virtual feed
2383 $query_strategy_part = "true";
2384 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2385 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2386 $label_id = feed_to_label_id($feed);
2388 $query_strategy_part = "label_id = '$label_id' AND
2389 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2390 ttrss_user_labels2.article_id = ref_id";
2392 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2393 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2394 $allow_archived = true;
2397 $query_strategy_part = "true";
2400 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2401 $date_sort_field = "updated";
2403 $date_sort_field = "date_entered";
2406 $order_by = "$date_sort_field DESC, updated DESC";
2408 if ($view_mode == "unread_first") {
2409 $order_by = "unread DESC, $order_by";
2412 if ($override_order) {
2413 $order_by = $override_order;
2419 $feed_title = T_sprintf("Search results: %s", $search);
2422 $feed_title = getCategoryTitle($link, $feed);
2424 if (is_numeric($feed) && $feed > 0) {
2425 $result = db_query($link, "SELECT title,site_url,last_error
2426 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2428 $feed_title = db_fetch_result($result, 0, "title");
2429 $feed_site_url = db_fetch_result($result, 0, "site_url");
2430 $last_error = db_fetch_result($result, 0, "last_error");
2432 $feed_title = getFeedTitle($link, $feed);
2437 $content_query_part = "content as content_preview, cached_content, ";
2439 if (is_numeric($feed)) {
2442 $feed_kind = "Feeds";
2444 $feed_kind = "Labels";
2447 if ($limit_query_part) {
2448 $offset_query_part = "OFFSET $offset";
2451 // proper override_order applied above
2452 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2453 if (!$override_order) {
2454 $order_by = "ttrss_feeds.title, $order_by";
2456 $order_by = "ttrss_feeds.title, $override_order";
2460 if (!$allow_archived) {
2461 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2462 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2465 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2466 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2469 $query = "SELECT DISTINCT
2472 ttrss_entries.id,ttrss_entries.title,
2476 always_display_enclosures,
2483 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2484 last_marked, last_published,
2492 ttrss_user_entries.ref_id = ttrss_entries.id AND
2493 ttrss_user_entries.owner_uid = '$owner_uid' AND
2498 $query_strategy_part ORDER BY $order_by
2499 $limit_query_part $offset_query_part";
2501 if ($_REQUEST["debug"]) print $query;
2503 $result = db_query($link, $query);
2508 $select_qpart = "SELECT DISTINCT " .
2512 "ttrss_entries.id as id," .
2525 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2526 "last_marked, last_published, " .
2529 $content_query_part .
2532 $feed_kind = "Tags";
2533 $all_tags = explode(",", $feed);
2534 if ($search_mode == 'any') {
2535 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2536 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2537 $where_qpart = " WHERE " .
2538 "ref_id = ttrss_entries.id AND " .
2539 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2540 "post_int_id = int_id AND $tag_sql AND " .
2542 $search_query_part .
2543 $query_strategy_part . " ORDER BY $order_by " .
2548 $sub_selects = array();
2549 $sub_ands = array();
2550 foreach ($all_tags as $term) {
2551 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");
2558 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2563 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2564 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2565 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2566 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2568 // error_log("TAG SQL: " . $tag_sql);
2569 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2571 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2572 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2575 return array($result, $feed_title, $feed_site_url, $last_error);
2579 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2580 if (!$owner) $owner = $_SESSION["uid"];
2582 $res = trim($str); if (!$res) return '';
2584 if (strpos($res, "href=") === false)
2585 $res = rewrite_urls($res);
2587 $charset_hack = '<head>
2588 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2591 $res = trim($res); if (!$res) return '';
2593 libxml_use_internal_errors(true);
2595 $doc = new DOMDocument();
2596 $doc->loadHTML($charset_hack . $res);
2597 $xpath = new DOMXPath($doc);
2599 $entries = $xpath->query('(//a[@href]|//img[@src])');
2601 foreach ($entries as $entry) {
2605 if ($entry->hasAttribute('href'))
2606 $entry->setAttribute('href',
2607 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2609 if ($entry->hasAttribute('src')) {
2610 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2612 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2614 if (file_exists($cached_filename)) {
2615 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2618 $entry->setAttribute('src', $src);
2621 if ($entry->nodeName
== 'img') {
2622 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2623 $force_remove_images ||
$_SESSION["bw_limit"]) {
2625 $p = $doc->createElement('p');
2627 $a = $doc->createElement('a');
2628 $a->setAttribute('href', $entry->getAttribute('src'));
2630 $a->appendChild(new DOMText($entry->getAttribute('src')));
2631 $a->setAttribute('target', '_blank');
2633 $p->appendChild($a);
2635 $entry->parentNode
->replaceChild($p, $entry);
2640 if (strtolower($entry->nodeName
) == "a") {
2641 $entry->setAttribute("target", "_blank");
2645 $entries = $xpath->query('//iframe');
2646 foreach ($entries as $entry) {
2647 $entry->setAttribute('sandbox', 'allow-scripts');
2651 $allowed_elements = array('a', 'address', 'audio', 'article',
2652 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2653 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2654 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2655 'header', 'html', 'i', 'img', 'ins', 'kbd',
2656 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2657 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2658 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2659 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2661 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2663 $disallowed_attributes = array('id', 'style', 'class');
2667 if (isset($pluginhost)) {
2668 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2669 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2670 if (is_array($retval)) {
2672 $allowed_elements = $retval[1];
2673 $disallowed_attributes = $retval[2];
2680 $doc->removeChild($doc->firstChild
); //remove doctype
2681 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2682 $res = $doc->saveHTML();
2686 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2687 $entries = $doc->getElementsByTagName("*");
2689 foreach ($entries as $entry) {
2690 if (!in_array($entry->nodeName
, $allowed_elements)) {
2691 $entry->parentNode
->removeChild($entry);
2694 if ($entry->hasAttributes()) {
2695 $attrs_to_remove = array();
2697 foreach ($entry->attributes
as $attr) {
2699 if (strpos($attr->nodeName
, 'on') === 0) {
2700 array_push($attrs_to_remove, $attr);
2703 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2704 array_push($attrs_to_remove, $attr);
2708 foreach ($attrs_to_remove as $attr) {
2709 $entry->removeAttributeNode($attr);
2717 function check_for_update($link) {
2718 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2719 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2720 "&iid=" . sha1(SELF_URL_PATH
);
2722 $version_data = @fetch_file_contents
($version_url);
2724 if ($version_data) {
2725 $version_data = json_decode($version_data, true);
2726 if ($version_data && $version_data['version']) {
2728 if (version_compare(VERSION
, $version_data['version']) == -1) {
2729 return $version_data;
2737 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2739 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2740 if (count($ids) == 0) return;
2744 foreach ($ids as $id) {
2745 array_push($tmp_ids, "ref_id = '$id'");
2748 $ids_qpart = join(" OR ", $tmp_ids);
2751 db_query($link, "UPDATE ttrss_user_entries SET
2752 unread = false,last_read = NOW()
2753 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2754 } else if ($cmode == 1) {
2755 db_query($link, "UPDATE ttrss_user_entries SET
2757 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2759 db_query($link, "UPDATE ttrss_user_entries SET
2760 unread = NOT unread,last_read = NOW()
2761 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2766 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2767 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2769 while ($line = db_fetch_assoc($result)) {
2770 ccache_update($link, $line["feed_id"], $owner_uid);
2774 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2776 $a_id = db_escape_string($link, $id);
2778 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2780 $query = "SELECT DISTINCT tag_name,
2781 owner_uid as owner FROM
2782 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2783 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2785 $obj_id = md5("TAGS:$owner_uid:$id");
2788 /* check cache first */
2790 if ($tag_cache === false) {
2791 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2792 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2794 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2798 $tags = explode(",", $tag_cache);
2801 /* do it the hard way */
2803 $tmp_result = db_query($link, $query);
2805 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2806 array_push($tags, $tmp_line["tag_name"]);
2809 /* update the cache */
2811 $tags_str = db_escape_string($link, join(",", $tags));
2813 db_query($link, "UPDATE ttrss_user_entries
2814 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2815 AND owner_uid = $owner_uid");
2821 function trim_array($array) {
2823 array_walk($tmp, 'trim');
2827 function tag_is_valid($tag) {
2828 if ($tag == '') return false;
2829 if (preg_match("/^[0-9]*$/", $tag)) return false;
2830 if (mb_strlen($tag) > 250) return false;
2832 if (function_exists('iconv')) {
2833 $tag = iconv("utf-8", "utf-8", $tag);
2836 if (!$tag) return false;
2841 function render_login_form($link) {
2842 require_once "login_form.php";
2846 // from http://developer.apple.com/internet/safari/faq.html
2847 function no_cache_incantation() {
2848 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2849 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2850 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2851 header("Cache-Control: post-check=0, pre-check=0", false);
2852 header("Pragma: no-cache"); // HTTP/1.0
2855 function format_warning($msg, $id = "") {
2857 return "<div class=\"warning\" id=\"$id\">
2858 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2861 function format_notice($msg, $id = "") {
2863 return "<div class=\"notice\" id=\"$id\">
2864 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2867 function format_error($msg, $id = "") {
2869 return "<div class=\"error\" id=\"$id\">
2870 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2873 function print_notice($msg) {
2874 return print format_notice($msg);
2877 function print_warning($msg) {
2878 return print format_warning($msg);
2881 function print_error($msg) {
2882 return print format_error($msg);
2886 function T_sprintf() {
2887 $args = func_get_args();
2888 return vsprintf(__(array_shift($args)), $args);
2891 function format_inline_player($link, $url, $ctype) {
2895 $url = htmlspecialchars($url);
2897 if (strpos($ctype, "audio/") === 0) {
2899 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2900 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2901 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2903 $id = 'AUDIO-' . uniqid();
2905 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2906 <source type=\"$ctype\" src=\"$url\"></source>
2909 $entry .= "<span onclick=\"player(this)\"
2910 title=\"".__("Click to play")."\" status=\"0\"
2911 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2915 $entry .= "<object type=\"application/x-shockwave-flash\"
2916 data=\"lib/button/musicplayer.swf?song_url=$url\"
2917 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2918 <param name=\"movie\"
2919 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2923 if ($entry) $entry .= " <a target=\"_blank\"
2924 href=\"$url\">" . basename($url) . "</a>";
2932 /* $filename = substr($url, strrpos($url, "/")+1);
2934 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2935 $filename . " (" . $ctype . ")" . "</a>"; */
2939 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2940 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2946 /* we can figure out feed_id from article id anyway, why do we
2947 * pass feed_id here? let's ignore the argument :( */
2949 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2950 WHERE ref_id = '$id'");
2952 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2954 $rv['feed_id'] = $feed_id;
2956 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2958 if ($mark_as_read) {
2959 $result = db_query($link, "UPDATE ttrss_user_entries
2960 SET unread = false,last_read = NOW()
2961 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2963 ccache_update($link, $feed_id, $owner_uid);
2966 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2967 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2968 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2969 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2976 FROM ttrss_entries,ttrss_user_entries
2977 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2981 $line = db_fetch_assoc($result);
2983 $tag_cache = $line["tag_cache"];
2985 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
2986 unset($line["tag_cache"]);
2988 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
2992 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
2993 $line = $p->hook_render_article($line);
2996 $num_comments = $line["num_comments"];
2997 $entry_comments = "";
2999 if ($num_comments > 0) {
3000 if ($line["comments"]) {
3001 $comments_url = htmlspecialchars($line["comments"]);
3003 $comments_url = htmlspecialchars($line["link"]);
3005 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3007 if ($line["comments"] && $line["link"] != $line["comments"]) {
3008 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3013 header("Content-Type: text/html");
3014 $rv['content'] .= "<html><head>
3015 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3016 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3017 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3018 </head><body id=\"ttrssZoom\">";
3021 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3023 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3025 $entry_author = $line["author"];
3027 if ($entry_author) {
3028 $entry_author = __(" - ") . $entry_author;
3031 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3034 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3036 if ($line["link"]) {
3037 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3038 title=\"".htmlspecialchars($line['title'])."\"
3040 htmlspecialchars($line["link"]) . "\">" .
3041 $line["title"] . "</a>" .
3042 "<span class='author'>$entry_author</span></div>";
3044 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3047 $tags_str = format_tags_string($line["tags"], $id);
3048 $tags_str_full = join(", ", $line["tags"]);
3050 if (!$tags_str_full) $tags_str_full = __("no tags");
3052 if (!$entry_comments) $entry_comments = " "; # placeholder
3054 $rv['content'] .= "<div class='postTags' style='float : right'>
3055 <img src='images/tag.png'
3056 class='tagsPic' alt='Tags' title='Tags'> ";
3059 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3060 <a title=\"".__('Edit tags for this article')."\"
3061 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3063 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3064 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3065 position=\"below\">$tags_str_full</div>";
3069 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3070 $rv['content'] .= $p->hook_article_button($line);
3075 $tags_str = strip_tags($tags_str);
3076 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3078 $rv['content'] .= "</div>";
3079 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3081 if ($line["orig_feed_id"]) {
3083 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3084 WHERE id = ".$line["orig_feed_id"]);
3086 if (db_num_rows($tmp_result) != 0) {
3088 $rv['content'] .= "<div clear='both'>";
3089 $rv['content'] .= __("Originally from:");
3091 $rv['content'] .= " ";
3093 $tmp_line = db_fetch_assoc($tmp_result);
3095 $rv['content'] .= "<a target='_blank'
3096 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3097 $tmp_line['title'] . "</a>";
3099 $rv['content'] .= " ";
3101 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3102 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3104 $rv['content'] .= "</div>";
3108 $rv['content'] .= "</div>";
3110 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3111 if ($line['note']) {
3112 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3114 $rv['content'] .= "</div>";
3116 $rv['content'] .= "<div class=\"postContent\">";
3118 $rv['content'] .= $line["content"];
3120 $rv['content'] .= format_article_enclosures($link, $id,
3121 $always_display_enclosures, $line["content"], $line["hide_images"]);
3123 $rv['content'] .= "</div>";
3125 $rv['content'] .= "</div>";
3131 <div class='footer'>
3132 <button onclick=\"return window.close()\">".
3133 __("Close this window")."</button></div>";
3134 $rv['content'] .= "</body></html>";
3141 function print_checkpoint($n, $s) {
3142 $ts = microtime(true);
3143 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3147 function sanitize_tag($tag) {
3150 $tag = mb_strtolower($tag, 'utf-8');
3152 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3154 // $tag = str_replace('"', "", $tag);
3155 // $tag = str_replace("+", " ", $tag);
3156 $tag = str_replace("technorati tag: ", "", $tag);
3161 function get_self_url_prefix() {
3162 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3163 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3165 return SELF_URL_PATH
;
3170 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3172 * @return string The Mozilla Firefox feed adding URL.
3174 function add_feed_url() {
3175 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3177 $url_path = get_self_url_prefix() .
3178 "/public.php?op=subscribe&feed_url=%s";
3180 } // function add_feed_url
3182 function encrypt_password($pass, $salt = '', $mode2 = false) {
3183 if ($salt && $mode2) {
3184 return "MODE2:" . hash('sha256', $salt . $pass);
3186 return "SHA1X:" . sha1("$salt:$pass");
3188 return "SHA1:" . sha1($pass);
3190 } // function encrypt_password
3192 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3195 $cat_id = (int)getFeedCategory($link, $feed_id);
3197 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3198 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3200 $check_cats = join(",", array_merge(
3201 getParentCategories($link, $cat_id, $owner_uid),
3204 while ($line = db_fetch_assoc($result)) {
3205 $filter_id = $line["id"];
3207 $result2 = db_query($link, "SELECT
3208 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3209 FROM ttrss_filters2_rules AS r,
3210 ttrss_filter_types AS t
3212 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3213 (feed_id IS NULL OR feed_id = '$feed_id') AND
3214 filter_type = t.id AND filter_id = '$filter_id'");
3219 while ($rule_line = db_fetch_assoc($result2)) {
3220 # print_r($rule_line);
3223 $rule["reg_exp"] = $rule_line["reg_exp"];
3224 $rule["type"] = $rule_line["type_name"];
3225 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3227 array_push($rules, $rule);
3230 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3231 FROM ttrss_filters2_actions AS a,
3232 ttrss_filter_actions AS t
3234 action_id = t.id AND filter_id = '$filter_id'");
3236 while ($action_line = db_fetch_assoc($result2)) {
3237 # print_r($action_line);
3240 $action["type"] = $action_line["type_name"];
3241 $action["param"] = $action_line["action_param"];
3243 array_push($actions, $action);
3248 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3249 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3250 $filter["rules"] = $rules;
3251 $filter["actions"] = $actions;
3253 if (count($rules) > 0 && count($actions) > 0) {
3254 array_push($filters, $filter);
3261 function get_score_pic($score) {
3263 return "score_high.png";
3264 } else if ($score > 0) {
3265 return "score_half_high.png";
3266 } else if ($score < -100) {
3267 return "score_low.png";
3268 } else if ($score < 0) {
3269 return "score_half_low.png";
3271 return "score_neutral.png";
3275 function feed_has_icon($id) {
3276 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3279 function init_connection($link) {
3282 if (DB_TYPE
== "pgsql") {
3283 pg_query($link, "set client_encoding = 'UTF-8'");
3284 pg_set_client_encoding("UNICODE");
3285 pg_query($link, "set datestyle = 'ISO, european'");
3286 pg_query($link, "set TIME ZONE 0");
3288 db_query($link, "SET time_zone = '+0:0'");
3290 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3291 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3297 $pluginhost = new PluginHost($link);
3298 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3302 print "Unable to connect to database:" . db_last_error();
3307 function format_tags_string($tags, $id) {
3310 $tags_nolinks_str = "";
3316 $formatted_tags = array();
3318 foreach ($tags as $tag) {
3320 $tag_escaped = str_replace("'", "\\'", $tag);
3322 if (mb_strlen($tag) > 30) {
3323 $tag = truncate_string($tag, 30);
3326 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3328 array_push($formatted_tags, $tag_str);
3330 $tmp_tags_str = implode(", ", $formatted_tags);
3332 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3337 $tags_str = implode(", ", $formatted_tags);
3339 if ($num_tags < count($tags)) {
3340 $tags_str .= ", …";
3343 if ($num_tags == 0) {
3344 $tags_str = __("no tags");
3351 function format_article_labels($labels, $id) {
3355 foreach ($labels as $l) {
3356 $labels_str .= sprintf("<span class='hlLabelRef'
3357 style='color : %s; background-color : %s'>%s</span>",
3358 $l[2], $l[3], $l[1]);
3365 function format_article_note($id, $note, $allow_edit = true) {
3367 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3368 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3369 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3375 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3376 if ($parent_cat_id) {
3377 $parent_qpart = "parent_cat = '$parent_cat_id'";
3378 $parent_insert = "'$parent_cat_id'";
3380 $parent_qpart = "parent_cat IS NULL";
3381 $parent_insert = "NULL";
3384 $result = db_query($link,
3385 "SELECT id FROM ttrss_feed_categories
3386 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3388 if (db_num_rows($result) == 0) {
3391 return db_fetch_result($result, 0, "id");
3395 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3397 if (!$feed_cat) return false;
3399 db_query($link, "BEGIN");
3401 if ($parent_cat_id) {
3402 $parent_qpart = "parent_cat = '$parent_cat_id'";
3403 $parent_insert = "'$parent_cat_id'";
3405 $parent_qpart = "parent_cat IS NULL";
3406 $parent_insert = "NULL";
3409 $feed_cat = mb_substr($feed_cat, 0, 250);
3411 $result = db_query($link,
3412 "SELECT id FROM ttrss_feed_categories
3413 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3415 if (db_num_rows($result) == 0) {
3417 $result = db_query($link,
3418 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3419 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3421 db_query($link, "COMMIT");
3429 function getArticleFeed($link, $id) {
3430 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3431 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3433 if (db_num_rows($result) != 0) {
3434 return db_fetch_result($result, 0, "feed_id");
3441 * Fixes incomplete URLs by prepending "http://".
3442 * Also replaces feed:// with http://, and
3443 * prepends a trailing slash if the url is a domain name only.
3445 * @param string $url Possibly incomplete URL
3447 * @return string Fixed URL.
3449 function fix_url($url) {
3450 if (strpos($url, '://') === false) {
3451 $url = 'http://' . $url;
3452 } else if (substr($url, 0, 5) == 'feed:') {
3453 $url = 'http:' . substr($url, 5);
3456 //prepend slash if the URL has no slash in it
3457 // "http://www.example" -> "http://www.example/"
3458 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3462 if ($url != "http:///")
3468 function validate_feed_url($url) {
3469 $parts = parse_url($url);
3471 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3475 function get_article_enclosures($link, $id) {
3477 $query = "SELECT * FROM ttrss_enclosures
3478 WHERE post_id = '$id' AND content_url != ''";
3482 $result = db_query($link, $query);
3484 if (db_num_rows($result) > 0) {
3485 while ($line = db_fetch_assoc($result)) {
3486 array_push($rv, $line);
3493 function save_email_address($link, $email) {
3494 // FIXME: implement persistent storage of emails
3496 if (!$_SESSION['stored_emails'])
3497 $_SESSION['stored_emails'] = array();
3499 if (!in_array($email, $_SESSION['stored_emails']))
3500 array_push($_SESSION['stored_emails'], $email);
3504 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3506 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3508 $sql_is_cat = bool_to_sql_bool($is_cat);
3510 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3511 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3512 AND owner_uid = " . $owner_uid);
3514 if (db_num_rows($result) == 1) {
3515 return db_fetch_result($result, 0, "access_key");
3517 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3519 $result = db_query($link, "INSERT INTO ttrss_access_keys
3520 (access_key, feed_id, is_cat, owner_uid)
3521 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3528 function get_feeds_from_html($url, $content)
3530 $url = fix_url($url);
3531 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3533 libxml_use_internal_errors(true);
3535 $doc = new DOMDocument();
3536 $doc->loadHTML($content);
3537 $xpath = new DOMXPath($doc);
3538 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3539 $feedUrls = array();
3540 foreach ($entries as $entry) {
3541 if ($entry->hasAttribute('href')) {
3542 $title = $entry->getAttribute('title');
3544 $title = $entry->getAttribute('type');
3546 $feedUrl = rewrite_relative_url(
3547 $baseUrl, $entry->getAttribute('href')
3549 $feedUrls[$feedUrl] = $title;
3555 function is_html($content) {
3556 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3559 function url_is_html($url, $login = false, $pass = false) {
3560 return is_html(fetch_file_contents($url, false, $login, $pass));
3563 function print_label_select($link, $name, $value, $attributes = "") {
3565 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3566 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3568 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3569 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3571 while ($line = db_fetch_assoc($result)) {
3573 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3575 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3576 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3580 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3587 function format_article_enclosures($link, $id, $always_display_enclosures,
3588 $article_content, $hide_images = false) {
3590 $result = get_article_enclosures($link, $id);
3593 if (count($result) > 0) {
3595 $entries_html = array();
3597 $entries_inline = array();
3599 foreach ($result as $line) {
3601 $url = $line["content_url"];
3602 $ctype = $line["content_type"];
3604 if (!$ctype) $ctype = __("unknown type");
3606 $filename = substr($url, strrpos($url, "/")+
1);
3608 $player = format_inline_player($link, $url, $ctype);
3610 if ($player) array_push($entries_inline, $player);
3612 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3613 # $filename . " (" . $ctype . ")" . "</a>";
3615 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3616 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3618 array_push($entries_html, $entry);
3622 $entry["type"] = $ctype;
3623 $entry["filename"] = $filename;
3624 $entry["url"] = $url;
3626 array_push($entries, $entry);
3629 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3630 if ($always_display_enclosures ||
3631 !preg_match("/<img/i", $article_content)) {
3633 foreach ($entries as $entry) {
3635 if (preg_match("/image/", $entry["type"]) ||
3636 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3638 if (!$hide_images) {
3640 alt=\"".htmlspecialchars($entry["filename"])."\"
3641 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3643 $rv .= "<p><a target=\"_blank\"
3644 href=\"".htmlspecialchars($entry["url"])."\"
3645 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3653 if (count($entries_inline) > 0) {
3654 $rv .= "<hr clear='both'/>";
3655 foreach ($entries_inline as $entry) { $rv .= $entry; };
3656 $rv .= "<hr clear='both'/>";
3659 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3660 "<option value=''>" . __('Attachments')."</option>";
3662 foreach ($entries as $entry) {
3663 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3673 function getLastArticleId($link) {
3674 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3675 WHERE owner_uid = " . $_SESSION["uid"]);
3677 if (db_num_rows($result) == 1) {
3678 return db_fetch_result($result, 0, "id");
3684 function build_url($parts) {
3685 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3689 * Converts a (possibly) relative URL to a absolute one.
3691 * @param string $url Base URL (i.e. from where the document is)
3692 * @param string $rel_url Possibly relative URL in the document
3694 * @return string Absolute URL
3696 function rewrite_relative_url($url, $rel_url) {
3697 if (strpos($rel_url, "magnet:") === 0) {
3699 } else if (strpos($rel_url, "://") !== false) {
3701 } else if (strpos($rel_url, "//") === 0) {
3702 # protocol-relative URL (rare but they exist)
3704 } else if (strpos($rel_url, "/") === 0)
3706 $parts = parse_url($url);
3707 $parts['path'] = $rel_url;
3709 return build_url($parts);
3712 $parts = parse_url($url);
3713 if (!isset($parts['path'])) {
3714 $parts['path'] = '/';
3716 $dir = $parts['path'];
3717 if (substr($dir, -1) !== '/') {
3718 $dir = dirname($parts['path']);
3719 $dir !== '/' && $dir .= '/';
3721 $parts['path'] = $dir . $rel_url;
3723 return build_url($parts);
3727 function sphinx_search($query, $offset = 0, $limit = 30) {
3728 require_once 'lib/sphinxapi.php';
3730 $sphinxClient = new SphinxClient();
3732 $sphinxClient->SetServer('localhost', 9312);
3733 $sphinxClient->SetConnectTimeout(1);
3735 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3736 'feed_title' => 20));
3738 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3739 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3740 $sphinxClient->SetLimits($offset, $limit, 1000);
3741 $sphinxClient->SetArrayResult(false);
3742 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3744 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3748 if (is_array($result['matches'])) {
3749 foreach (array_keys($result['matches']) as $int_id) {
3750 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3751 array_push($ids, $ref_id);
3758 function cleanup_tags($link, $days = 14, $limit = 1000) {
3760 if (DB_TYPE
== "pgsql") {
3761 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3762 } else if (DB_TYPE
== "mysql") {
3763 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3768 while ($limit > 0) {
3771 $query = "SELECT ttrss_tags.id AS id
3772 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3773 WHERE post_int_id = int_id AND $interval_query AND
3774 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3776 $result = db_query($link, $query);
3780 while ($line = db_fetch_assoc($result)) {
3781 array_push($ids, $line['id']);
3784 if (count($ids) > 0) {
3785 $ids = join(",", $ids);
3787 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3788 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3793 $limit -= $limit_part;
3796 return $tags_deleted;
3799 function print_user_stylesheet($link) {
3800 $value = get_pref($link, 'USER_STYLESHEET');
3803 print "<style type=\"text/css\">";
3804 print str_replace("<br/>", "\n", $value);
3810 function rewrite_urls($html) {
3811 libxml_use_internal_errors(true);
3813 $charset_hack = '<head>
3814 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3817 $doc = new DOMDocument();
3818 $doc->loadHTML($charset_hack . $html);
3819 $xpath = new DOMXPath($doc);
3821 $entries = $xpath->query('//*/text()');
3823 foreach ($entries as $entry) {
3824 if (strstr($entry->wholeText
, "://") !== false) {
3825 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3826 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3828 if ($text != $entry->wholeText
) {
3829 $cdoc = new DOMDocument();
3830 $cdoc->loadHTML($charset_hack . $text);
3833 foreach ($cdoc->childNodes
as $cnode) {
3834 $cnode = $doc->importNode($cnode, true);
3837 $entry->parentNode
->insertBefore($cnode);
3841 $entry->parentNode
->removeChild($entry);
3847 $node = $doc->getElementsByTagName('body')->item(0);
3849 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3851 return $doc->saveXML($node);
3856 function filter_to_sql($link, $filter, $owner_uid) {
3859 if (DB_TYPE
== "pgsql")
3862 $reg_qpart = "REGEXP";
3864 foreach ($filter["rules"] AS $rule) {
3865 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3866 $rule['reg_exp']) !== FALSE;
3868 if ($regexp_valid) {
3870 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3872 switch ($rule["type"]) {
3874 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3875 $rule['reg_exp'] . "')";
3878 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3879 $rule['reg_exp'] . "')";
3882 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3883 $rule['reg_exp'] . "') OR LOWER(" .
3884 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3887 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3888 $rule['reg_exp'] . "')";
3891 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3892 $rule['reg_exp'] . "')";
3895 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3896 $rule['reg_exp'] . "')";
3900 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3902 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3903 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3906 if (isset($rule["cat_id"])) {
3908 if ($rule["cat_id"] > 0) {
3909 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3910 array_push($children, $rule["cat_id"]);
3912 $children = join(",", $children);
3914 $cat_qpart = "cat_id IN ($children)";
3916 $cat_qpart = "cat_id IS NULL";
3919 $qpart .= " AND $cat_qpart";
3922 array_push($query, "($qpart)");
3927 if (count($query) > 0) {
3928 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3930 $fullquery = "(false)";
3933 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3938 if (!function_exists('gzdecode')) {
3939 function gzdecode($string) { // no support for 2nd argument
3940 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3941 base64_encode($string));
3945 function get_random_bytes($length) {
3946 if (function_exists('openssl_random_pseudo_bytes')) {
3947 return openssl_random_pseudo_bytes($length);
3951 for ($i = 0; $i < $length; $i++
)
3952 $output .= chr(mt_rand(0, 255));
3958 function read_stdin() {
3959 $fp = fopen("php://stdin", "r");
3962 $line = trim(fgets($fp));
3970 function tmpdirname($path, $prefix) {
3971 // Use PHP's tmpfile function to create a temporary
3972 // directory name. Delete the file and keep the name.
3973 $tempname = tempnam($path,$prefix);
3977 if (!unlink($tempname))
3983 function getFeedCategory($link, $feed) {
3984 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3985 WHERE id = '$feed'");
3987 if (db_num_rows($result) > 0) {
3988 return db_fetch_result($result, 0, "cat_id");
3995 function implements_interface($class, $interface) {
3996 return in_array($interface, class_implements($class));
3999 function geturl($url){
4001 (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');
4003 $curl = curl_init();
4004 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4005 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4006 $header[] = "Cache-Control: max-age=0";
4007 $header[] = "Connection: keep-alive";
4008 $header[] = "Keep-Alive: 300";
4009 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4010 $header[] = "Accept-Language: en-us,en;q=0.5";
4011 $header[] = "Pragma: ";
4013 curl_setopt($curl, CURLOPT_URL
, $url);
4014 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4015 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4016 curl_setopt($curl, CURLOPT_HEADER
, true);
4017 curl_setopt($curl, CURLOPT_REFERER
, $url);
4018 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4019 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4020 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4021 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4022 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4024 $html = curl_exec($curl);
4026 $status = curl_getinfo($curl);
4029 if($status['http_code']!=200){
4030 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4031 list($header) = explode("\r\n\r\n", $html, 2);
4033 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4034 $url = trim(str_replace($matches[1],"",$matches[0]));
4035 $url_parsed = parse_url($url);
4036 return (isset($url_parsed))?
geturl($url, $referer):'';
4039 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4040 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4041 # $handle = @fopen('./curl.error.log', 'a');
4042 # fwrite($handle, $line);
4048 function get_minified_js($files) {
4049 require_once 'lib/jshrink/Minifier.php';
4053 foreach ($files as $js) {
4054 if (!isset($_GET['debug'])) {
4055 $cached_file = CACHE_DIR
. "/js/$js.js";
4057 if (file_exists($cached_file) &&
4058 is_readable($cached_file) &&
4059 filemtime($cached_file) >= filemtime("js/$js.js")) {
4061 $rv .= file_get_contents($cached_file);
4064 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4065 file_put_contents($cached_file, $minified);
4069 $rv .= file_get_contents("js/$js.js");
4076 function stylesheet_tag($filename) {
4077 $timestamp = filemtime($filename);
4079 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4082 function javascript_tag($filename) {
4085 if (!(strpos($filename, "?") === FALSE)) {
4086 $query = substr($filename, strpos($filename, "?")+
1);
4087 $filename = substr($filename, 0, strpos($filename, "?"));
4090 $timestamp = filemtime($filename);
4092 if ($query) $timestamp .= "&$query";
4094 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4097 function calculate_dep_timestamp() {
4098 $files = array_merge(glob("js/*.js"), glob("*.css"));
4102 foreach ($files as $file) {
4103 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4109 function T_js_decl($s1, $s2) {
4111 $s1 = preg_replace("/\n/", "", $s1);
4112 $s2 = preg_replace("/\n/", "", $s2);
4114 $s1 = preg_replace("/\"/", "\\\"", $s1);
4115 $s2 = preg_replace("/\"/", "\\\"", $s2);
4117 return "T_messages[\"$s1\"] = \"$s2\";\n";
4121 function init_js_translations() {
4123 print 'var T_messages = new Object();
4126 if (T_messages[msg]) {
4127 return T_messages[msg];
4133 function ngettext(msg1, msg2, n) {
4134 return (parseInt(n) > 1) ? msg2 : msg1;
4137 $l10n = _get_reader();
4139 for ($i = 0; $i < $l10n->total
; $i++
) {
4140 $orig = $l10n->get_original_string($i);
4141 $translation = __($orig);
4143 print T_js_decl($orig, $translation);
4147 function label_to_feed_id($label) {
4148 return LABEL_BASE_INDEX
- 1 - abs($label);
4151 function feed_to_label_id($feed) {
4152 return LABEL_BASE_INDEX
- 1 +
abs($feed);