2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 114);
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",
63 // "ru_RU" => "Русский",
64 "pt_BR" => "Portuguese/Brazil",
65 "zh_CN" => "Simplified Chinese",
71 require_once "lib/accept-to-gettext.php";
72 require_once "lib/gettext/gettext.inc";
75 function startup_gettext() {
77 # Get locale from Accept-Language header
78 $lang = al2gt(array_keys(get_translations()), "text/html");
80 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
81 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
84 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
85 $lang = $_SESSION["language"];
89 if (defined('LC_MESSAGES')) {
90 _setlocale(LC_MESSAGES
, $lang);
91 } else if (defined('LC_ALL')) {
92 _setlocale(LC_ALL
, $lang);
95 _bindtextdomain("messages", "locale");
97 _textdomain("messages");
98 _bind_textdomain_codeset("messages", "UTF-8");
104 require_once 'db-prefs.php';
105 require_once 'version.php';
106 require_once 'ccache.php';
107 require_once 'labels.php';
109 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
110 ini_set('user_agent', SELF_USER_AGENT
);
112 require_once 'lib/pubsubhubbub/publisher.php';
115 $utc_tz = new DateTimeZone('UTC');
116 $schema_version = false;
119 * Print a timestamped debug message.
121 * @param string $msg The debug message.
124 function _debug($msg) {
125 $ts = strftime("%H:%M:%S", time());
126 if (function_exists('posix_getpid')) {
127 $ts = "$ts/" . posix_getpid();
130 if (!(defined('QUIET') && QUIET
)) {
131 print "[$ts] $msg\n";
134 if (defined('LOGFILE')) {
135 $fp = fopen(LOGFILE
, 'a+');
138 fputs($fp, "[$ts] $msg\n");
146 * Purge a feed old posts.
148 * @param mixed $link A database connection.
149 * @param mixed $feed_id The id of the purged feed.
150 * @param mixed $purge_interval Olderness of purged posts.
151 * @param boolean $debug Set to True to enable the debug. False by default.
155 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
157 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
161 $result = db_query($link,
162 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
166 if (db_num_rows($result) == 1) {
167 $owner_uid = db_fetch_result($result, 0, "owner_uid");
170 if ($purge_interval == -1 ||
!$purge_interval) {
172 ccache_update($link, $feed_id, $owner_uid);
177 if (!$owner_uid) return;
179 if (FORCE_ARTICLE_PURGE
== 0) {
180 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
183 $purge_unread = true;
184 $purge_interval = FORCE_ARTICLE_PURGE
;
187 if (!$purge_unread) $query_limit = " unread = false AND ";
189 if (DB_TYPE
== "pgsql") {
190 $pg_version = get_pgsql_version($link);
192 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
194 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
195 ttrss_entries.id = ref_id AND
197 feed_id = '$feed_id' AND
199 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
203 $result = db_query($link, "DELETE FROM ttrss_user_entries
205 WHERE ttrss_entries.id = ref_id AND
207 feed_id = '$feed_id' AND
209 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
212 $rows = pg_affected_rows($result);
216 /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
217 marked = false AND feed_id = '$feed_id' AND
218 (SELECT date_updated FROM ttrss_entries WHERE
219 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
221 $result = db_query($link, "DELETE FROM ttrss_user_entries
222 USING ttrss_user_entries, ttrss_entries
223 WHERE ttrss_entries.id = ref_id AND
225 feed_id = '$feed_id' AND
227 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
229 $rows = mysql_affected_rows($link);
233 ccache_update($link, $feed_id, $owner_uid);
236 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
240 } // function purge_feed
242 function feed_purge_interval($link, $feed_id) {
244 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
245 WHERE id = '$feed_id'");
247 if (db_num_rows($result) == 1) {
248 $purge_interval = db_fetch_result($result, 0, "purge_interval");
249 $owner_uid = db_fetch_result($result, 0, "owner_uid");
251 if ($purge_interval == 0) $purge_interval = get_pref($link,
252 'PURGE_OLD_DAYS', $owner_uid);
254 return $purge_interval;
261 function purge_orphans($link, $do_output = false) {
263 // purge orphaned posts in main content table
264 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
265 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
268 $rows = db_affected_rows($link, $result);
269 _debug("Purged $rows orphaned posts.");
273 function get_feed_update_interval($link, $feed_id) {
274 $result = db_query($link, "SELECT owner_uid, update_interval FROM
275 ttrss_feeds WHERE id = '$feed_id'");
277 if (db_num_rows($result) == 1) {
278 $update_interval = db_fetch_result($result, 0, "update_interval");
279 $owner_uid = db_fetch_result($result, 0, "owner_uid");
281 if ($update_interval != 0) {
282 return $update_interval;
284 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
292 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
294 global $fetch_last_error;
295 global $fetch_last_error_code;
297 if (function_exists('curl_init') && !ini_get("open_basedir")) {
299 if (ini_get("safe_mode")) {
300 $ch = curl_init(geturl($url));
302 $ch = curl_init($url);
306 curl_setopt($ch, CURLOPT_HTTPHEADER
,
307 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
310 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : 15);
311 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : 45);
312 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode"));
313 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
314 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
315 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
316 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
317 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
318 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
319 curl_setopt($ch, CURLOPT_ENCODING
, "gzip");
320 curl_setopt($ch, CURLOPT_REFERER
, $url);
323 curl_setopt($ch, CURLOPT_POST
, true);
324 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
328 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
330 $contents = @curl_exec
($ch);
332 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
333 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
334 $contents = @curl_exec
($ch);
337 if ($contents === false) {
338 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
343 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
344 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
346 $fetch_last_error_code = $http_code;
348 if ($http_code != 200 ||
$type && strpos($content_type, "$type") === false) {
349 if (curl_errno($ch) != 0) {
350 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
352 $fetch_last_error = "HTTP Code: $http_code";
362 if ($login && $pass){
363 $url_parts = array();
365 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
367 $pass = urlencode($pass);
369 if ($url_parts[1] && $url_parts[2]) {
370 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
374 $data = @file_get_contents
($url);
376 @$gzdecoded = gzdecode($data);
377 if ($gzdecoded) $data = $gzdecoded;
379 if (!$data && function_exists('error_get_last')) {
380 $error = error_get_last();
381 $fetch_last_error = $error["message"];
389 * Try to determine the favicon URL for a feed.
390 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
391 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
393 * @param string $url A feed or page URL
395 * @return mixed The favicon URL, or false if none was found.
397 function get_favicon_url($url) {
399 $favicon_url = false;
401 if ($html = @fetch_file_contents
($url)) {
403 libxml_use_internal_errors(true);
405 $doc = new DOMDocument();
406 $doc->loadHTML($html);
407 $xpath = new DOMXPath($doc);
409 $base = $xpath->query('/html/head/base');
410 foreach ($base as $b) {
411 $url = $b->getAttribute("href");
415 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
416 if (count($entries) > 0) {
417 foreach ($entries as $entry) {
418 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
425 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
428 } // function get_favicon_url
430 function check_feed_favicon($site_url, $feed, $link) {
431 # print "FAVICON [$site_url]: $favicon_url\n";
433 $icon_file = ICONS_DIR
. "/$feed.ico";
435 if (!file_exists($icon_file)) {
436 $favicon_url = get_favicon_url($site_url);
439 // Limiting to "image" type misses those served with text/plain
440 $contents = fetch_file_contents($favicon_url); // , "image");
443 // Crude image type matching.
444 // Patterns gleaned from the file(1) source code.
445 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
446 // 0 string \000\000\001\000 MS Windows icon resource
447 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
449 elseif (preg_match('/^GIF8/', $contents)) {
450 // 0 string GIF8 GIF image data
451 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
453 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
454 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
455 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
457 elseif (preg_match('/^\xff\xd8/', $contents)) {
458 // 0 beshort 0xffd8 JPEG image data
459 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
462 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
468 $fp = @fopen
($icon_file, "w");
471 fwrite($fp, $contents);
473 chmod($icon_file, 0644);
480 function print_select($id, $default, $values, $attributes = "") {
481 print "<select name=\"$id\" id=\"$id\" $attributes>";
482 foreach ($values as $v) {
484 $sel = "selected=\"1\"";
490 print "<option value=\"$v\" $sel>$v</option>";
495 function print_select_hash($id, $default, $values, $attributes = "") {
496 print "<select name=\"$id\" id='$id' $attributes>";
497 foreach (array_keys($values) as $v) {
499 $sel = 'selected="selected"';
505 print "<option $sel value=\"$v\">".$values[$v]."</option>";
511 function print_radio($id, $default, $true_is, $values, $attributes = "") {
512 foreach ($values as $v) {
519 if ($v == $true_is) {
520 $sel .= " value=\"1\"";
522 $sel .= " value=\"0\"";
525 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
526 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
531 function initialize_user_prefs($link, $uid, $profile = false) {
533 $uid = db_escape_string($link, $uid);
537 $profile_qpart = "AND profile IS NULL";
539 $profile_qpart = "AND profile = '$profile'";
542 if (get_schema_version($link) < 63) $profile_qpart = "";
544 db_query($link, "BEGIN");
546 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
548 $u_result = db_query($link, "SELECT pref_name
549 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
551 $active_prefs = array();
553 while ($line = db_fetch_assoc($u_result)) {
554 array_push($active_prefs, $line["pref_name"]);
557 while ($line = db_fetch_assoc($result)) {
558 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
559 // print "adding " . $line["pref_name"] . "<br>";
561 $line["def_value"] = db_escape_string($link, $line["def_value"]);
562 $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
564 if (get_schema_version($link) < 63) {
565 db_query($link, "INSERT INTO ttrss_user_prefs
566 (owner_uid,pref_name,value) VALUES
567 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
570 db_query($link, "INSERT INTO ttrss_user_prefs
571 (owner_uid,pref_name,value, profile) VALUES
572 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
578 db_query($link, "COMMIT");
582 function get_ssl_certificate_id() {
583 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
584 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
585 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
586 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
587 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
592 function authenticate_user($link, $login, $password, $check_only = false) {
594 if (!SINGLE_USER_MODE
) {
598 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
600 $user_id = (int) $plugin->authenticate($login, $password);
603 $_SESSION["auth_module"] = strtolower(get_class($plugin));
608 if ($user_id && !$check_only) {
611 $_SESSION["uid"] = $user_id;
613 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
614 WHERE id = '$user_id'");
616 $_SESSION["name"] = db_fetch_result($result, 0, "login");
617 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
618 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
620 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
623 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
624 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
625 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
627 $_SESSION["last_version_check"] = time();
629 initialize_user_prefs($link, $_SESSION["uid"]);
638 $_SESSION["uid"] = 1;
639 $_SESSION["name"] = "admin";
640 $_SESSION["access_level"] = 10;
642 $_SESSION["hide_hello"] = true;
643 $_SESSION["hide_logout"] = true;
645 $_SESSION["auth_module"] = false;
647 if (!$_SESSION["csrf_token"]) {
648 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
651 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
653 initialize_user_prefs($link, $_SESSION["uid"]);
659 function make_password($length = 8) {
662 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
666 while ($i < $length) {
667 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
669 if (!strstr($password, $char)) {
677 // this is called after user is created to initialize default feeds, labels
680 // user preferences are checked on every login, not here
682 function initialize_user($link, $uid) {
684 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
685 values ('$uid', 'Tiny Tiny RSS: New Releases',
686 'http://tt-rss.org/releases.rss')");
688 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
689 values ('$uid', 'Tiny Tiny RSS: Forum',
690 'http://tt-rss.org/forum/rss.php')");
693 function logout_user() {
695 if (isset($_COOKIE[session_name()])) {
696 setcookie(session_name(), '', time()-42000, '/');
700 function validate_csrf($csrf_token) {
701 return $csrf_token == $_SESSION['csrf_token'];
704 function load_user_plugins($link, $owner_uid) {
706 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
709 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
711 if (get_schema_version($link) > 100) {
712 $pluginhost->load_data();
717 function login_sequence($link) {
718 $_SESSION["prefs_cache"] = false;
720 if (SINGLE_USER_MODE
) {
722 authenticate_user($link, "admin", null);
724 load_user_plugins($link, $_SESSION["uid"]);
726 if (!$_SESSION["uid"] ||
!validate_session($link)) {
728 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
729 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
731 authenticate_user($link, null, null, true);
734 if (!$_SESSION["uid"]) render_login_form($link);
737 /* bump login timestamp */
738 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
740 $_SESSION["last_login_update"] = time();
743 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
744 setcookie("ttrss_lang", $_SESSION["language"],
745 time() + SESSION_COOKIE_LIFETIME
);
748 if ($_SESSION["uid"]) {
750 load_user_plugins($link, $_SESSION["uid"]);
754 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
755 $_SESSION["uid"] . " AND
756 (SELECT COUNT(id) FROM ttrss_feeds WHERE
757 ttrss_feeds.id = feed_id) = 0");
759 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
760 $_SESSION["uid"] . " AND
761 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
762 ttrss_feed_categories.id = feed_id) = 0");
769 function truncate_string($str, $max_len, $suffix = '…') {
770 if (mb_strlen($str, "utf-8") > $max_len - 3) {
771 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
777 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
780 $source_tz = new DateTimeZone($source_tz);
781 } catch (Exception
$e) {
782 $source_tz = new DateTimeZone('UTC');
786 $dest_tz = new DateTimeZone($dest_tz);
787 } catch (Exception
$e) {
788 $dest_tz = new DateTimeZone('UTC');
791 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
792 return $dt->format('U') +
$dest_tz->getOffset($dt);
795 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
796 $no_smart_dt = false) {
798 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
799 if (!$timestamp) $timestamp = '1970-01-01 0:00';
804 # We store date in UTC internally
805 $dt = new DateTime($timestamp, $utc_tz);
807 if ($tz_offset == -1) {
809 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
812 $user_tz = new DateTimeZone($user_tz_string);
813 } catch (Exception
$e) {
817 $tz_offset = $user_tz->getOffset($dt);
820 $user_timestamp = $dt->format('U') +
$tz_offset;
823 return smart_date_time($link, $user_timestamp,
824 $tz_offset, $owner_uid);
827 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
829 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
831 return date($format, $user_timestamp);
835 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
836 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
838 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
839 return date("G:i", $timestamp);
840 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
841 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
842 return date($format, $timestamp);
844 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
845 return date($format, $timestamp);
849 function sql_bool_to_bool($s) {
850 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
857 function bool_to_sql_bool($s) {
865 // Session caching removed due to causing wrong redirects to upgrade
866 // script when get_schema_version() is called on an obsolete session
867 // created on a previous schema version.
868 function get_schema_version($link, $nocache = false) {
869 global $schema_version;
871 if (!$schema_version) {
872 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
873 $version = db_fetch_result($result, 0, "schema_version");
874 $schema_version = $version;
877 return $schema_version;
881 function sanity_check($link) {
882 require_once 'errors.php';
885 $schema_version = get_schema_version($link, true);
887 if ($schema_version != SCHEMA_VERSION
) {
891 if (DB_TYPE
== "mysql") {
892 $result = db_query($link, "SELECT true", false);
893 if (db_num_rows($result) != 1) {
898 if (db_escape_string($link, "testTEST") != "testTEST") {
902 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
905 function file_is_locked($filename) {
906 if (function_exists('flock')) {
907 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
909 if (flock($fp, LOCK_EX | LOCK_NB
)) {
920 return true; // consider the file always locked and skip the test
923 function make_lockfile($filename) {
924 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
926 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
927 if (function_exists('posix_getpid')) {
928 fwrite($fp, posix_getpid() . "\n");
936 function make_stampfile($filename) {
937 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
939 if (flock($fp, LOCK_EX | LOCK_NB
)) {
940 fwrite($fp, time() . "\n");
949 function sql_random_function() {
950 if (DB_TYPE
== "mysql") {
957 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
959 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
961 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
963 // Todo: all this interval stuff needs some generic generator function
965 $date_qpart = "false";
969 if (DB_TYPE
== "pgsql") {
970 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
972 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
976 if (DB_TYPE
== "pgsql") {
977 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
979 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
983 if (DB_TYPE
== "pgsql") {
984 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
986 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
990 $date_qpart = "true";
993 if (is_numeric($feed)) {
999 $children = getChildCategories($link, $feed, $owner_uid);
1000 array_push($children, $feed);
1002 $children = join(",", $children);
1004 $cat_qpart = "cat_id IN ($children)";
1006 $cat_qpart = "cat_id IS NULL";
1009 db_query($link, "UPDATE ttrss_user_entries
1010 SET unread = false, last_read = NOW() WHERE ref_id IN
1012 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1013 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1014 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1016 } else if ($feed == -2) {
1018 db_query($link, "UPDATE ttrss_user_entries
1019 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1020 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1021 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1024 } else if ($feed > 0) {
1026 db_query($link, "UPDATE ttrss_user_entries
1027 SET unread = false, last_read = NOW() WHERE ref_id IN
1029 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1030 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1032 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1035 db_query($link, "UPDATE ttrss_user_entries
1036 SET unread = false, last_read = NOW() WHERE ref_id IN
1038 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1039 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1043 db_query($link, "UPDATE ttrss_user_entries
1044 SET unread = false, last_read = NOW() WHERE ref_id IN
1046 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1047 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1052 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1054 if (DB_TYPE
== "pgsql") {
1055 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1057 $match_part = "updated > DATE_SUB(NOW(),
1058 INTERVAL $intl HOUR) ";
1061 db_query($link, "UPDATE ttrss_user_entries
1062 SET unread = false, last_read = NOW() WHERE ref_id IN
1064 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1065 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart AND $match_part) as tmp)");
1069 db_query($link, "UPDATE ttrss_user_entries
1070 SET unread = false, last_read = NOW() WHERE ref_id IN
1072 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1073 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1076 } else if ($feed < LABEL_BASE_INDEX
) { // label
1078 $label_id = feed_to_label_id($feed);
1080 db_query($link, "UPDATE ttrss_user_entries
1081 SET unread = false, last_read = NOW() WHERE ref_id IN
1083 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1084 AND label_id = '$label_id' AND ref_id = article_id
1085 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1089 ccache_update($link, $feed, $owner_uid, $cat_view);
1092 db_query($link, "UPDATE ttrss_user_entries
1093 SET unread = false, last_read = NOW() WHERE ref_id IN
1095 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1096 AND post_int_id = int_id AND tag_name = '$feed'
1097 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1102 function getAllCounters($link) {
1103 $data = getGlobalCounters($link);
1105 $data = array_merge($data, getVirtCounters($link));
1106 $data = array_merge($data, getLabelCounters($link));
1107 $data = array_merge($data, getFeedCounters($link, $active_feed));
1108 $data = array_merge($data, getCategoryCounters($link));
1113 function getCategoryTitle($link, $cat_id) {
1115 if ($cat_id == -1) {
1116 return __("Special");
1117 } else if ($cat_id == -2) {
1118 return __("Labels");
1121 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1124 if (db_num_rows($result) == 1) {
1125 return db_fetch_result($result, 0, "title");
1127 return __("Uncategorized");
1133 function getCategoryCounters($link) {
1136 /* Labels category */
1138 $cv = array("id" => -2, "kind" => "cat",
1139 "counter" => getCategoryUnread($link, -2));
1141 array_push($ret_arr, $cv);
1143 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1144 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1145 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1146 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1147 WHERE ttrss_cat_counters_cache.feed_id = id AND
1148 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1149 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1151 while ($line = db_fetch_assoc($result)) {
1152 $line["cat_id"] = (int) $line["cat_id"];
1154 if ($line["num_children"] > 0) {
1155 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1160 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1161 "counter" => $line["unread"] +
$child_counter);
1163 array_push($ret_arr, $cv);
1166 /* Special case: NULL category doesn't actually exist in the DB */
1168 $cv = array("id" => 0, "kind" => "cat",
1169 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1171 array_push($ret_arr, $cv);
1176 // only accepts real cats (>= 0)
1177 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1178 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1180 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1181 AND owner_uid = $owner_uid");
1185 while ($line = db_fetch_assoc($result)) {
1186 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1187 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1193 function getCategoryUnread($link, $cat, $owner_uid = false) {
1195 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1200 $cat_query = "cat_id = '$cat'";
1202 $cat_query = "cat_id IS NULL";
1205 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1206 AND owner_uid = " . $owner_uid);
1208 $cat_feeds = array();
1209 while ($line = db_fetch_assoc($result)) {
1210 array_push($cat_feeds, "feed_id = " . $line["id"]);
1213 if (count($cat_feeds) == 0) return 0;
1215 $match_part = implode(" OR ", $cat_feeds);
1217 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1218 FROM ttrss_user_entries
1219 WHERE unread = true AND ($match_part)
1220 AND owner_uid = " . $owner_uid);
1224 # this needs to be rewritten
1225 while ($line = db_fetch_assoc($result)) {
1226 $unread +
= $line["unread"];
1230 } else if ($cat == -1) {
1231 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1232 } else if ($cat == -2) {
1234 $result = db_query($link, "
1235 SELECT COUNT(unread) AS unread FROM
1236 ttrss_user_entries, ttrss_user_labels2
1237 WHERE article_id = ref_id AND unread = true
1238 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1240 $unread = db_fetch_result($result, 0, "unread");
1247 function getFeedUnread($link, $feed, $is_cat = false) {
1248 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1251 function getLabelUnread($link, $label_id, $owner_uid = false) {
1252 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1254 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1255 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1257 if (db_num_rows($result) != 0) {
1258 return db_fetch_result($result, 0, "unread");
1264 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1265 $owner_uid = false) {
1267 $n_feed = (int) $feed;
1268 $need_entries = false;
1270 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1273 $unread_qpart = "unread = true";
1275 $unread_qpart = "true";
1279 return getCategoryUnread($link, $n_feed, $owner_uid);
1280 } else if ($n_feed == -6) {
1282 } else if ($feed != "0" && $n_feed == 0) {
1284 $feed = db_escape_string($link, $feed);
1286 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1287 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1288 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1289 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1290 return db_fetch_result($result, 0, "count");
1292 } else if ($n_feed == -1) {
1293 $match_part = "marked = true";
1294 } else if ($n_feed == -2) {
1295 $match_part = "published = true";
1296 } else if ($n_feed == -3) {
1297 $match_part = "unread = true AND score >= 0";
1299 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1301 if (DB_TYPE
== "pgsql") {
1302 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1304 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1307 $need_entries = true;
1309 } else if ($n_feed == -4) {
1310 $match_part = "true";
1311 } else if ($n_feed >= 0) {
1314 $match_part = "feed_id = '$n_feed'";
1316 $match_part = "feed_id IS NULL";
1319 } else if ($feed < LABEL_BASE_INDEX
) {
1321 $label_id = feed_to_label_id($feed);
1323 return getLabelUnread($link, $label_id, $owner_uid);
1329 if ($need_entries) {
1330 $from_qpart = "ttrss_user_entries,ttrss_entries";
1331 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1333 $from_qpart = "ttrss_user_entries";
1336 $query = "SELECT count(int_id) AS unread
1337 FROM $from_qpart WHERE
1338 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1340 //echo "[$feed/$query]\n";
1342 $result = db_query($link, $query);
1346 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1347 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1348 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1349 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1352 $unread = db_fetch_result($result, 0, "unread");
1357 function getGlobalUnread($link, $user_id = false) {
1360 $user_id = $_SESSION["uid"];
1363 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1364 WHERE owner_uid = '$user_id' AND feed_id > 0");
1366 $c_id = db_fetch_result($result, 0, "c_id");
1371 function getGlobalCounters($link, $global_unread = -1) {
1374 if ($global_unread == -1) {
1375 $global_unread = getGlobalUnread($link);
1378 $cv = array("id" => "global-unread",
1379 "counter" => (int) $global_unread);
1381 array_push($ret_arr, $cv);
1383 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1384 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1386 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1388 $cv = array("id" => "subscribed-feeds",
1389 "counter" => (int) $subscribed_feeds);
1391 array_push($ret_arr, $cv);
1396 function getVirtCounters($link) {
1400 for ($i = 0; $i >= -4; $i--) {
1402 $count = getFeedUnread($link, $i);
1404 $cv = array("id" => $i,
1405 "counter" => (int) $count);
1407 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1408 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1410 array_push($ret_arr, $cv);
1416 $feeds = $pluginhost->get_feeds(-1);
1418 if (is_array($feeds)) {
1419 foreach ($feeds as $feed) {
1420 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1421 "counter" => $feed['sender']->get_unread($feed['id']));
1423 array_push($ret_arr, $cv);
1431 function getLabelCounters($link, $descriptions = false) {
1435 $owner_uid = $_SESSION["uid"];
1437 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1438 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1439 (ttrss_labels2.id = label_id)
1440 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1441 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1442 ttrss_labels2.caption");
1444 while ($line = db_fetch_assoc($result)) {
1446 $id = label_to_feed_id($line["id"]);
1448 $label_name = $line["caption"];
1449 $count = $line["unread"];
1451 $cv = array("id" => $id,
1452 "counter" => (int) $count);
1455 $cv["description"] = $label_name;
1457 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1458 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1460 array_push($ret_arr, $cv);
1466 function getFeedCounters($link, $active_feed = false) {
1470 $query = "SELECT ttrss_feeds.id,
1472 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1473 last_error, value AS count
1474 FROM ttrss_feeds, ttrss_counters_cache
1475 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1476 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1477 AND ttrss_counters_cache.feed_id = id";
1479 $result = db_query($link, $query);
1480 $fctrs_modified = false;
1482 while ($line = db_fetch_assoc($result)) {
1485 $count = $line["count"];
1486 $last_error = htmlspecialchars($line["last_error"]);
1488 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1490 $has_img = feed_has_icon($id);
1492 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1495 $cv = array("id" => $id,
1496 "updated" => $last_updated,
1497 "counter" => (int) $count,
1498 "has_img" => (int) $has_img);
1501 $cv["error"] = $last_error;
1503 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1504 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1506 if ($active_feed && $id == $active_feed)
1507 $cv["title"] = truncate_string($line["title"], 30);
1509 array_push($ret_arr, $cv);
1516 function get_pgsql_version($link) {
1517 $result = db_query($link, "SELECT version() AS version");
1518 $version = explode(" ", db_fetch_result($result, 0, "version"));
1523 * @return array (code => Status code, message => error message if available)
1525 * 0 - OK, Feed already exists
1526 * 1 - OK, Feed added
1528 * 3 - URL content is HTML, no feeds available
1529 * 4 - URL content is HTML which contains multiple feeds.
1530 * Here you should call extractfeedurls in rpc-backend
1531 * to get all possible feeds.
1532 * 5 - Couldn't download the URL content.
1534 function subscribe_to_feed($link, $url, $cat_id = 0,
1535 $auth_login = '', $auth_pass = '') {
1537 global $fetch_last_error;
1539 require_once "include/rssfuncs.php";
1541 $url = fix_url($url);
1543 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1545 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1548 return array("code" => 5, "message" => $fetch_last_error);
1551 if (is_html($contents)) {
1552 $feedUrls = get_feeds_from_html($url, $contents);
1554 if (count($feedUrls) == 0) {
1555 return array("code" => 3);
1556 } else if (count($feedUrls) > 1) {
1557 return array("code" => 4, "feeds" => $feedUrls);
1559 //use feed url as new URL
1560 $url = key($feedUrls);
1563 if ($cat_id == "0" ||
!$cat_id) {
1564 $cat_qpart = "NULL";
1566 $cat_qpart = "'$cat_id'";
1569 $result = db_query($link,
1570 "SELECT id FROM ttrss_feeds
1571 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1573 if (db_num_rows($result) == 0) {
1574 $result = db_query($link,
1575 "INSERT INTO ttrss_feeds
1576 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1577 VALUES ('".$_SESSION["uid"]."', '$url',
1578 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1580 $result = db_query($link,
1581 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1582 AND owner_uid = " . $_SESSION["uid"]);
1584 $feed_id = db_fetch_result($result, 0, "id");
1587 update_rss_feed($link, $feed_id, true);
1590 return array("code" => 1);
1592 return array("code" => 0);
1596 function print_feed_select($link, $id, $default_id = "",
1597 $attributes = "", $include_all_feeds = true,
1598 $root_id = false, $nest_level = 0) {
1601 print "<select id=\"$id\" name=\"$id\" $attributes>";
1602 if ($include_all_feeds) {
1603 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1604 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1608 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1611 $parent_qpart = "parent_cat = '$root_id'";
1613 $parent_qpart = "parent_cat IS NULL";
1615 $result = db_query($link, "SELECT id,title,
1616 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1617 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1618 FROM ttrss_feed_categories
1619 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1621 while ($line = db_fetch_assoc($result)) {
1623 for ($i = 0; $i < $nest_level; $i++
)
1624 $line["title"] = " - " . $line["title"];
1626 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1628 printf("<option $is_selected value='CAT:%d'>%s</option>",
1629 $line["id"], htmlspecialchars($line["title"]));
1631 if ($line["num_children"] > 0)
1632 print_feed_select($link, $id, $default_id, $attributes,
1633 $include_all_feeds, $line["id"], $nest_level+
1);
1635 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1636 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1638 while ($fline = db_fetch_assoc($feed_result)) {
1639 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1641 $fline["title"] = " + " . $fline["title"];
1643 for ($i = 0; $i < $nest_level; $i++
)
1644 $fline["title"] = " - " . $fline["title"];
1646 printf("<option $is_selected value='%d'>%s</option>",
1647 $fline["id"], htmlspecialchars($fline["title"]));
1652 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1654 printf("<option $is_selected value='CAT:0'>%s</option>",
1655 __("Uncategorized"));
1657 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1658 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1660 while ($fline = db_fetch_assoc($feed_result)) {
1661 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1663 $fline["title"] = " + " . $fline["title"];
1665 for ($i = 0; $i < $nest_level; $i++
)
1666 $fline["title"] = " - " . $fline["title"];
1668 printf("<option $is_selected value='%d'>%s</option>",
1669 $fline["id"], htmlspecialchars($fline["title"]));
1674 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1675 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1677 while ($line = db_fetch_assoc($result)) {
1679 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1681 printf("<option $is_selected value='%d'>%s</option>",
1682 $line["id"], htmlspecialchars($line["title"]));
1691 function print_feed_cat_select($link, $id, $default_id,
1692 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1695 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1699 $parent_qpart = "parent_cat = '$root_id'";
1701 $parent_qpart = "parent_cat IS NULL";
1703 $result = db_query($link, "SELECT id,title,
1704 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1705 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1706 FROM ttrss_feed_categories
1707 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1709 while ($line = db_fetch_assoc($result)) {
1710 if ($line["id"] == $default_id) {
1711 $is_selected = "selected=\"1\"";
1716 for ($i = 0; $i < $nest_level; $i++
)
1717 $line["title"] = " - " . $line["title"];
1720 printf("<option $is_selected value='%d'>%s</option>",
1721 $line["id"], htmlspecialchars($line["title"]));
1723 if ($line["num_children"] > 0)
1724 print_feed_cat_select($link, $id, $default_id, $attributes,
1725 $include_all_cats, $line["id"], $nest_level+
1);
1729 if ($include_all_cats) {
1730 if (db_num_rows($result) > 0) {
1731 print "<option disabled=\"1\">--------</option>";
1734 if ($default_id == 0) {
1735 $is_selected = "selected=\"1\"";
1740 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1746 function checkbox_to_sql_bool($val) {
1747 return ($val == "on") ?
"true" : "false";
1750 function getFeedCatTitle($link, $id) {
1752 return __("Special");
1753 } else if ($id < LABEL_BASE_INDEX
) {
1754 return __("Labels");
1755 } else if ($id > 0) {
1756 $result = db_query($link, "SELECT ttrss_feed_categories.title
1757 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1758 cat_id = ttrss_feed_categories.id");
1759 if (db_num_rows($result) == 1) {
1760 return db_fetch_result($result, 0, "title");
1762 return __("Uncategorized");
1765 return "getFeedCatTitle($id) failed";
1770 function getFeedIcon($id) {
1773 return "images/archive.png";
1776 return "images/mark_set.svg";
1779 return "images/pub_set.svg";
1782 return "images/fresh.png";
1785 return "images/tag.png";
1788 return "images/recently_read.png";
1791 if ($id < LABEL_BASE_INDEX
) {
1792 return "images/label.png";
1794 if (file_exists(ICONS_DIR
. "/$id.ico"))
1795 return ICONS_URL
. "/$id.ico";
1801 function getFeedTitle($link, $id, $cat = false) {
1803 return getCategoryTitle($link, $id);
1804 } else if ($id == -1) {
1805 return __("Starred articles");
1806 } else if ($id == -2) {
1807 return __("Published articles");
1808 } else if ($id == -3) {
1809 return __("Fresh articles");
1810 } else if ($id == -4) {
1811 return __("All articles");
1812 } else if ($id === 0 ||
$id === "0") {
1813 return __("Archived articles");
1814 } else if ($id == -6) {
1815 return __("Recently read");
1816 } else if ($id < LABEL_BASE_INDEX
) {
1817 $label_id = feed_to_label_id($id);
1818 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1819 if (db_num_rows($result) == 1) {
1820 return db_fetch_result($result, 0, "caption");
1822 return "Unknown label ($label_id)";
1825 } else if (is_numeric($id) && $id > 0) {
1826 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1827 if (db_num_rows($result) == 1) {
1828 return db_fetch_result($result, 0, "title");
1830 return "Unknown feed ($id)";
1837 function make_init_params($link) {
1840 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1841 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1842 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1843 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1845 $params[strtolower($param)] = (int) get_pref($link, $param);
1848 $params["icons_url"] = ICONS_URL
;
1849 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1850 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1851 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1852 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1853 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1854 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1856 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1857 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1859 $max_feed_id = db_fetch_result($result, 0, "mid");
1860 $num_feeds = db_fetch_result($result, 0, "nf");
1862 $params["max_feed_id"] = (int) $max_feed_id;
1863 $params["num_feeds"] = (int) $num_feeds;
1865 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1866 $params["hotkeys"] = get_hotkeys_map($link);
1868 $params["csrf_token"] = $_SESSION["csrf_token"];
1869 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1871 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1876 function get_hotkeys_info($link) {
1878 __("Navigation") => array(
1879 "next_feed" => __("Open next feed"),
1880 "prev_feed" => __("Open previous feed"),
1881 "next_article" => __("Open next article"),
1882 "prev_article" => __("Open previous article"),
1883 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1884 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1885 "search_dialog" => __("Show search dialog")),
1886 __("Article") => array(
1887 "toggle_mark" => __("Toggle starred"),
1888 "toggle_publ" => __("Toggle published"),
1889 "toggle_unread" => __("Toggle unread"),
1890 "edit_tags" => __("Edit tags"),
1891 "dismiss_selected" => __("Dismiss selected"),
1892 "dismiss_read" => __("Dismiss read"),
1893 "open_in_new_window" => __("Open in new window"),
1894 "catchup_below" => __("Mark below as read"),
1895 "catchup_above" => __("Mark above as read"),
1896 "article_scroll_down" => __("Scroll down"),
1897 "article_scroll_up" => __("Scroll up"),
1898 "select_article_cursor" => __("Select article under cursor"),
1899 "email_article" => __("Email article"),
1900 "close_article" => __("Close/collapse article"),
1901 "toggle_widescreen" => __("Toggle widescreen mode"),
1902 "toggle_embed_original" => __("Toggle embed original")),
1903 __("Article selection") => array(
1904 "select_all" => __("Select all articles"),
1905 "select_unread" => __("Select unread"),
1906 "select_marked" => __("Select starred"),
1907 "select_published" => __("Select published"),
1908 "select_invert" => __("Invert selection"),
1909 "select_none" => __("Deselect everything")),
1910 __("Feed") => array(
1911 "feed_refresh" => __("Refresh current feed"),
1912 "feed_unhide_read" => __("Un/hide read feeds"),
1913 "feed_subscribe" => __("Subscribe to feed"),
1914 "feed_edit" => __("Edit feed"),
1915 "feed_catchup" => __("Mark as read"),
1916 "feed_reverse" => __("Reverse headlines"),
1917 "feed_debug_update" => __("Debug feed update"),
1918 "catchup_all" => __("Mark all feeds as read"),
1919 "cat_toggle_collapse" => __("Un/collapse current category"),
1920 "toggle_combined_mode" => __("Toggle combined mode"),
1921 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1922 __("Go to") => array(
1923 "goto_all" => __("All articles"),
1924 "goto_fresh" => __("Fresh"),
1925 "goto_marked" => __("Starred"),
1926 "goto_published" => __("Published"),
1927 "goto_tagcloud" => __("Tag cloud"),
1928 "goto_prefs" => __("Preferences")),
1929 __("Other") => array(
1930 "create_label" => __("Create label"),
1931 "create_filter" => __("Create filter"),
1932 "collapse_sidebar" => __("Un/collapse sidebar"),
1933 "help_dialog" => __("Show help dialog"))
1939 function get_hotkeys_map($link) {
1941 // "navigation" => array(
1944 "n" => "next_article",
1945 "p" => "prev_article",
1946 "(38)|up" => "prev_article",
1947 "(40)|down" => "next_article",
1948 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1949 // "^(40)|Ctrl-down" => "next_article_noscroll",
1950 "(191)|/" => "search_dialog",
1951 // "article" => array(
1952 "s" => "toggle_mark",
1953 "*s" => "toggle_publ",
1954 "u" => "toggle_unread",
1955 "*t" => "edit_tags",
1956 "*d" => "dismiss_selected",
1957 "*x" => "dismiss_read",
1958 "o" => "open_in_new_window",
1959 "c p" => "catchup_below",
1960 "c n" => "catchup_above",
1961 "*n" => "article_scroll_down",
1962 "*p" => "article_scroll_up",
1963 "*(38)|Shift+up" => "article_scroll_up",
1964 "*(40)|Shift+down" => "article_scroll_down",
1965 "a *w" => "toggle_widescreen",
1966 "a e" => "toggle_embed_original",
1967 "e" => "email_article",
1968 "a q" => "close_article",
1969 // "article_selection" => array(
1970 "a a" => "select_all",
1971 "a u" => "select_unread",
1972 "a *u" => "select_marked",
1973 "a p" => "select_published",
1974 "a i" => "select_invert",
1975 "a n" => "select_none",
1977 "f r" => "feed_refresh",
1978 "f a" => "feed_unhide_read",
1979 "f s" => "feed_subscribe",
1980 "f e" => "feed_edit",
1981 "f q" => "feed_catchup",
1982 "f x" => "feed_reverse",
1983 "f *d" => "feed_debug_update",
1984 "f *c" => "toggle_combined_mode",
1985 "f c" => "toggle_cdm_expanded",
1986 "*q" => "catchup_all",
1987 "x" => "cat_toggle_collapse",
1989 "g a" => "goto_all",
1990 "g f" => "goto_fresh",
1991 "g s" => "goto_marked",
1992 "g p" => "goto_published",
1993 "g t" => "goto_tagcloud",
1994 "g *p" => "goto_prefs",
1995 // "other" => array(
1996 "(9)|Tab" => "select_article_cursor", // tab
1997 "c l" => "create_label",
1998 "c f" => "create_filter",
1999 "c s" => "collapse_sidebar",
2000 "^(191)|Ctrl+/" => "help_dialog",
2003 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2004 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2005 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2009 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2010 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2013 $prefixes = array();
2015 foreach (array_keys($hotkeys) as $hotkey) {
2016 $pair = explode(" ", $hotkey, 2);
2018 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2019 array_push($prefixes, $pair[0]);
2023 return array($prefixes, $hotkeys);
2026 function make_runtime_info($link) {
2029 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2030 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2032 $max_feed_id = db_fetch_result($result, 0, "mid");
2033 $num_feeds = db_fetch_result($result, 0, "nf");
2035 $data["max_feed_id"] = (int) $max_feed_id;
2036 $data["num_feeds"] = (int) $num_feeds;
2038 $data['last_article_id'] = getLastArticleId($link);
2039 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2041 $data['dep_ts'] = calculate_dep_timestamp();
2042 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2044 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2046 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2048 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2050 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2053 $stamp_delta = time() - $stamp;
2055 if ($stamp_delta > 1800) {
2059 $_SESSION["daemon_stamp_check"] = time();
2062 $data['daemon_stamp_ok'] = $stamp_check;
2064 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2066 $data['daemon_stamp'] = $stamp_fmt;
2071 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2072 $new_version_details = @check_for_update
($link);
2074 $data['new_version_available'] = (int) ($new_version_details != false);
2076 $_SESSION["last_version_check"] = time();
2077 $_SESSION["version_data"] = $new_version_details;
2083 function search_to_sql($link, $search) {
2085 $search_query_part = "";
2087 $keywords = explode(" ", $search);
2088 $query_keywords = array();
2090 foreach ($keywords as $k) {
2091 if (strpos($k, "-") === 0) {
2098 $commandpair = explode(":", mb_strtolower($k), 2);
2100 if ($commandpair[0] == "note" && $commandpair[1]) {
2102 if ($commandpair[1] == "true")
2103 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2105 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2107 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2109 if ($commandpair[1] == "true")
2110 array_push($query_keywords, "($not (marked = true))");
2112 array_push($query_keywords, "($not (marked = false))");
2114 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2116 if ($commandpair[1] == "true")
2117 array_push($query_keywords, "($not (published = true))");
2119 array_push($query_keywords, "($not (published = false))");
2121 } else if (strpos($k, "@") === 0) {
2123 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2124 $orig_ts = strtotime(substr($k, 1));
2125 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2127 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2129 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2131 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2132 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2136 $search_query_part = implode("AND", $query_keywords);
2138 return $search_query_part;
2141 function getParentCategories($link, $cat, $owner_uid) {
2144 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2145 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2147 while ($line = db_fetch_assoc($result)) {
2148 array_push($rv, $line["parent_cat"]);
2149 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2155 function getChildCategories($link, $cat, $owner_uid) {
2158 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2159 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2161 while ($line = db_fetch_assoc($result)) {
2162 array_push($rv, $line["id"]);
2163 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2169 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) {
2171 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2173 $ext_tables_part = "";
2177 if (SPHINX_ENABLED
) {
2178 $ids = join(",", @sphinx_search
($search, 0, 500));
2181 $search_query_part = "ref_id IN ($ids) AND ";
2183 $search_query_part = "ref_id = -1 AND ";
2186 $search_query_part = search_to_sql($link, $search);
2187 $search_query_part .= " AND ";
2191 $search_query_part = "";
2196 if (DB_TYPE
== "pgsql") {
2197 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2199 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2202 $override_order = "updated DESC";
2204 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2206 // Try to check if SQL regexp implementation chokes on a valid regexp
2207 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2208 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2209 WHERE $filter_query_part LIMIT 1", false);
2212 $test = db_fetch_result($result, 0, "true_val");
2215 $filter_query_part = "false AND";
2217 $filter_query_part .= " AND";
2220 $filter_query_part = "false AND";
2224 $filter_query_part = "";
2228 $since_id_part = "ttrss_entries.id > $since_id AND ";
2230 $since_id_part = "";
2233 $view_query_part = "";
2235 if ($view_mode == "adaptive") {
2237 $view_query_part = " ";
2238 } else if ($feed != -1) {
2240 $unread = getFeedUnread($link, $feed, $cat_view);
2242 if ($cat_view && $feed > 0 && $include_children)
2243 $unread +
= getCategoryChildrenUnread($link, $feed);
2246 $view_query_part = " unread = true AND ";
2251 if ($view_mode == "marked") {
2252 $view_query_part = " marked = true AND ";
2255 if ($view_mode == "has_note") {
2256 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2259 if ($view_mode == "published") {
2260 $view_query_part = " published = true AND ";
2263 if ($view_mode == "unread" && $feed != -6) {
2264 $view_query_part = " unread = true AND ";
2268 $limit_query_part = "LIMIT " . $limit;
2271 $allow_archived = false;
2273 $vfeed_query_part = "";
2275 // override query strategy and enable feed display when searching globally
2276 if ($search && $search_mode == "all_feeds") {
2277 $query_strategy_part = "true";
2278 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2280 } else if (!is_numeric($feed)) {
2281 $query_strategy_part = "true";
2282 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2283 id = feed_id) as feed_title,";
2284 } else if ($search && $search_mode == "this_cat") {
2285 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2288 if ($include_children) {
2289 $subcats = getChildCategories($link, $feed, $owner_uid);
2290 array_push($subcats, $feed);
2291 $cats_qpart = join(",", $subcats);
2293 $cats_qpart = $feed;
2296 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2299 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2302 } else if ($feed > 0) {
2307 if ($include_children) {
2309 $subcats = getChildCategories($link, $feed, $owner_uid);
2311 array_push($subcats, $feed);
2312 $query_strategy_part = "cat_id IN (".
2313 implode(",", $subcats).")";
2316 $query_strategy_part = "cat_id = '$feed'";
2320 $query_strategy_part = "cat_id IS NULL";
2323 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2326 $query_strategy_part = "feed_id = '$feed'";
2328 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2329 $query_strategy_part = "feed_id IS NULL";
2330 $allow_archived = true;
2331 } else if ($feed == 0 && $cat_view) { // uncategorized
2332 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2333 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2334 } else if ($feed == -1) { // starred virtual feed
2335 $query_strategy_part = "marked = true";
2336 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2337 $allow_archived = true;
2339 if (!$override_order) {
2340 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2343 } else if ($feed == -2) { // published virtual feed OR labels category
2346 $query_strategy_part = "published = true";
2347 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2348 $allow_archived = true;
2350 if (!$override_order) {
2351 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2355 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2357 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2359 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2360 ttrss_user_labels2.article_id = ref_id";
2363 } else if ($feed == -6) { // recently read
2364 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2365 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2366 $allow_archived = true;
2368 if (!$override_order) $override_order = "last_read DESC";
2369 } else if ($feed == -3) { // fresh virtual feed
2370 $query_strategy_part = "unread = true AND score >= 0";
2372 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2374 if (DB_TYPE
== "pgsql") {
2375 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2377 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2380 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2381 } else if ($feed == -4) { // all articles virtual feed
2382 $query_strategy_part = "true";
2383 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2384 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2385 $label_id = feed_to_label_id($feed);
2387 $query_strategy_part = "label_id = '$label_id' AND
2388 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2389 ttrss_user_labels2.article_id = ref_id";
2391 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2392 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2393 $allow_archived = true;
2396 $query_strategy_part = "true";
2399 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2400 $date_sort_field = "updated";
2402 $date_sort_field = "date_entered";
2405 $order_by = "$date_sort_field DESC, updated DESC";
2407 if ($view_mode == "unread_first") {
2408 $order_by = "unread DESC, $order_by";
2411 if ($override_order) {
2412 $order_by = $override_order;
2418 $feed_title = T_sprintf("Search results: %s", $search);
2421 $feed_title = getCategoryTitle($link, $feed);
2423 if (is_numeric($feed) && $feed > 0) {
2424 $result = db_query($link, "SELECT title,site_url,last_error
2425 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2427 $feed_title = db_fetch_result($result, 0, "title");
2428 $feed_site_url = db_fetch_result($result, 0, "site_url");
2429 $last_error = db_fetch_result($result, 0, "last_error");
2431 $feed_title = getFeedTitle($link, $feed);
2436 $content_query_part = "content as content_preview, cached_content, ";
2438 if (is_numeric($feed)) {
2441 $feed_kind = "Feeds";
2443 $feed_kind = "Labels";
2446 if ($limit_query_part) {
2447 $offset_query_part = "OFFSET $offset";
2450 // proper override_order applied above
2451 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2452 if (!$override_order) {
2453 $order_by = "ttrss_feeds.title, $order_by";
2455 $order_by = "ttrss_feeds.title, $override_order";
2459 if (!$allow_archived) {
2460 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2461 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2464 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2465 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2468 $query = "SELECT DISTINCT
2471 ttrss_entries.id,ttrss_entries.title,
2475 always_display_enclosures,
2482 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2483 last_marked, last_published,
2491 ttrss_user_entries.ref_id = ttrss_entries.id AND
2492 ttrss_user_entries.owner_uid = '$owner_uid' AND
2497 $query_strategy_part ORDER BY $order_by
2498 $limit_query_part $offset_query_part";
2500 if ($_REQUEST["debug"]) print $query;
2502 $result = db_query($link, $query);
2507 $select_qpart = "SELECT DISTINCT " .
2511 "ttrss_entries.id as id," .
2524 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2525 "last_marked, last_published, " .
2528 $content_query_part .
2531 $feed_kind = "Tags";
2532 $all_tags = explode(",", $feed);
2533 if ($search_mode == 'any') {
2534 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2535 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2536 $where_qpart = " WHERE " .
2537 "ref_id = ttrss_entries.id AND " .
2538 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2539 "post_int_id = int_id AND $tag_sql AND " .
2541 $search_query_part .
2542 $query_strategy_part . " ORDER BY $order_by " .
2547 $sub_selects = array();
2548 $sub_ands = array();
2549 foreach ($all_tags as $term) {
2550 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");
2557 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2562 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2563 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2564 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2565 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2567 // error_log("TAG SQL: " . $tag_sql);
2568 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2570 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2571 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2574 return array($result, $feed_title, $feed_site_url, $last_error);
2578 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2579 if (!$owner) $owner = $_SESSION["uid"];
2581 $res = trim($str); if (!$res) return '';
2583 if (strpos($res, "href=") === false)
2584 $res = rewrite_urls($res);
2586 $charset_hack = '<head>
2587 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2590 $res = trim($res); if (!$res) return '';
2592 libxml_use_internal_errors(true);
2594 $doc = new DOMDocument();
2595 $doc->loadHTML($charset_hack . $res);
2596 $xpath = new DOMXPath($doc);
2598 $entries = $xpath->query('(//a[@href]|//img[@src])');
2600 foreach ($entries as $entry) {
2604 if ($entry->hasAttribute('href'))
2605 $entry->setAttribute('href',
2606 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2608 if ($entry->hasAttribute('src')) {
2609 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2611 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2613 if (file_exists($cached_filename)) {
2614 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2617 $entry->setAttribute('src', $src);
2620 if ($entry->nodeName
== 'img') {
2621 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2622 $force_remove_images ||
$_SESSION["bw_limit"]) {
2624 $p = $doc->createElement('p');
2626 $a = $doc->createElement('a');
2627 $a->setAttribute('href', $entry->getAttribute('src'));
2629 $a->appendChild(new DOMText($entry->getAttribute('src')));
2630 $a->setAttribute('target', '_blank');
2632 $p->appendChild($a);
2634 $entry->parentNode
->replaceChild($p, $entry);
2639 if (strtolower($entry->nodeName
) == "a") {
2640 $entry->setAttribute("target", "_blank");
2644 $entries = $xpath->query('//iframe');
2645 foreach ($entries as $entry) {
2646 $entry->setAttribute('sandbox', 'allow-scripts');
2650 $allowed_elements = array('a', 'address', 'audio', 'article',
2651 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2652 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2653 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2654 'header', 'html', 'i', 'img', 'ins', 'kbd',
2655 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2656 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2657 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2658 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2660 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2662 $disallowed_attributes = array('id', 'style', 'class');
2666 if (isset($pluginhost)) {
2667 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2668 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2669 if (is_array($retval)) {
2671 $allowed_elements = $retval[1];
2672 $disallowed_attributes = $retval[2];
2679 $doc->removeChild($doc->firstChild
); //remove doctype
2680 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2681 $res = $doc->saveHTML();
2685 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2686 $entries = $doc->getElementsByTagName("*");
2688 foreach ($entries as $entry) {
2689 if (!in_array($entry->nodeName
, $allowed_elements)) {
2690 $entry->parentNode
->removeChild($entry);
2693 if ($entry->hasAttributes()) {
2694 $attrs_to_remove = array();
2696 foreach ($entry->attributes
as $attr) {
2698 if (strpos($attr->nodeName
, 'on') === 0) {
2699 array_push($attrs_to_remove, $attr);
2702 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2703 array_push($attrs_to_remove, $attr);
2707 foreach ($attrs_to_remove as $attr) {
2708 $entry->removeAttributeNode($attr);
2716 function check_for_update($link) {
2717 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2718 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2719 "&iid=" . sha1(SELF_URL_PATH
);
2721 $version_data = @fetch_file_contents
($version_url);
2723 if ($version_data) {
2724 $version_data = json_decode($version_data, true);
2725 if ($version_data && $version_data['version']) {
2727 if (version_compare(VERSION
, $version_data['version']) == -1) {
2728 return $version_data;
2736 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2738 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2739 if (count($ids) == 0) return;
2743 foreach ($ids as $id) {
2744 array_push($tmp_ids, "ref_id = '$id'");
2747 $ids_qpart = join(" OR ", $tmp_ids);
2750 db_query($link, "UPDATE ttrss_user_entries SET
2751 unread = false,last_read = NOW()
2752 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2753 } else if ($cmode == 1) {
2754 db_query($link, "UPDATE ttrss_user_entries SET
2756 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2758 db_query($link, "UPDATE ttrss_user_entries SET
2759 unread = NOT unread,last_read = NOW()
2760 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2765 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2766 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2768 while ($line = db_fetch_assoc($result)) {
2769 ccache_update($link, $line["feed_id"], $owner_uid);
2773 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2775 $a_id = db_escape_string($link, $id);
2777 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2779 $query = "SELECT DISTINCT tag_name,
2780 owner_uid as owner FROM
2781 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2782 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2784 $obj_id = md5("TAGS:$owner_uid:$id");
2787 /* check cache first */
2789 if ($tag_cache === false) {
2790 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2791 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2793 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2797 $tags = explode(",", $tag_cache);
2800 /* do it the hard way */
2802 $tmp_result = db_query($link, $query);
2804 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2805 array_push($tags, $tmp_line["tag_name"]);
2808 /* update the cache */
2810 $tags_str = db_escape_string($link, join(",", $tags));
2812 db_query($link, "UPDATE ttrss_user_entries
2813 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2814 AND owner_uid = $owner_uid");
2820 function trim_array($array) {
2822 array_walk($tmp, 'trim');
2826 function tag_is_valid($tag) {
2827 if ($tag == '') return false;
2828 if (preg_match("/^[0-9]*$/", $tag)) return false;
2829 if (mb_strlen($tag) > 250) return false;
2831 if (function_exists('iconv')) {
2832 $tag = iconv("utf-8", "utf-8", $tag);
2835 if (!$tag) return false;
2840 function render_login_form($link) {
2841 require_once "login_form.php";
2845 // from http://developer.apple.com/internet/safari/faq.html
2846 function no_cache_incantation() {
2847 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2848 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2849 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2850 header("Cache-Control: post-check=0, pre-check=0", false);
2851 header("Pragma: no-cache"); // HTTP/1.0
2854 function format_warning($msg, $id = "") {
2856 return "<div class=\"warning\" id=\"$id\">
2857 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2860 function format_notice($msg, $id = "") {
2862 return "<div class=\"notice\" id=\"$id\">
2863 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2866 function format_error($msg, $id = "") {
2868 return "<div class=\"error\" id=\"$id\">
2869 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2872 function print_notice($msg) {
2873 return print format_notice($msg);
2876 function print_warning($msg) {
2877 return print format_warning($msg);
2880 function print_error($msg) {
2881 return print format_error($msg);
2885 function T_sprintf() {
2886 $args = func_get_args();
2887 return vsprintf(__(array_shift($args)), $args);
2890 function format_inline_player($link, $url, $ctype) {
2894 $url = htmlspecialchars($url);
2896 if (strpos($ctype, "audio/") === 0) {
2898 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2899 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2900 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2902 $id = 'AUDIO-' . uniqid();
2904 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2905 <source type=\"$ctype\" src=\"$url\"></source>
2908 $entry .= "<span onclick=\"player(this)\"
2909 title=\"".__("Click to play")."\" status=\"0\"
2910 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2914 $entry .= "<object type=\"application/x-shockwave-flash\"
2915 data=\"lib/button/musicplayer.swf?song_url=$url\"
2916 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2917 <param name=\"movie\"
2918 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2922 if ($entry) $entry .= " <a target=\"_blank\"
2923 href=\"$url\">" . basename($url) . "</a>";
2931 /* $filename = substr($url, strrpos($url, "/")+1);
2933 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2934 $filename . " (" . $ctype . ")" . "</a>"; */
2938 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2939 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2945 /* we can figure out feed_id from article id anyway, why do we
2946 * pass feed_id here? let's ignore the argument :( */
2948 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2949 WHERE ref_id = '$id'");
2951 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2953 $rv['feed_id'] = $feed_id;
2955 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2957 if ($mark_as_read) {
2958 $result = db_query($link, "UPDATE ttrss_user_entries
2959 SET unread = false,last_read = NOW()
2960 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2962 ccache_update($link, $feed_id, $owner_uid);
2965 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2966 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2967 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2968 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2975 FROM ttrss_entries,ttrss_user_entries
2976 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2980 $line = db_fetch_assoc($result);
2982 $tag_cache = $line["tag_cache"];
2984 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
2985 unset($line["tag_cache"]);
2987 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
2991 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
2992 $line = $p->hook_render_article($line);
2995 $num_comments = $line["num_comments"];
2996 $entry_comments = "";
2998 if ($num_comments > 0) {
2999 if ($line["comments"]) {
3000 $comments_url = htmlspecialchars($line["comments"]);
3002 $comments_url = htmlspecialchars($line["link"]);
3004 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3006 if ($line["comments"] && $line["link"] != $line["comments"]) {
3007 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3012 header("Content-Type: text/html");
3013 $rv['content'] .= "<html><head>
3014 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3015 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3016 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3017 </head><body id=\"ttrssZoom\">";
3020 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3022 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3024 $entry_author = $line["author"];
3026 if ($entry_author) {
3027 $entry_author = __(" - ") . $entry_author;
3030 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3033 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3035 if ($line["link"]) {
3036 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3037 title=\"".htmlspecialchars($line['title'])."\"
3039 htmlspecialchars($line["link"]) . "\">" .
3040 $line["title"] . "</a>" .
3041 "<span class='author'>$entry_author</span></div>";
3043 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3046 $tags_str = format_tags_string($line["tags"], $id);
3047 $tags_str_full = join(", ", $line["tags"]);
3049 if (!$tags_str_full) $tags_str_full = __("no tags");
3051 if (!$entry_comments) $entry_comments = " "; # placeholder
3053 $rv['content'] .= "<div class='postTags' style='float : right'>
3054 <img src='images/tag.png'
3055 class='tagsPic' alt='Tags' title='Tags'> ";
3058 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3059 <a title=\"".__('Edit tags for this article')."\"
3060 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3062 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3063 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3064 position=\"below\">$tags_str_full</div>";
3068 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3069 $rv['content'] .= $p->hook_article_button($line);
3074 $tags_str = strip_tags($tags_str);
3075 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3077 $rv['content'] .= "</div>";
3078 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3080 if ($line["orig_feed_id"]) {
3082 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3083 WHERE id = ".$line["orig_feed_id"]);
3085 if (db_num_rows($tmp_result) != 0) {
3087 $rv['content'] .= "<div clear='both'>";
3088 $rv['content'] .= __("Originally from:");
3090 $rv['content'] .= " ";
3092 $tmp_line = db_fetch_assoc($tmp_result);
3094 $rv['content'] .= "<a target='_blank'
3095 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3096 $tmp_line['title'] . "</a>";
3098 $rv['content'] .= " ";
3100 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3101 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3103 $rv['content'] .= "</div>";
3107 $rv['content'] .= "</div>";
3109 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3110 if ($line['note']) {
3111 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3113 $rv['content'] .= "</div>";
3115 $rv['content'] .= "<div class=\"postContent\">";
3117 $rv['content'] .= $line["content"];
3119 $rv['content'] .= format_article_enclosures($link, $id,
3120 $always_display_enclosures, $line["content"], $line["hide_images"]);
3122 $rv['content'] .= "</div>";
3124 $rv['content'] .= "</div>";
3130 <div class='footer'>
3131 <button onclick=\"return window.close()\">".
3132 __("Close this window")."</button></div>";
3133 $rv['content'] .= "</body></html>";
3140 function print_checkpoint($n, $s) {
3141 $ts = microtime(true);
3142 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3146 function sanitize_tag($tag) {
3149 $tag = mb_strtolower($tag, 'utf-8');
3151 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3153 // $tag = str_replace('"', "", $tag);
3154 // $tag = str_replace("+", " ", $tag);
3155 $tag = str_replace("technorati tag: ", "", $tag);
3160 function get_self_url_prefix() {
3161 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3162 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3164 return SELF_URL_PATH
;
3169 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3171 * @return string The Mozilla Firefox feed adding URL.
3173 function add_feed_url() {
3174 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3176 $url_path = get_self_url_prefix() .
3177 "/public.php?op=subscribe&feed_url=%s";
3179 } // function add_feed_url
3181 function encrypt_password($pass, $salt = '', $mode2 = false) {
3182 if ($salt && $mode2) {
3183 return "MODE2:" . hash('sha256', $salt . $pass);
3185 return "SHA1X:" . sha1("$salt:$pass");
3187 return "SHA1:" . sha1($pass);
3189 } // function encrypt_password
3191 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3194 $cat_id = (int)getFeedCategory($link, $feed_id);
3196 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3197 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3199 $check_cats = join(",", array_merge(
3200 getParentCategories($link, $cat_id, $owner_uid),
3203 while ($line = db_fetch_assoc($result)) {
3204 $filter_id = $line["id"];
3206 $result2 = db_query($link, "SELECT
3207 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3208 FROM ttrss_filters2_rules AS r,
3209 ttrss_filter_types AS t
3211 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3212 (feed_id IS NULL OR feed_id = '$feed_id') AND
3213 filter_type = t.id AND filter_id = '$filter_id'");
3218 while ($rule_line = db_fetch_assoc($result2)) {
3219 # print_r($rule_line);
3222 $rule["reg_exp"] = $rule_line["reg_exp"];
3223 $rule["type"] = $rule_line["type_name"];
3224 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3226 array_push($rules, $rule);
3229 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3230 FROM ttrss_filters2_actions AS a,
3231 ttrss_filter_actions AS t
3233 action_id = t.id AND filter_id = '$filter_id'");
3235 while ($action_line = db_fetch_assoc($result2)) {
3236 # print_r($action_line);
3239 $action["type"] = $action_line["type_name"];
3240 $action["param"] = $action_line["action_param"];
3242 array_push($actions, $action);
3247 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3248 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3249 $filter["rules"] = $rules;
3250 $filter["actions"] = $actions;
3252 if (count($rules) > 0 && count($actions) > 0) {
3253 array_push($filters, $filter);
3260 function get_score_pic($score) {
3262 return "score_high.png";
3263 } else if ($score > 0) {
3264 return "score_half_high.png";
3265 } else if ($score < -100) {
3266 return "score_low.png";
3267 } else if ($score < 0) {
3268 return "score_half_low.png";
3270 return "score_neutral.png";
3274 function feed_has_icon($id) {
3275 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3278 function init_connection($link) {
3281 if (DB_TYPE
== "pgsql") {
3282 pg_query($link, "set client_encoding = 'UTF-8'");
3283 pg_set_client_encoding("UNICODE");
3284 pg_query($link, "set datestyle = 'ISO, european'");
3285 pg_query($link, "set TIME ZONE 0");
3287 db_query($link, "SET time_zone = '+0:0'");
3289 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3290 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3296 $pluginhost = new PluginHost($link);
3297 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3301 print "Unable to connect to database:" . db_last_error();
3306 function format_tags_string($tags, $id) {
3309 $tags_nolinks_str = "";
3315 $formatted_tags = array();
3317 foreach ($tags as $tag) {
3319 $tag_escaped = str_replace("'", "\\'", $tag);
3321 if (mb_strlen($tag) > 30) {
3322 $tag = truncate_string($tag, 30);
3325 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3327 array_push($formatted_tags, $tag_str);
3329 $tmp_tags_str = implode(", ", $formatted_tags);
3331 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3336 $tags_str = implode(", ", $formatted_tags);
3338 if ($num_tags < count($tags)) {
3339 $tags_str .= ", …";
3342 if ($num_tags == 0) {
3343 $tags_str = __("no tags");
3350 function format_article_labels($labels, $id) {
3354 foreach ($labels as $l) {
3355 $labels_str .= sprintf("<span class='hlLabelRef'
3356 style='color : %s; background-color : %s'>%s</span>",
3357 $l[2], $l[3], $l[1]);
3364 function format_article_note($id, $note, $allow_edit = true) {
3366 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3367 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3368 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3374 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3375 if ($parent_cat_id) {
3376 $parent_qpart = "parent_cat = '$parent_cat_id'";
3377 $parent_insert = "'$parent_cat_id'";
3379 $parent_qpart = "parent_cat IS NULL";
3380 $parent_insert = "NULL";
3383 $result = db_query($link,
3384 "SELECT id FROM ttrss_feed_categories
3385 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3387 if (db_num_rows($result) == 0) {
3390 return db_fetch_result($result, 0, "id");
3394 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3396 if (!$feed_cat) return false;
3398 db_query($link, "BEGIN");
3400 if ($parent_cat_id) {
3401 $parent_qpart = "parent_cat = '$parent_cat_id'";
3402 $parent_insert = "'$parent_cat_id'";
3404 $parent_qpart = "parent_cat IS NULL";
3405 $parent_insert = "NULL";
3408 $result = db_query($link,
3409 "SELECT id FROM ttrss_feed_categories
3410 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3412 if (db_num_rows($result) == 0) {
3414 $result = db_query($link,
3415 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3416 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3418 db_query($link, "COMMIT");
3426 function getArticleFeed($link, $id) {
3427 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3428 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3430 if (db_num_rows($result) != 0) {
3431 return db_fetch_result($result, 0, "feed_id");
3438 * Fixes incomplete URLs by prepending "http://".
3439 * Also replaces feed:// with http://, and
3440 * prepends a trailing slash if the url is a domain name only.
3442 * @param string $url Possibly incomplete URL
3444 * @return string Fixed URL.
3446 function fix_url($url) {
3447 if (strpos($url, '://') === false) {
3448 $url = 'http://' . $url;
3449 } else if (substr($url, 0, 5) == 'feed:') {
3450 $url = 'http:' . substr($url, 5);
3453 //prepend slash if the URL has no slash in it
3454 // "http://www.example" -> "http://www.example/"
3455 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3459 if ($url != "http:///")
3465 function validate_feed_url($url) {
3466 $parts = parse_url($url);
3468 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3472 function get_article_enclosures($link, $id) {
3474 $query = "SELECT * FROM ttrss_enclosures
3475 WHERE post_id = '$id' AND content_url != ''";
3479 $result = db_query($link, $query);
3481 if (db_num_rows($result) > 0) {
3482 while ($line = db_fetch_assoc($result)) {
3483 array_push($rv, $line);
3490 function save_email_address($link, $email) {
3491 // FIXME: implement persistent storage of emails
3493 if (!$_SESSION['stored_emails'])
3494 $_SESSION['stored_emails'] = array();
3496 if (!in_array($email, $_SESSION['stored_emails']))
3497 array_push($_SESSION['stored_emails'], $email);
3501 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3503 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3505 $sql_is_cat = bool_to_sql_bool($is_cat);
3507 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3508 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3509 AND owner_uid = " . $owner_uid);
3511 if (db_num_rows($result) == 1) {
3512 return db_fetch_result($result, 0, "access_key");
3514 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3516 $result = db_query($link, "INSERT INTO ttrss_access_keys
3517 (access_key, feed_id, is_cat, owner_uid)
3518 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3525 function get_feeds_from_html($url, $content)
3527 $url = fix_url($url);
3528 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3530 libxml_use_internal_errors(true);
3532 $doc = new DOMDocument();
3533 $doc->loadHTML($content);
3534 $xpath = new DOMXPath($doc);
3535 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3536 $feedUrls = array();
3537 foreach ($entries as $entry) {
3538 if ($entry->hasAttribute('href')) {
3539 $title = $entry->getAttribute('title');
3541 $title = $entry->getAttribute('type');
3543 $feedUrl = rewrite_relative_url(
3544 $baseUrl, $entry->getAttribute('href')
3546 $feedUrls[$feedUrl] = $title;
3552 function is_html($content) {
3553 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3556 function url_is_html($url, $login = false, $pass = false) {
3557 return is_html(fetch_file_contents($url, false, $login, $pass));
3560 function print_label_select($link, $name, $value, $attributes = "") {
3562 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3563 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3565 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3566 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3568 while ($line = db_fetch_assoc($result)) {
3570 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3572 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3573 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3577 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3584 function format_article_enclosures($link, $id, $always_display_enclosures,
3585 $article_content, $hide_images = false) {
3587 $result = get_article_enclosures($link, $id);
3590 if (count($result) > 0) {
3592 $entries_html = array();
3594 $entries_inline = array();
3596 foreach ($result as $line) {
3598 $url = $line["content_url"];
3599 $ctype = $line["content_type"];
3601 if (!$ctype) $ctype = __("unknown type");
3603 $filename = substr($url, strrpos($url, "/")+
1);
3605 $player = format_inline_player($link, $url, $ctype);
3607 if ($player) array_push($entries_inline, $player);
3609 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3610 # $filename . " (" . $ctype . ")" . "</a>";
3612 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3613 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3615 array_push($entries_html, $entry);
3619 $entry["type"] = $ctype;
3620 $entry["filename"] = $filename;
3621 $entry["url"] = $url;
3623 array_push($entries, $entry);
3626 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3627 if ($always_display_enclosures ||
3628 !preg_match("/<img/i", $article_content)) {
3630 foreach ($entries as $entry) {
3632 if (preg_match("/image/", $entry["type"]) ||
3633 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3635 if (!$hide_images) {
3637 alt=\"".htmlspecialchars($entry["filename"])."\"
3638 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3640 $rv .= "<p><a target=\"_blank\"
3641 href=\"".htmlspecialchars($entry["url"])."\"
3642 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3650 if (count($entries_inline) > 0) {
3651 $rv .= "<hr clear='both'/>";
3652 foreach ($entries_inline as $entry) { $rv .= $entry; };
3653 $rv .= "<hr clear='both'/>";
3656 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3657 "<option value=''>" . __('Attachments')."</option>";
3659 foreach ($entries as $entry) {
3660 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3670 function getLastArticleId($link) {
3671 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3672 WHERE owner_uid = " . $_SESSION["uid"]);
3674 if (db_num_rows($result) == 1) {
3675 return db_fetch_result($result, 0, "id");
3681 function build_url($parts) {
3682 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3686 * Converts a (possibly) relative URL to a absolute one.
3688 * @param string $url Base URL (i.e. from where the document is)
3689 * @param string $rel_url Possibly relative URL in the document
3691 * @return string Absolute URL
3693 function rewrite_relative_url($url, $rel_url) {
3694 if (strpos($rel_url, "magnet:") === 0) {
3696 } else if (strpos($rel_url, "://") !== false) {
3698 } else if (strpos($rel_url, "//") === 0) {
3699 # protocol-relative URL (rare but they exist)
3701 } else if (strpos($rel_url, "/") === 0)
3703 $parts = parse_url($url);
3704 $parts['path'] = $rel_url;
3706 return build_url($parts);
3709 $parts = parse_url($url);
3710 if (!isset($parts['path'])) {
3711 $parts['path'] = '/';
3713 $dir = $parts['path'];
3714 if (substr($dir, -1) !== '/') {
3715 $dir = dirname($parts['path']);
3716 $dir !== '/' && $dir .= '/';
3718 $parts['path'] = $dir . $rel_url;
3720 return build_url($parts);
3724 function sphinx_search($query, $offset = 0, $limit = 30) {
3725 require_once 'lib/sphinxapi.php';
3727 $sphinxClient = new SphinxClient();
3729 $sphinxClient->SetServer('localhost', 9312);
3730 $sphinxClient->SetConnectTimeout(1);
3732 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3733 'feed_title' => 20));
3735 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3736 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3737 $sphinxClient->SetLimits($offset, $limit, 1000);
3738 $sphinxClient->SetArrayResult(false);
3739 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3741 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3745 if (is_array($result['matches'])) {
3746 foreach (array_keys($result['matches']) as $int_id) {
3747 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3748 array_push($ids, $ref_id);
3755 function cleanup_tags($link, $days = 14, $limit = 1000) {
3757 if (DB_TYPE
== "pgsql") {
3758 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3759 } else if (DB_TYPE
== "mysql") {
3760 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3765 while ($limit > 0) {
3768 $query = "SELECT ttrss_tags.id AS id
3769 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3770 WHERE post_int_id = int_id AND $interval_query AND
3771 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3773 $result = db_query($link, $query);
3777 while ($line = db_fetch_assoc($result)) {
3778 array_push($ids, $line['id']);
3781 if (count($ids) > 0) {
3782 $ids = join(",", $ids);
3784 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3785 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3790 $limit -= $limit_part;
3793 return $tags_deleted;
3796 function print_user_stylesheet($link) {
3797 $value = get_pref($link, 'USER_STYLESHEET');
3800 print "<style type=\"text/css\">";
3801 print str_replace("<br/>", "\n", $value);
3807 function rewrite_urls($html) {
3808 libxml_use_internal_errors(true);
3810 $charset_hack = '<head>
3811 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3814 $doc = new DOMDocument();
3815 $doc->loadHTML($charset_hack . $html);
3816 $xpath = new DOMXPath($doc);
3818 $entries = $xpath->query('//*/text()');
3820 foreach ($entries as $entry) {
3821 if (strstr($entry->wholeText
, "://") !== false) {
3822 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3823 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3825 if ($text != $entry->wholeText
) {
3826 $cdoc = new DOMDocument();
3827 $cdoc->loadHTML($charset_hack . $text);
3830 foreach ($cdoc->childNodes
as $cnode) {
3831 $cnode = $doc->importNode($cnode, true);
3834 $entry->parentNode
->insertBefore($cnode);
3838 $entry->parentNode
->removeChild($entry);
3844 $node = $doc->getElementsByTagName('body')->item(0);
3846 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3848 return $doc->saveXML($node);
3853 function filter_to_sql($link, $filter, $owner_uid) {
3856 if (DB_TYPE
== "pgsql")
3859 $reg_qpart = "REGEXP";
3861 foreach ($filter["rules"] AS $rule) {
3862 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3863 $rule['reg_exp']) !== FALSE;
3865 if ($regexp_valid) {
3867 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3869 switch ($rule["type"]) {
3871 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3872 $rule['reg_exp'] . "')";
3875 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3876 $rule['reg_exp'] . "')";
3879 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3880 $rule['reg_exp'] . "') OR LOWER(" .
3881 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3884 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3885 $rule['reg_exp'] . "')";
3888 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3889 $rule['reg_exp'] . "')";
3892 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3893 $rule['reg_exp'] . "')";
3897 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3899 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3900 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3903 if (isset($rule["cat_id"])) {
3905 if ($rule["cat_id"] > 0) {
3906 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3907 array_push($children, $rule["cat_id"]);
3909 $children = join(",", $children);
3911 $cat_qpart = "cat_id IN ($children)";
3913 $cat_qpart = "cat_id IS NULL";
3916 $qpart .= " AND $cat_qpart";
3919 array_push($query, "($qpart)");
3924 if (count($query) > 0) {
3925 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3927 $fullquery = "(false)";
3930 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3935 if (!function_exists('gzdecode')) {
3936 function gzdecode($string) { // no support for 2nd argument
3937 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3938 base64_encode($string));
3942 function get_random_bytes($length) {
3943 if (function_exists('openssl_random_pseudo_bytes')) {
3944 return openssl_random_pseudo_bytes($length);
3948 for ($i = 0; $i < $length; $i++
)
3949 $output .= chr(mt_rand(0, 255));
3955 function read_stdin() {
3956 $fp = fopen("php://stdin", "r");
3959 $line = trim(fgets($fp));
3967 function tmpdirname($path, $prefix) {
3968 // Use PHP's tmpfile function to create a temporary
3969 // directory name. Delete the file and keep the name.
3970 $tempname = tempnam($path,$prefix);
3974 if (!unlink($tempname))
3980 function getFeedCategory($link, $feed) {
3981 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3982 WHERE id = '$feed'");
3984 if (db_num_rows($result) > 0) {
3985 return db_fetch_result($result, 0, "cat_id");
3992 function implements_interface($class, $interface) {
3993 return in_array($interface, class_implements($class));
3996 function geturl($url){
3998 (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');
4000 $curl = curl_init();
4001 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4002 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4003 $header[] = "Cache-Control: max-age=0";
4004 $header[] = "Connection: keep-alive";
4005 $header[] = "Keep-Alive: 300";
4006 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4007 $header[] = "Accept-Language: en-us,en;q=0.5";
4008 $header[] = "Pragma: ";
4010 curl_setopt($curl, CURLOPT_URL
, $url);
4011 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4012 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4013 curl_setopt($curl, CURLOPT_HEADER
, true);
4014 curl_setopt($curl, CURLOPT_REFERER
, $url);
4015 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4016 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4017 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4018 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4019 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4021 $html = curl_exec($curl);
4023 $status = curl_getinfo($curl);
4026 if($status['http_code']!=200){
4027 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4028 list($header) = explode("\r\n\r\n", $html, 2);
4030 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4031 $url = trim(str_replace($matches[1],"",$matches[0]));
4032 $url_parsed = parse_url($url);
4033 return (isset($url_parsed))?
geturl($url, $referer):'';
4036 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4037 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4038 # $handle = @fopen('./curl.error.log', 'a');
4039 # fwrite($handle, $line);
4045 function get_minified_js($files) {
4046 require_once 'lib/jshrink/Minifier.php';
4050 foreach ($files as $js) {
4051 if (!isset($_GET['debug'])) {
4052 $cached_file = CACHE_DIR
. "/js/$js.js";
4054 if (file_exists($cached_file) &&
4055 is_readable($cached_file) &&
4056 filemtime($cached_file) >= filemtime("js/$js.js")) {
4058 $rv .= file_get_contents($cached_file);
4061 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4062 file_put_contents($cached_file, $minified);
4066 $rv .= file_get_contents("js/$js.js");
4073 function stylesheet_tag($filename) {
4074 $timestamp = filemtime($filename);
4076 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4079 function javascript_tag($filename) {
4082 if (!(strpos($filename, "?") === FALSE)) {
4083 $query = substr($filename, strpos($filename, "?")+
1);
4084 $filename = substr($filename, 0, strpos($filename, "?"));
4087 $timestamp = filemtime($filename);
4089 if ($query) $timestamp .= "&$query";
4091 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4094 function calculate_dep_timestamp() {
4095 $files = array_merge(glob("js/*.js"), glob("*.css"));
4099 foreach ($files as $file) {
4100 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4106 function T_js_decl($s1, $s2) {
4108 $s1 = preg_replace("/\n/", "", $s1);
4109 $s2 = preg_replace("/\n/", "", $s2);
4111 $s1 = preg_replace("/\"/", "\\\"", $s1);
4112 $s2 = preg_replace("/\"/", "\\\"", $s2);
4114 return "T_messages[\"$s1\"] = \"$s2\";\n";
4118 function init_js_translations() {
4120 print 'var T_messages = new Object();
4123 if (T_messages[msg]) {
4124 return T_messages[msg];
4130 function ngettext(msg1, msg2, n) {
4131 return (parseInt(n) > 1) ? msg2 : msg1;
4134 $l10n = _get_reader();
4136 for ($i = 0; $i < $l10n->total
; $i++
) {
4137 $orig = $l10n->get_original_string($i);
4138 $translation = __($orig);
4140 print T_js_decl($orig, $translation);
4144 function label_to_feed_id($label) {
4145 return LABEL_BASE_INDEX
- 1 - abs($label);
4148 function feed_to_label_id($feed) {
4149 return LABEL_BASE_INDEX
- 1 +
abs($feed);