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",
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["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
626 $_SESSION["last_version_check"] = time();
628 initialize_user_prefs($link, $_SESSION["uid"]);
637 $_SESSION["uid"] = 1;
638 $_SESSION["name"] = "admin";
639 $_SESSION["access_level"] = 10;
641 $_SESSION["hide_hello"] = true;
642 $_SESSION["hide_logout"] = true;
644 $_SESSION["auth_module"] = false;
646 if (!$_SESSION["csrf_token"]) {
647 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
650 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
652 initialize_user_prefs($link, $_SESSION["uid"]);
658 function make_password($length = 8) {
661 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
665 while ($i < $length) {
666 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
668 if (!strstr($password, $char)) {
676 // this is called after user is created to initialize default feeds, labels
679 // user preferences are checked on every login, not here
681 function initialize_user($link, $uid) {
683 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
684 values ('$uid', 'Tiny Tiny RSS: New Releases',
685 'http://tt-rss.org/releases.rss')");
687 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
688 values ('$uid', 'Tiny Tiny RSS: Forum',
689 'http://tt-rss.org/forum/rss.php')");
692 function logout_user() {
694 if (isset($_COOKIE[session_name()])) {
695 setcookie(session_name(), '', time()-42000, '/');
699 function validate_csrf($csrf_token) {
700 return $csrf_token == $_SESSION['csrf_token'];
703 function load_user_plugins($link, $owner_uid) {
705 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
708 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
710 if (get_schema_version($link) > 100) {
711 $pluginhost->load_data();
716 function login_sequence($link) {
717 $_SESSION["prefs_cache"] = false;
719 if (SINGLE_USER_MODE
) {
721 authenticate_user($link, "admin", null);
723 load_user_plugins($link, $_SESSION["uid"]);
725 if (!$_SESSION["uid"] ||
!validate_session($link)) {
727 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
728 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
730 authenticate_user($link, null, null, true);
733 if (!$_SESSION["uid"]) render_login_form($link);
736 /* bump login timestamp */
737 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
739 $_SESSION["last_login_update"] = time();
742 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
743 setcookie("ttrss_lang", $_SESSION["language"],
744 time() + SESSION_COOKIE_LIFETIME
);
747 if ($_SESSION["uid"]) {
749 load_user_plugins($link, $_SESSION["uid"]);
753 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
754 $_SESSION["uid"] . " AND
755 (SELECT COUNT(id) FROM ttrss_feeds WHERE
756 ttrss_feeds.id = feed_id) = 0");
758 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
759 $_SESSION["uid"] . " AND
760 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
761 ttrss_feed_categories.id = feed_id) = 0");
768 function truncate_string($str, $max_len, $suffix = '…') {
769 if (mb_strlen($str, "utf-8") > $max_len - 3) {
770 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
776 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
779 $source_tz = new DateTimeZone($source_tz);
780 } catch (Exception
$e) {
781 $source_tz = new DateTimeZone('UTC');
785 $dest_tz = new DateTimeZone($dest_tz);
786 } catch (Exception
$e) {
787 $dest_tz = new DateTimeZone('UTC');
790 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
791 return $dt->format('U') +
$dest_tz->getOffset($dt);
794 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
795 $no_smart_dt = false) {
797 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
798 if (!$timestamp) $timestamp = '1970-01-01 0:00';
803 # We store date in UTC internally
804 $dt = new DateTime($timestamp, $utc_tz);
806 if ($tz_offset == -1) {
808 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
811 $user_tz = new DateTimeZone($user_tz_string);
812 } catch (Exception
$e) {
816 $tz_offset = $user_tz->getOffset($dt);
819 $user_timestamp = $dt->format('U') +
$tz_offset;
822 return smart_date_time($link, $user_timestamp,
823 $tz_offset, $owner_uid);
826 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
828 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
830 return date($format, $user_timestamp);
834 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
835 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
837 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
838 return date("G:i", $timestamp);
839 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
840 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
841 return date($format, $timestamp);
843 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
844 return date($format, $timestamp);
848 function sql_bool_to_bool($s) {
849 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
856 function bool_to_sql_bool($s) {
864 // Session caching removed due to causing wrong redirects to upgrade
865 // script when get_schema_version() is called on an obsolete session
866 // created on a previous schema version.
867 function get_schema_version($link, $nocache = false) {
868 global $schema_version;
870 if (!$schema_version) {
871 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
872 $version = db_fetch_result($result, 0, "schema_version");
873 $schema_version = $version;
876 return $schema_version;
880 function sanity_check($link) {
881 require_once 'errors.php';
884 $schema_version = get_schema_version($link, true);
886 if ($schema_version != SCHEMA_VERSION
) {
890 if (DB_TYPE
== "mysql") {
891 $result = db_query($link, "SELECT true", false);
892 if (db_num_rows($result) != 1) {
897 if (db_escape_string($link, "testTEST") != "testTEST") {
901 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
904 function file_is_locked($filename) {
905 if (function_exists('flock')) {
906 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
908 if (flock($fp, LOCK_EX | LOCK_NB
)) {
919 return true; // consider the file always locked and skip the test
922 function make_lockfile($filename) {
923 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
925 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
926 if (function_exists('posix_getpid')) {
927 fwrite($fp, posix_getpid() . "\n");
935 function make_stampfile($filename) {
936 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
938 if (flock($fp, LOCK_EX | LOCK_NB
)) {
939 fwrite($fp, time() . "\n");
948 function sql_random_function() {
949 if (DB_TYPE
== "mysql") {
956 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
958 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
960 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
962 // Todo: all this interval stuff needs some generic generator function
964 $date_qpart = "false";
968 if (DB_TYPE
== "pgsql") {
969 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
971 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
975 if (DB_TYPE
== "pgsql") {
976 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
978 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
982 if (DB_TYPE
== "pgsql") {
983 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
985 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
989 $date_qpart = "true";
992 if (is_numeric($feed)) {
998 $children = getChildCategories($link, $feed, $owner_uid);
999 array_push($children, $feed);
1001 $children = join(",", $children);
1003 $cat_qpart = "cat_id IN ($children)";
1005 $cat_qpart = "cat_id IS NULL";
1008 db_query($link, "UPDATE ttrss_user_entries
1009 SET unread = false, last_read = NOW() WHERE ref_id IN
1011 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1012 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1013 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1015 } else if ($feed == -2) {
1017 db_query($link, "UPDATE ttrss_user_entries
1018 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1019 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1020 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1023 } else if ($feed > 0) {
1025 db_query($link, "UPDATE ttrss_user_entries
1026 SET unread = false, last_read = NOW() WHERE ref_id IN
1028 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1029 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1031 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1034 db_query($link, "UPDATE ttrss_user_entries
1035 SET unread = false, last_read = NOW() WHERE ref_id IN
1037 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1038 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1042 db_query($link, "UPDATE ttrss_user_entries
1043 SET unread = false, last_read = NOW() WHERE ref_id IN
1045 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1046 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1051 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1053 if (DB_TYPE
== "pgsql") {
1054 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1056 $match_part = "updated > DATE_SUB(NOW(),
1057 INTERVAL $intl HOUR) ";
1060 db_query($link, "UPDATE ttrss_user_entries
1061 SET unread = false, last_read = NOW() WHERE ref_id IN
1063 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1064 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart AND $match_part) as tmp)");
1068 db_query($link, "UPDATE ttrss_user_entries
1069 SET unread = false, last_read = NOW() WHERE ref_id IN
1071 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1072 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1075 } else if ($feed < LABEL_BASE_INDEX
) { // label
1077 $label_id = feed_to_label_id($feed);
1079 db_query($link, "UPDATE ttrss_user_entries
1080 SET unread = false, last_read = NOW() WHERE ref_id IN
1082 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1083 AND label_id = '$label_id' AND ref_id = article_id
1084 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1088 ccache_update($link, $feed, $owner_uid, $cat_view);
1091 db_query($link, "UPDATE ttrss_user_entries
1092 SET unread = false, last_read = NOW() WHERE ref_id IN
1094 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1095 AND post_int_id = int_id AND tag_name = '$feed'
1096 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1101 function getAllCounters($link) {
1102 $data = getGlobalCounters($link);
1104 $data = array_merge($data, getVirtCounters($link));
1105 $data = array_merge($data, getLabelCounters($link));
1106 $data = array_merge($data, getFeedCounters($link, $active_feed));
1107 $data = array_merge($data, getCategoryCounters($link));
1112 function getCategoryTitle($link, $cat_id) {
1114 if ($cat_id == -1) {
1115 return __("Special");
1116 } else if ($cat_id == -2) {
1117 return __("Labels");
1120 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1123 if (db_num_rows($result) == 1) {
1124 return db_fetch_result($result, 0, "title");
1126 return __("Uncategorized");
1132 function getCategoryCounters($link) {
1135 /* Labels category */
1137 $cv = array("id" => -2, "kind" => "cat",
1138 "counter" => getCategoryUnread($link, -2));
1140 array_push($ret_arr, $cv);
1142 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1143 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1144 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1145 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1146 WHERE ttrss_cat_counters_cache.feed_id = id AND
1147 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1148 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1150 while ($line = db_fetch_assoc($result)) {
1151 $line["cat_id"] = (int) $line["cat_id"];
1153 if ($line["num_children"] > 0) {
1154 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1159 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1160 "counter" => $line["unread"] +
$child_counter);
1162 array_push($ret_arr, $cv);
1165 /* Special case: NULL category doesn't actually exist in the DB */
1167 $cv = array("id" => 0, "kind" => "cat",
1168 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1170 array_push($ret_arr, $cv);
1175 // only accepts real cats (>= 0)
1176 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1177 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1179 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1180 AND owner_uid = $owner_uid");
1184 while ($line = db_fetch_assoc($result)) {
1185 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1186 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1192 function getCategoryUnread($link, $cat, $owner_uid = false) {
1194 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1199 $cat_query = "cat_id = '$cat'";
1201 $cat_query = "cat_id IS NULL";
1204 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1205 AND owner_uid = " . $owner_uid);
1207 $cat_feeds = array();
1208 while ($line = db_fetch_assoc($result)) {
1209 array_push($cat_feeds, "feed_id = " . $line["id"]);
1212 if (count($cat_feeds) == 0) return 0;
1214 $match_part = implode(" OR ", $cat_feeds);
1216 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1217 FROM ttrss_user_entries
1218 WHERE unread = true AND ($match_part)
1219 AND owner_uid = " . $owner_uid);
1223 # this needs to be rewritten
1224 while ($line = db_fetch_assoc($result)) {
1225 $unread +
= $line["unread"];
1229 } else if ($cat == -1) {
1230 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1231 } else if ($cat == -2) {
1233 $result = db_query($link, "
1234 SELECT COUNT(unread) AS unread FROM
1235 ttrss_user_entries, ttrss_user_labels2
1236 WHERE article_id = ref_id AND unread = true
1237 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1239 $unread = db_fetch_result($result, 0, "unread");
1246 function getFeedUnread($link, $feed, $is_cat = false) {
1247 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1250 function getLabelUnread($link, $label_id, $owner_uid = false) {
1251 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1253 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1254 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1256 if (db_num_rows($result) != 0) {
1257 return db_fetch_result($result, 0, "unread");
1263 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1264 $owner_uid = false) {
1266 $n_feed = (int) $feed;
1267 $need_entries = false;
1269 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1272 $unread_qpart = "unread = true";
1274 $unread_qpart = "true";
1278 return getCategoryUnread($link, $n_feed, $owner_uid);
1279 } else if ($n_feed == -6) {
1281 } else if ($feed != "0" && $n_feed == 0) {
1283 $feed = db_escape_string($link, $feed);
1285 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1286 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1287 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1288 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1289 return db_fetch_result($result, 0, "count");
1291 } else if ($n_feed == -1) {
1292 $match_part = "marked = true";
1293 } else if ($n_feed == -2) {
1294 $match_part = "published = true";
1295 } else if ($n_feed == -3) {
1296 $match_part = "unread = true AND score >= 0";
1298 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1300 if (DB_TYPE
== "pgsql") {
1301 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1303 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1306 $need_entries = true;
1308 } else if ($n_feed == -4) {
1309 $match_part = "true";
1310 } else if ($n_feed >= 0) {
1313 $match_part = "feed_id = '$n_feed'";
1315 $match_part = "feed_id IS NULL";
1318 } else if ($feed < LABEL_BASE_INDEX
) {
1320 $label_id = feed_to_label_id($feed);
1322 return getLabelUnread($link, $label_id, $owner_uid);
1328 if ($need_entries) {
1329 $from_qpart = "ttrss_user_entries,ttrss_entries";
1330 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1332 $from_qpart = "ttrss_user_entries";
1335 $query = "SELECT count(int_id) AS unread
1336 FROM $from_qpart WHERE
1337 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1339 //echo "[$feed/$query]\n";
1341 $result = db_query($link, $query);
1345 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1346 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1347 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1348 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1351 $unread = db_fetch_result($result, 0, "unread");
1356 function getGlobalUnread($link, $user_id = false) {
1359 $user_id = $_SESSION["uid"];
1362 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1363 WHERE owner_uid = '$user_id' AND feed_id > 0");
1365 $c_id = db_fetch_result($result, 0, "c_id");
1370 function getGlobalCounters($link, $global_unread = -1) {
1373 if ($global_unread == -1) {
1374 $global_unread = getGlobalUnread($link);
1377 $cv = array("id" => "global-unread",
1378 "counter" => (int) $global_unread);
1380 array_push($ret_arr, $cv);
1382 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1383 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1385 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1387 $cv = array("id" => "subscribed-feeds",
1388 "counter" => (int) $subscribed_feeds);
1390 array_push($ret_arr, $cv);
1395 function getVirtCounters($link) {
1399 for ($i = 0; $i >= -4; $i--) {
1401 $count = getFeedUnread($link, $i);
1403 $cv = array("id" => $i,
1404 "counter" => (int) $count);
1406 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1407 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1409 array_push($ret_arr, $cv);
1415 $feeds = $pluginhost->get_feeds(-1);
1417 if (is_array($feeds)) {
1418 foreach ($feeds as $feed) {
1419 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1420 "counter" => $feed['sender']->get_unread($feed['id']));
1422 array_push($ret_arr, $cv);
1430 function getLabelCounters($link, $descriptions = false) {
1434 $owner_uid = $_SESSION["uid"];
1436 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1437 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1438 (ttrss_labels2.id = label_id)
1439 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1440 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1441 ttrss_labels2.caption");
1443 while ($line = db_fetch_assoc($result)) {
1445 $id = label_to_feed_id($line["id"]);
1447 $label_name = $line["caption"];
1448 $count = $line["unread"];
1450 $cv = array("id" => $id,
1451 "counter" => (int) $count);
1454 $cv["description"] = $label_name;
1456 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1457 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1459 array_push($ret_arr, $cv);
1465 function getFeedCounters($link, $active_feed = false) {
1469 $query = "SELECT ttrss_feeds.id,
1471 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1472 last_error, value AS count
1473 FROM ttrss_feeds, ttrss_counters_cache
1474 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1475 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1476 AND ttrss_counters_cache.feed_id = id";
1478 $result = db_query($link, $query);
1479 $fctrs_modified = false;
1481 while ($line = db_fetch_assoc($result)) {
1484 $count = $line["count"];
1485 $last_error = htmlspecialchars($line["last_error"]);
1487 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1489 $has_img = feed_has_icon($id);
1491 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1494 $cv = array("id" => $id,
1495 "updated" => $last_updated,
1496 "counter" => (int) $count,
1497 "has_img" => (int) $has_img);
1500 $cv["error"] = $last_error;
1502 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1503 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1505 if ($active_feed && $id == $active_feed)
1506 $cv["title"] = truncate_string($line["title"], 30);
1508 array_push($ret_arr, $cv);
1515 function get_pgsql_version($link) {
1516 $result = db_query($link, "SELECT version() AS version");
1517 $version = explode(" ", db_fetch_result($result, 0, "version"));
1522 * @return array (code => Status code, message => error message if available)
1524 * 0 - OK, Feed already exists
1525 * 1 - OK, Feed added
1527 * 3 - URL content is HTML, no feeds available
1528 * 4 - URL content is HTML which contains multiple feeds.
1529 * Here you should call extractfeedurls in rpc-backend
1530 * to get all possible feeds.
1531 * 5 - Couldn't download the URL content.
1533 function subscribe_to_feed($link, $url, $cat_id = 0,
1534 $auth_login = '', $auth_pass = '') {
1536 global $fetch_last_error;
1538 require_once "include/rssfuncs.php";
1540 $url = fix_url($url);
1542 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1544 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1547 return array("code" => 5, "message" => $fetch_last_error);
1550 if (is_html($contents)) {
1551 $feedUrls = get_feeds_from_html($url, $contents);
1553 if (count($feedUrls) == 0) {
1554 return array("code" => 3);
1555 } else if (count($feedUrls) > 1) {
1556 return array("code" => 4, "feeds" => $feedUrls);
1558 //use feed url as new URL
1559 $url = key($feedUrls);
1562 if ($cat_id == "0" ||
!$cat_id) {
1563 $cat_qpart = "NULL";
1565 $cat_qpart = "'$cat_id'";
1568 $result = db_query($link,
1569 "SELECT id FROM ttrss_feeds
1570 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1572 if (db_num_rows($result) == 0) {
1573 $result = db_query($link,
1574 "INSERT INTO ttrss_feeds
1575 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1576 VALUES ('".$_SESSION["uid"]."', '$url',
1577 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1579 $result = db_query($link,
1580 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1581 AND owner_uid = " . $_SESSION["uid"]);
1583 $feed_id = db_fetch_result($result, 0, "id");
1586 update_rss_feed($link, $feed_id, true);
1589 return array("code" => 1);
1591 return array("code" => 0);
1595 function print_feed_select($link, $id, $default_id = "",
1596 $attributes = "", $include_all_feeds = true,
1597 $root_id = false, $nest_level = 0) {
1600 print "<select id=\"$id\" name=\"$id\" $attributes>";
1601 if ($include_all_feeds) {
1602 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1603 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1607 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1610 $parent_qpart = "parent_cat = '$root_id'";
1612 $parent_qpart = "parent_cat IS NULL";
1614 $result = db_query($link, "SELECT id,title,
1615 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1616 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1617 FROM ttrss_feed_categories
1618 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1620 while ($line = db_fetch_assoc($result)) {
1622 for ($i = 0; $i < $nest_level; $i++
)
1623 $line["title"] = " - " . $line["title"];
1625 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1627 printf("<option $is_selected value='CAT:%d'>%s</option>",
1628 $line["id"], htmlspecialchars($line["title"]));
1630 if ($line["num_children"] > 0)
1631 print_feed_select($link, $id, $default_id, $attributes,
1632 $include_all_feeds, $line["id"], $nest_level+
1);
1634 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1635 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1637 while ($fline = db_fetch_assoc($feed_result)) {
1638 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1640 $fline["title"] = " + " . $fline["title"];
1642 for ($i = 0; $i < $nest_level; $i++
)
1643 $fline["title"] = " - " . $fline["title"];
1645 printf("<option $is_selected value='%d'>%s</option>",
1646 $fline["id"], htmlspecialchars($fline["title"]));
1651 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1653 printf("<option $is_selected value='CAT:0'>%s</option>",
1654 __("Uncategorized"));
1656 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1657 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1659 while ($fline = db_fetch_assoc($feed_result)) {
1660 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1662 $fline["title"] = " + " . $fline["title"];
1664 for ($i = 0; $i < $nest_level; $i++
)
1665 $fline["title"] = " - " . $fline["title"];
1667 printf("<option $is_selected value='%d'>%s</option>",
1668 $fline["id"], htmlspecialchars($fline["title"]));
1673 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1674 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1676 while ($line = db_fetch_assoc($result)) {
1678 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1680 printf("<option $is_selected value='%d'>%s</option>",
1681 $line["id"], htmlspecialchars($line["title"]));
1690 function print_feed_cat_select($link, $id, $default_id,
1691 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1694 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1698 $parent_qpart = "parent_cat = '$root_id'";
1700 $parent_qpart = "parent_cat IS NULL";
1702 $result = db_query($link, "SELECT id,title,
1703 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1704 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1705 FROM ttrss_feed_categories
1706 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1708 while ($line = db_fetch_assoc($result)) {
1709 if ($line["id"] == $default_id) {
1710 $is_selected = "selected=\"1\"";
1715 for ($i = 0; $i < $nest_level; $i++
)
1716 $line["title"] = " - " . $line["title"];
1719 printf("<option $is_selected value='%d'>%s</option>",
1720 $line["id"], htmlspecialchars($line["title"]));
1722 if ($line["num_children"] > 0)
1723 print_feed_cat_select($link, $id, $default_id, $attributes,
1724 $include_all_cats, $line["id"], $nest_level+
1);
1728 if ($include_all_cats) {
1729 if (db_num_rows($result) > 0) {
1730 print "<option disabled=\"1\">--------</option>";
1733 if ($default_id == 0) {
1734 $is_selected = "selected=\"1\"";
1739 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1745 function checkbox_to_sql_bool($val) {
1746 return ($val == "on") ?
"true" : "false";
1749 function getFeedCatTitle($link, $id) {
1751 return __("Special");
1752 } else if ($id < LABEL_BASE_INDEX
) {
1753 return __("Labels");
1754 } else if ($id > 0) {
1755 $result = db_query($link, "SELECT ttrss_feed_categories.title
1756 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1757 cat_id = ttrss_feed_categories.id");
1758 if (db_num_rows($result) == 1) {
1759 return db_fetch_result($result, 0, "title");
1761 return __("Uncategorized");
1764 return "getFeedCatTitle($id) failed";
1769 function getFeedIcon($id) {
1772 return "images/archive.png";
1775 return "images/mark_set.svg";
1778 return "images/pub_set.svg";
1781 return "images/fresh.png";
1784 return "images/tag.png";
1787 return "images/recently_read.png";
1790 if ($id < LABEL_BASE_INDEX
) {
1791 return "images/label.png";
1793 if (file_exists(ICONS_DIR
. "/$id.ico"))
1794 return ICONS_URL
. "/$id.ico";
1800 function getFeedTitle($link, $id, $cat = false) {
1802 return getCategoryTitle($link, $id);
1803 } else if ($id == -1) {
1804 return __("Starred articles");
1805 } else if ($id == -2) {
1806 return __("Published articles");
1807 } else if ($id == -3) {
1808 return __("Fresh articles");
1809 } else if ($id == -4) {
1810 return __("All articles");
1811 } else if ($id === 0 ||
$id === "0") {
1812 return __("Archived articles");
1813 } else if ($id == -6) {
1814 return __("Recently read");
1815 } else if ($id < LABEL_BASE_INDEX
) {
1816 $label_id = feed_to_label_id($id);
1817 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1818 if (db_num_rows($result) == 1) {
1819 return db_fetch_result($result, 0, "caption");
1821 return "Unknown label ($label_id)";
1824 } else if (is_numeric($id) && $id > 0) {
1825 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1826 if (db_num_rows($result) == 1) {
1827 return db_fetch_result($result, 0, "title");
1829 return "Unknown feed ($id)";
1836 function make_init_params($link) {
1839 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1840 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1841 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1842 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1844 $params[strtolower($param)] = (int) get_pref($link, $param);
1847 $params["icons_url"] = ICONS_URL
;
1848 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1849 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1850 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1851 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1852 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1853 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1855 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1856 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1858 $max_feed_id = db_fetch_result($result, 0, "mid");
1859 $num_feeds = db_fetch_result($result, 0, "nf");
1861 $params["max_feed_id"] = (int) $max_feed_id;
1862 $params["num_feeds"] = (int) $num_feeds;
1864 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1865 $params["hotkeys"] = get_hotkeys_map($link);
1867 $params["csrf_token"] = $_SESSION["csrf_token"];
1868 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1870 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1875 function get_hotkeys_info($link) {
1877 __("Navigation") => array(
1878 "next_feed" => __("Open next feed"),
1879 "prev_feed" => __("Open previous feed"),
1880 "next_article" => __("Open next article"),
1881 "prev_article" => __("Open previous article"),
1882 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1883 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1884 "search_dialog" => __("Show search dialog")),
1885 __("Article") => array(
1886 "toggle_mark" => __("Toggle starred"),
1887 "toggle_publ" => __("Toggle published"),
1888 "toggle_unread" => __("Toggle unread"),
1889 "edit_tags" => __("Edit tags"),
1890 "dismiss_selected" => __("Dismiss selected"),
1891 "dismiss_read" => __("Dismiss read"),
1892 "open_in_new_window" => __("Open in new window"),
1893 "catchup_below" => __("Mark below as read"),
1894 "catchup_above" => __("Mark above as read"),
1895 "article_scroll_down" => __("Scroll down"),
1896 "article_scroll_up" => __("Scroll up"),
1897 "select_article_cursor" => __("Select article under cursor"),
1898 "email_article" => __("Email article"),
1899 "close_article" => __("Close/collapse article"),
1900 "toggle_widescreen" => __("Toggle widescreen mode"),
1901 "toggle_embed_original" => __("Toggle embed original")),
1902 __("Article selection") => array(
1903 "select_all" => __("Select all articles"),
1904 "select_unread" => __("Select unread"),
1905 "select_marked" => __("Select starred"),
1906 "select_published" => __("Select published"),
1907 "select_invert" => __("Invert selection"),
1908 "select_none" => __("Deselect everything")),
1909 __("Feed") => array(
1910 "feed_refresh" => __("Refresh current feed"),
1911 "feed_unhide_read" => __("Un/hide read feeds"),
1912 "feed_subscribe" => __("Subscribe to feed"),
1913 "feed_edit" => __("Edit feed"),
1914 "feed_catchup" => __("Mark as read"),
1915 "feed_reverse" => __("Reverse headlines"),
1916 "feed_debug_update" => __("Debug feed update"),
1917 "catchup_all" => __("Mark all feeds as read"),
1918 "cat_toggle_collapse" => __("Un/collapse current category"),
1919 "toggle_combined_mode" => __("Toggle combined mode"),
1920 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1921 __("Go to") => array(
1922 "goto_all" => __("All articles"),
1923 "goto_fresh" => __("Fresh"),
1924 "goto_marked" => __("Starred"),
1925 "goto_published" => __("Published"),
1926 "goto_tagcloud" => __("Tag cloud"),
1927 "goto_prefs" => __("Preferences")),
1928 __("Other") => array(
1929 "create_label" => __("Create label"),
1930 "create_filter" => __("Create filter"),
1931 "collapse_sidebar" => __("Un/collapse sidebar"),
1932 "help_dialog" => __("Show help dialog"))
1938 function get_hotkeys_map($link) {
1940 // "navigation" => array(
1943 "n" => "next_article",
1944 "p" => "prev_article",
1945 "(38)|up" => "prev_article",
1946 "(40)|down" => "next_article",
1947 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1948 // "^(40)|Ctrl-down" => "next_article_noscroll",
1949 "(191)|/" => "search_dialog",
1950 // "article" => array(
1951 "s" => "toggle_mark",
1952 "*s" => "toggle_publ",
1953 "u" => "toggle_unread",
1954 "*t" => "edit_tags",
1955 "*d" => "dismiss_selected",
1956 "*x" => "dismiss_read",
1957 "o" => "open_in_new_window",
1958 "c p" => "catchup_below",
1959 "c n" => "catchup_above",
1960 "*n" => "article_scroll_down",
1961 "*p" => "article_scroll_up",
1962 "*(38)|Shift+up" => "article_scroll_up",
1963 "*(40)|Shift+down" => "article_scroll_down",
1964 "a *w" => "toggle_widescreen",
1965 "a e" => "toggle_embed_original",
1966 "e" => "email_article",
1967 "a q" => "close_article",
1968 // "article_selection" => array(
1969 "a a" => "select_all",
1970 "a u" => "select_unread",
1971 "a *u" => "select_marked",
1972 "a p" => "select_published",
1973 "a i" => "select_invert",
1974 "a n" => "select_none",
1976 "f r" => "feed_refresh",
1977 "f a" => "feed_unhide_read",
1978 "f s" => "feed_subscribe",
1979 "f e" => "feed_edit",
1980 "f q" => "feed_catchup",
1981 "f x" => "feed_reverse",
1982 "f *d" => "feed_debug_update",
1983 "f *c" => "toggle_combined_mode",
1984 "f c" => "toggle_cdm_expanded",
1985 "*q" => "catchup_all",
1986 "x" => "cat_toggle_collapse",
1988 "g a" => "goto_all",
1989 "g f" => "goto_fresh",
1990 "g s" => "goto_marked",
1991 "g p" => "goto_published",
1992 "g t" => "goto_tagcloud",
1993 "g *p" => "goto_prefs",
1994 // "other" => array(
1995 "(9)|Tab" => "select_article_cursor", // tab
1996 "c l" => "create_label",
1997 "c f" => "create_filter",
1998 "c s" => "collapse_sidebar",
1999 "^(191)|Ctrl+/" => "help_dialog",
2002 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2003 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2004 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2008 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2009 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2012 $prefixes = array();
2014 foreach (array_keys($hotkeys) as $hotkey) {
2015 $pair = explode(" ", $hotkey, 2);
2017 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2018 array_push($prefixes, $pair[0]);
2022 return array($prefixes, $hotkeys);
2025 function make_runtime_info($link) {
2028 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2029 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2031 $max_feed_id = db_fetch_result($result, 0, "mid");
2032 $num_feeds = db_fetch_result($result, 0, "nf");
2034 $data["max_feed_id"] = (int) $max_feed_id;
2035 $data["num_feeds"] = (int) $num_feeds;
2037 $data['last_article_id'] = getLastArticleId($link);
2038 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2040 $data['dep_ts'] = calculate_dep_timestamp();
2041 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2043 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2045 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2047 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2049 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2052 $stamp_delta = time() - $stamp;
2054 if ($stamp_delta > 1800) {
2058 $_SESSION["daemon_stamp_check"] = time();
2061 $data['daemon_stamp_ok'] = $stamp_check;
2063 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2065 $data['daemon_stamp'] = $stamp_fmt;
2070 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2071 $new_version_details = @check_for_update
($link);
2073 $data['new_version_available'] = (int) ($new_version_details != false);
2075 $_SESSION["last_version_check"] = time();
2076 $_SESSION["version_data"] = $new_version_details;
2082 function search_to_sql($link, $search) {
2084 $search_query_part = "";
2086 $keywords = explode(" ", $search);
2087 $query_keywords = array();
2089 foreach ($keywords as $k) {
2090 if (strpos($k, "-") === 0) {
2097 $commandpair = explode(":", mb_strtolower($k), 2);
2099 if ($commandpair[0] == "note" && $commandpair[1]) {
2101 if ($commandpair[1] == "true")
2102 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2104 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2106 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2108 if ($commandpair[1] == "true")
2109 array_push($query_keywords, "($not (marked = true))");
2111 array_push($query_keywords, "($not (marked = false))");
2113 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2115 if ($commandpair[1] == "true")
2116 array_push($query_keywords, "($not (published = true))");
2118 array_push($query_keywords, "($not (published = false))");
2120 } else if (strpos($k, "@") === 0) {
2122 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2123 $orig_ts = strtotime(substr($k, 1));
2124 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2126 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2128 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2130 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2131 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2135 $search_query_part = implode("AND", $query_keywords);
2137 return $search_query_part;
2140 function getParentCategories($link, $cat, $owner_uid) {
2143 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2144 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2146 while ($line = db_fetch_assoc($result)) {
2147 array_push($rv, $line["parent_cat"]);
2148 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2154 function getChildCategories($link, $cat, $owner_uid) {
2157 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2158 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2160 while ($line = db_fetch_assoc($result)) {
2161 array_push($rv, $line["id"]);
2162 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2168 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) {
2170 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2172 $ext_tables_part = "";
2176 if (SPHINX_ENABLED
) {
2177 $ids = join(",", @sphinx_search
($search, 0, 500));
2180 $search_query_part = "ref_id IN ($ids) AND ";
2182 $search_query_part = "ref_id = -1 AND ";
2185 $search_query_part = search_to_sql($link, $search);
2186 $search_query_part .= " AND ";
2190 $search_query_part = "";
2195 if (DB_TYPE
== "pgsql") {
2196 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2198 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2201 $override_order = "updated DESC";
2203 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2205 // Try to check if SQL regexp implementation chokes on a valid regexp
2206 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2207 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2208 WHERE $filter_query_part LIMIT 1", false);
2211 $test = db_fetch_result($result, 0, "true_val");
2214 $filter_query_part = "false AND";
2216 $filter_query_part .= " AND";
2219 $filter_query_part = "false AND";
2223 $filter_query_part = "";
2227 $since_id_part = "ttrss_entries.id > $since_id AND ";
2229 $since_id_part = "";
2232 $view_query_part = "";
2234 if ($view_mode == "adaptive") {
2236 $view_query_part = " ";
2237 } else if ($feed != -1) {
2239 $unread = getFeedUnread($link, $feed, $cat_view);
2241 if ($cat_view && $feed > 0 && $include_children)
2242 $unread +
= getCategoryChildrenUnread($link, $feed);
2245 $view_query_part = " unread = true AND ";
2250 if ($view_mode == "marked") {
2251 $view_query_part = " marked = true AND ";
2254 if ($view_mode == "has_note") {
2255 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2258 if ($view_mode == "published") {
2259 $view_query_part = " published = true AND ";
2262 if ($view_mode == "unread" && $feed != -6) {
2263 $view_query_part = " unread = true AND ";
2267 $limit_query_part = "LIMIT " . $limit;
2270 $allow_archived = false;
2272 $vfeed_query_part = "";
2274 // override query strategy and enable feed display when searching globally
2275 if ($search && $search_mode == "all_feeds") {
2276 $query_strategy_part = "true";
2277 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2279 } else if (!is_numeric($feed)) {
2280 $query_strategy_part = "true";
2281 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2282 id = feed_id) as feed_title,";
2283 } else if ($search && $search_mode == "this_cat") {
2284 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2287 if ($include_children) {
2288 $subcats = getChildCategories($link, $feed, $owner_uid);
2289 array_push($subcats, $feed);
2290 $cats_qpart = join(",", $subcats);
2292 $cats_qpart = $feed;
2295 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2298 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2301 } else if ($feed > 0) {
2306 if ($include_children) {
2308 $subcats = getChildCategories($link, $feed, $owner_uid);
2310 array_push($subcats, $feed);
2311 $query_strategy_part = "cat_id IN (".
2312 implode(",", $subcats).")";
2315 $query_strategy_part = "cat_id = '$feed'";
2319 $query_strategy_part = "cat_id IS NULL";
2322 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2325 $query_strategy_part = "feed_id = '$feed'";
2327 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2328 $query_strategy_part = "feed_id IS NULL";
2329 $allow_archived = true;
2330 } else if ($feed == 0 && $cat_view) { // uncategorized
2331 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2332 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2333 } else if ($feed == -1) { // starred virtual feed
2334 $query_strategy_part = "marked = true";
2335 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2336 $allow_archived = true;
2338 if (!$override_order) {
2339 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2342 } else if ($feed == -2) { // published virtual feed OR labels category
2345 $query_strategy_part = "published = true";
2346 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2347 $allow_archived = true;
2349 if (!$override_order) {
2350 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2354 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2356 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2358 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2359 ttrss_user_labels2.article_id = ref_id";
2362 } else if ($feed == -6) { // recently read
2363 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2364 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2365 $allow_archived = true;
2367 if (!$override_order) $override_order = "last_read DESC";
2368 } else if ($feed == -3) { // fresh virtual feed
2369 $query_strategy_part = "unread = true AND score >= 0";
2371 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2373 if (DB_TYPE
== "pgsql") {
2374 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2376 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2379 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2380 } else if ($feed == -4) { // all articles virtual feed
2381 $query_strategy_part = "true";
2382 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2383 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2384 $label_id = feed_to_label_id($feed);
2386 $query_strategy_part = "label_id = '$label_id' AND
2387 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2388 ttrss_user_labels2.article_id = ref_id";
2390 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2391 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2392 $allow_archived = true;
2395 $query_strategy_part = "true";
2398 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2399 $date_sort_field = "updated";
2401 $date_sort_field = "date_entered";
2404 $order_by = "$date_sort_field DESC, updated DESC";
2406 if ($view_mode == "unread_first") {
2407 $order_by = "unread DESC, $order_by";
2410 if ($override_order) {
2411 $order_by = $override_order;
2417 $feed_title = T_sprintf("Search results: %s", $search);
2420 $feed_title = getCategoryTitle($link, $feed);
2422 if (is_numeric($feed) && $feed > 0) {
2423 $result = db_query($link, "SELECT title,site_url,last_error
2424 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2426 $feed_title = db_fetch_result($result, 0, "title");
2427 $feed_site_url = db_fetch_result($result, 0, "site_url");
2428 $last_error = db_fetch_result($result, 0, "last_error");
2430 $feed_title = getFeedTitle($link, $feed);
2435 $content_query_part = "content as content_preview, cached_content, ";
2437 if (is_numeric($feed)) {
2440 $feed_kind = "Feeds";
2442 $feed_kind = "Labels";
2445 if ($limit_query_part) {
2446 $offset_query_part = "OFFSET $offset";
2449 // proper override_order applied above
2450 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2451 if (!$override_order) {
2452 $order_by = "ttrss_feeds.title, $order_by";
2454 $order_by = "ttrss_feeds.title, $override_order";
2458 if (!$allow_archived) {
2459 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2460 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2463 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2464 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2467 $query = "SELECT DISTINCT
2470 ttrss_entries.id,ttrss_entries.title,
2474 always_display_enclosures,
2481 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2482 last_marked, last_published,
2490 ttrss_user_entries.ref_id = ttrss_entries.id AND
2491 ttrss_user_entries.owner_uid = '$owner_uid' AND
2496 $query_strategy_part ORDER BY $order_by
2497 $limit_query_part $offset_query_part";
2499 if ($_REQUEST["debug"]) print $query;
2501 $result = db_query($link, $query);
2506 $select_qpart = "SELECT DISTINCT " .
2510 "ttrss_entries.id as id," .
2523 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2524 "last_marked, last_published, " .
2527 $content_query_part .
2530 $feed_kind = "Tags";
2531 $all_tags = explode(",", $feed);
2532 if ($search_mode == 'any') {
2533 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2534 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2535 $where_qpart = " WHERE " .
2536 "ref_id = ttrss_entries.id AND " .
2537 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2538 "post_int_id = int_id AND $tag_sql AND " .
2540 $search_query_part .
2541 $query_strategy_part . " ORDER BY $order_by " .
2546 $sub_selects = array();
2547 $sub_ands = array();
2548 foreach ($all_tags as $term) {
2549 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");
2556 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2561 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2562 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2563 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2564 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2566 // error_log("TAG SQL: " . $tag_sql);
2567 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2569 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2570 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2573 return array($result, $feed_title, $feed_site_url, $last_error);
2577 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2578 if (!$owner) $owner = $_SESSION["uid"];
2580 $res = trim($str); if (!$res) return '';
2582 if (strpos($res, "href=") === false)
2583 $res = rewrite_urls($res);
2585 $charset_hack = '<head>
2586 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2589 $res = trim($res); if (!$res) return '';
2591 libxml_use_internal_errors(true);
2593 $doc = new DOMDocument();
2594 $doc->loadHTML($charset_hack . $res);
2595 $xpath = new DOMXPath($doc);
2597 $entries = $xpath->query('(//a[@href]|//img[@src])');
2599 foreach ($entries as $entry) {
2603 if ($entry->hasAttribute('href'))
2604 $entry->setAttribute('href',
2605 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2607 if ($entry->hasAttribute('src')) {
2608 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2610 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2612 if (file_exists($cached_filename)) {
2613 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2616 $entry->setAttribute('src', $src);
2619 if ($entry->nodeName
== 'img') {
2620 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2621 $force_remove_images ||
$_SESSION["bw_limit"]) {
2623 $p = $doc->createElement('p');
2625 $a = $doc->createElement('a');
2626 $a->setAttribute('href', $entry->getAttribute('src'));
2628 $a->appendChild(new DOMText($entry->getAttribute('src')));
2629 $a->setAttribute('target', '_blank');
2631 $p->appendChild($a);
2633 $entry->parentNode
->replaceChild($p, $entry);
2638 if (strtolower($entry->nodeName
) == "a") {
2639 $entry->setAttribute("target", "_blank");
2643 $entries = $xpath->query('//iframe');
2644 foreach ($entries as $entry) {
2645 $entry->setAttribute('sandbox', 'allow-scripts');
2649 $allowed_elements = array('a', 'address', 'audio', 'article',
2650 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2651 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2652 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2653 'header', 'html', 'i', 'img', 'ins', 'kbd',
2654 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2655 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2656 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2657 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2659 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2661 $disallowed_attributes = array('id', 'style', 'class');
2665 if (isset($pluginhost)) {
2666 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2667 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2668 if (is_array($retval)) {
2670 $allowed_elements = $retval[1];
2671 $disallowed_attributes = $retval[2];
2678 $doc->removeChild($doc->firstChild
); //remove doctype
2679 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2680 $res = $doc->saveHTML();
2684 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2685 $entries = $doc->getElementsByTagName("*");
2687 foreach ($entries as $entry) {
2688 if (!in_array($entry->nodeName
, $allowed_elements)) {
2689 $entry->parentNode
->removeChild($entry);
2692 if ($entry->hasAttributes()) {
2693 $attrs_to_remove = array();
2695 foreach ($entry->attributes
as $attr) {
2697 if (strpos($attr->nodeName
, 'on') === 0) {
2698 array_push($attrs_to_remove, $attr);
2701 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2702 array_push($attrs_to_remove, $attr);
2706 foreach ($attrs_to_remove as $attr) {
2707 $entry->removeAttributeNode($attr);
2715 function check_for_update($link) {
2716 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2717 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2718 "&iid=" . sha1(SELF_URL_PATH
);
2720 $version_data = @fetch_file_contents
($version_url);
2722 if ($version_data) {
2723 $version_data = json_decode($version_data, true);
2724 if ($version_data && $version_data['version']) {
2726 if (version_compare(VERSION
, $version_data['version']) == -1) {
2727 return $version_data;
2735 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2737 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2738 if (count($ids) == 0) return;
2742 foreach ($ids as $id) {
2743 array_push($tmp_ids, "ref_id = '$id'");
2746 $ids_qpart = join(" OR ", $tmp_ids);
2749 db_query($link, "UPDATE ttrss_user_entries SET
2750 unread = false,last_read = NOW()
2751 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2752 } else if ($cmode == 1) {
2753 db_query($link, "UPDATE ttrss_user_entries SET
2755 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2757 db_query($link, "UPDATE ttrss_user_entries SET
2758 unread = NOT unread,last_read = NOW()
2759 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2764 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2765 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2767 while ($line = db_fetch_assoc($result)) {
2768 ccache_update($link, $line["feed_id"], $owner_uid);
2772 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2774 $a_id = db_escape_string($link, $id);
2776 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2778 $query = "SELECT DISTINCT tag_name,
2779 owner_uid as owner FROM
2780 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2781 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2783 $obj_id = md5("TAGS:$owner_uid:$id");
2786 /* check cache first */
2788 if ($tag_cache === false) {
2789 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2790 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2792 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2796 $tags = explode(",", $tag_cache);
2799 /* do it the hard way */
2801 $tmp_result = db_query($link, $query);
2803 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2804 array_push($tags, $tmp_line["tag_name"]);
2807 /* update the cache */
2809 $tags_str = db_escape_string($link, join(",", $tags));
2811 db_query($link, "UPDATE ttrss_user_entries
2812 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2813 AND owner_uid = $owner_uid");
2819 function trim_array($array) {
2821 array_walk($tmp, 'trim');
2825 function tag_is_valid($tag) {
2826 if ($tag == '') return false;
2827 if (preg_match("/^[0-9]*$/", $tag)) return false;
2828 if (mb_strlen($tag) > 250) return false;
2830 if (function_exists('iconv')) {
2831 $tag = iconv("utf-8", "utf-8", $tag);
2834 if (!$tag) return false;
2839 function render_login_form($link) {
2840 require_once "login_form.php";
2844 // from http://developer.apple.com/internet/safari/faq.html
2845 function no_cache_incantation() {
2846 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2847 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2848 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2849 header("Cache-Control: post-check=0, pre-check=0", false);
2850 header("Pragma: no-cache"); // HTTP/1.0
2853 function format_warning($msg, $id = "") {
2855 return "<div class=\"warning\" id=\"$id\">
2856 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2859 function format_notice($msg, $id = "") {
2861 return "<div class=\"notice\" id=\"$id\">
2862 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2865 function format_error($msg, $id = "") {
2867 return "<div class=\"error\" id=\"$id\">
2868 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2871 function print_notice($msg) {
2872 return print format_notice($msg);
2875 function print_warning($msg) {
2876 return print format_warning($msg);
2879 function print_error($msg) {
2880 return print format_error($msg);
2884 function T_sprintf() {
2885 $args = func_get_args();
2886 return vsprintf(__(array_shift($args)), $args);
2889 function format_inline_player($link, $url, $ctype) {
2893 $url = htmlspecialchars($url);
2895 if (strpos($ctype, "audio/") === 0) {
2897 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2898 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2899 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2901 $id = 'AUDIO-' . uniqid();
2903 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2904 <source type=\"$ctype\" src=\"$url\"></source>
2907 $entry .= "<span onclick=\"player(this)\"
2908 title=\"".__("Click to play")."\" status=\"0\"
2909 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2913 $entry .= "<object type=\"application/x-shockwave-flash\"
2914 data=\"lib/button/musicplayer.swf?song_url=$url\"
2915 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2916 <param name=\"movie\"
2917 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2921 if ($entry) $entry .= " <a target=\"_blank\"
2922 href=\"$url\">" . basename($url) . "</a>";
2930 /* $filename = substr($url, strrpos($url, "/")+1);
2932 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2933 $filename . " (" . $ctype . ")" . "</a>"; */
2937 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2938 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2944 /* we can figure out feed_id from article id anyway, why do we
2945 * pass feed_id here? let's ignore the argument :( */
2947 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2948 WHERE ref_id = '$id'");
2950 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2952 $rv['feed_id'] = $feed_id;
2954 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2956 if ($mark_as_read) {
2957 $result = db_query($link, "UPDATE ttrss_user_entries
2958 SET unread = false,last_read = NOW()
2959 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2961 ccache_update($link, $feed_id, $owner_uid);
2964 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2965 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2966 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2967 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2974 FROM ttrss_entries,ttrss_user_entries
2975 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2979 $line = db_fetch_assoc($result);
2981 $tag_cache = $line["tag_cache"];
2983 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
2984 unset($line["tag_cache"]);
2986 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
2990 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
2991 $line = $p->hook_render_article($line);
2994 $num_comments = $line["num_comments"];
2995 $entry_comments = "";
2997 if ($num_comments > 0) {
2998 if ($line["comments"]) {
2999 $comments_url = htmlspecialchars($line["comments"]);
3001 $comments_url = htmlspecialchars($line["link"]);
3003 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3005 if ($line["comments"] && $line["link"] != $line["comments"]) {
3006 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3011 header("Content-Type: text/html");
3012 $rv['content'] .= "<html><head>
3013 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3014 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3015 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3016 </head><body id=\"ttrssZoom\">";
3019 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3021 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3023 $entry_author = $line["author"];
3025 if ($entry_author) {
3026 $entry_author = __(" - ") . $entry_author;
3029 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3032 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3034 if ($line["link"]) {
3035 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3036 title=\"".htmlspecialchars($line['title'])."\"
3038 htmlspecialchars($line["link"]) . "\">" .
3039 $line["title"] . "</a>" .
3040 "<span class='author'>$entry_author</span></div>";
3042 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3045 $tags_str = format_tags_string($line["tags"], $id);
3046 $tags_str_full = join(", ", $line["tags"]);
3048 if (!$tags_str_full) $tags_str_full = __("no tags");
3050 if (!$entry_comments) $entry_comments = " "; # placeholder
3052 $rv['content'] .= "<div class='postTags' style='float : right'>
3053 <img src='images/tag.png'
3054 class='tagsPic' alt='Tags' title='Tags'> ";
3057 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3058 <a title=\"".__('Edit tags for this article')."\"
3059 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3061 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3062 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3063 position=\"below\">$tags_str_full</div>";
3067 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3068 $rv['content'] .= $p->hook_article_button($line);
3073 $tags_str = strip_tags($tags_str);
3074 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3076 $rv['content'] .= "</div>";
3077 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3079 if ($line["orig_feed_id"]) {
3081 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3082 WHERE id = ".$line["orig_feed_id"]);
3084 if (db_num_rows($tmp_result) != 0) {
3086 $rv['content'] .= "<div clear='both'>";
3087 $rv['content'] .= __("Originally from:");
3089 $rv['content'] .= " ";
3091 $tmp_line = db_fetch_assoc($tmp_result);
3093 $rv['content'] .= "<a target='_blank'
3094 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3095 $tmp_line['title'] . "</a>";
3097 $rv['content'] .= " ";
3099 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3100 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3102 $rv['content'] .= "</div>";
3106 $rv['content'] .= "</div>";
3108 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3109 if ($line['note']) {
3110 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3112 $rv['content'] .= "</div>";
3114 $rv['content'] .= "<div class=\"postContent\">";
3116 $rv['content'] .= $line["content"];
3118 $rv['content'] .= format_article_enclosures($link, $id,
3119 $always_display_enclosures, $line["content"], $line["hide_images"]);
3121 $rv['content'] .= "</div>";
3123 $rv['content'] .= "</div>";
3129 <div class='footer'>
3130 <button onclick=\"return window.close()\">".
3131 __("Close this window")."</button></div>";
3132 $rv['content'] .= "</body></html>";
3139 function print_checkpoint($n, $s) {
3140 $ts = microtime(true);
3141 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3145 function sanitize_tag($tag) {
3148 $tag = mb_strtolower($tag, 'utf-8');
3150 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3152 // $tag = str_replace('"', "", $tag);
3153 // $tag = str_replace("+", " ", $tag);
3154 $tag = str_replace("technorati tag: ", "", $tag);
3159 function get_self_url_prefix() {
3160 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3161 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3163 return SELF_URL_PATH
;
3168 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3170 * @return string The Mozilla Firefox feed adding URL.
3172 function add_feed_url() {
3173 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3175 $url_path = get_self_url_prefix() .
3176 "/public.php?op=subscribe&feed_url=%s";
3178 } // function add_feed_url
3180 function encrypt_password($pass, $salt = '', $mode2 = false) {
3181 if ($salt && $mode2) {
3182 return "MODE2:" . hash('sha256', $salt . $pass);
3184 return "SHA1X:" . sha1("$salt:$pass");
3186 return "SHA1:" . sha1($pass);
3188 } // function encrypt_password
3190 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3193 $cat_id = (int)getFeedCategory($link, $feed_id);
3195 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3196 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3198 $check_cats = join(",", array_merge(
3199 getParentCategories($link, $cat_id, $owner_uid),
3202 while ($line = db_fetch_assoc($result)) {
3203 $filter_id = $line["id"];
3205 $result2 = db_query($link, "SELECT
3206 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3207 FROM ttrss_filters2_rules AS r,
3208 ttrss_filter_types AS t
3210 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3211 (feed_id IS NULL OR feed_id = '$feed_id') AND
3212 filter_type = t.id AND filter_id = '$filter_id'");
3217 while ($rule_line = db_fetch_assoc($result2)) {
3218 # print_r($rule_line);
3221 $rule["reg_exp"] = $rule_line["reg_exp"];
3222 $rule["type"] = $rule_line["type_name"];
3223 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3225 array_push($rules, $rule);
3228 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3229 FROM ttrss_filters2_actions AS a,
3230 ttrss_filter_actions AS t
3232 action_id = t.id AND filter_id = '$filter_id'");
3234 while ($action_line = db_fetch_assoc($result2)) {
3235 # print_r($action_line);
3238 $action["type"] = $action_line["type_name"];
3239 $action["param"] = $action_line["action_param"];
3241 array_push($actions, $action);
3246 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3247 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3248 $filter["rules"] = $rules;
3249 $filter["actions"] = $actions;
3251 if (count($rules) > 0 && count($actions) > 0) {
3252 array_push($filters, $filter);
3259 function get_score_pic($score) {
3261 return "score_high.png";
3262 } else if ($score > 0) {
3263 return "score_half_high.png";
3264 } else if ($score < -100) {
3265 return "score_low.png";
3266 } else if ($score < 0) {
3267 return "score_half_low.png";
3269 return "score_neutral.png";
3273 function feed_has_icon($id) {
3274 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3277 function init_connection($link) {
3280 if (DB_TYPE
== "pgsql") {
3281 pg_query($link, "set client_encoding = 'UTF-8'");
3282 pg_set_client_encoding("UNICODE");
3283 pg_query($link, "set datestyle = 'ISO, european'");
3284 pg_query($link, "set TIME ZONE 0");
3286 db_query($link, "SET time_zone = '+0:0'");
3288 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3289 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3295 $pluginhost = new PluginHost($link);
3296 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3300 print "Unable to connect to database:" . db_last_error();
3305 function format_tags_string($tags, $id) {
3308 $tags_nolinks_str = "";
3314 $formatted_tags = array();
3316 foreach ($tags as $tag) {
3318 $tag_escaped = str_replace("'", "\\'", $tag);
3320 if (mb_strlen($tag) > 30) {
3321 $tag = truncate_string($tag, 30);
3324 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3326 array_push($formatted_tags, $tag_str);
3328 $tmp_tags_str = implode(", ", $formatted_tags);
3330 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3335 $tags_str = implode(", ", $formatted_tags);
3337 if ($num_tags < count($tags)) {
3338 $tags_str .= ", …";
3341 if ($num_tags == 0) {
3342 $tags_str = __("no tags");
3349 function format_article_labels($labels, $id) {
3353 foreach ($labels as $l) {
3354 $labels_str .= sprintf("<span class='hlLabelRef'
3355 style='color : %s; background-color : %s'>%s</span>",
3356 $l[2], $l[3], $l[1]);
3363 function format_article_note($id, $note, $allow_edit = true) {
3365 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3366 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3367 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3373 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3374 if ($parent_cat_id) {
3375 $parent_qpart = "parent_cat = '$parent_cat_id'";
3376 $parent_insert = "'$parent_cat_id'";
3378 $parent_qpart = "parent_cat IS NULL";
3379 $parent_insert = "NULL";
3382 $result = db_query($link,
3383 "SELECT id FROM ttrss_feed_categories
3384 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3386 if (db_num_rows($result) == 0) {
3389 return db_fetch_result($result, 0, "id");
3393 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3395 if (!$feed_cat) return false;
3397 db_query($link, "BEGIN");
3399 if ($parent_cat_id) {
3400 $parent_qpart = "parent_cat = '$parent_cat_id'";
3401 $parent_insert = "'$parent_cat_id'";
3403 $parent_qpart = "parent_cat IS NULL";
3404 $parent_insert = "NULL";
3407 $result = db_query($link,
3408 "SELECT id FROM ttrss_feed_categories
3409 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3411 if (db_num_rows($result) == 0) {
3413 $result = db_query($link,
3414 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3415 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3417 db_query($link, "COMMIT");
3425 function getArticleFeed($link, $id) {
3426 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3427 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3429 if (db_num_rows($result) != 0) {
3430 return db_fetch_result($result, 0, "feed_id");
3437 * Fixes incomplete URLs by prepending "http://".
3438 * Also replaces feed:// with http://, and
3439 * prepends a trailing slash if the url is a domain name only.
3441 * @param string $url Possibly incomplete URL
3443 * @return string Fixed URL.
3445 function fix_url($url) {
3446 if (strpos($url, '://') === false) {
3447 $url = 'http://' . $url;
3448 } else if (substr($url, 0, 5) == 'feed:') {
3449 $url = 'http:' . substr($url, 5);
3452 //prepend slash if the URL has no slash in it
3453 // "http://www.example" -> "http://www.example/"
3454 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3458 if ($url != "http:///")
3464 function validate_feed_url($url) {
3465 $parts = parse_url($url);
3467 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3471 function get_article_enclosures($link, $id) {
3473 $query = "SELECT * FROM ttrss_enclosures
3474 WHERE post_id = '$id' AND content_url != ''";
3478 $result = db_query($link, $query);
3480 if (db_num_rows($result) > 0) {
3481 while ($line = db_fetch_assoc($result)) {
3482 array_push($rv, $line);
3489 function save_email_address($link, $email) {
3490 // FIXME: implement persistent storage of emails
3492 if (!$_SESSION['stored_emails'])
3493 $_SESSION['stored_emails'] = array();
3495 if (!in_array($email, $_SESSION['stored_emails']))
3496 array_push($_SESSION['stored_emails'], $email);
3500 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3502 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3504 $sql_is_cat = bool_to_sql_bool($is_cat);
3506 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3507 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3508 AND owner_uid = " . $owner_uid);
3510 if (db_num_rows($result) == 1) {
3511 return db_fetch_result($result, 0, "access_key");
3513 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3515 $result = db_query($link, "INSERT INTO ttrss_access_keys
3516 (access_key, feed_id, is_cat, owner_uid)
3517 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3524 function get_feeds_from_html($url, $content)
3526 $url = fix_url($url);
3527 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3529 libxml_use_internal_errors(true);
3531 $doc = new DOMDocument();
3532 $doc->loadHTML($content);
3533 $xpath = new DOMXPath($doc);
3534 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3535 $feedUrls = array();
3536 foreach ($entries as $entry) {
3537 if ($entry->hasAttribute('href')) {
3538 $title = $entry->getAttribute('title');
3540 $title = $entry->getAttribute('type');
3542 $feedUrl = rewrite_relative_url(
3543 $baseUrl, $entry->getAttribute('href')
3545 $feedUrls[$feedUrl] = $title;
3551 function is_html($content) {
3552 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3555 function url_is_html($url, $login = false, $pass = false) {
3556 return is_html(fetch_file_contents($url, false, $login, $pass));
3559 function print_label_select($link, $name, $value, $attributes = "") {
3561 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3562 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3564 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3565 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3567 while ($line = db_fetch_assoc($result)) {
3569 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3571 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3572 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3576 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3583 function format_article_enclosures($link, $id, $always_display_enclosures,
3584 $article_content, $hide_images = false) {
3586 $result = get_article_enclosures($link, $id);
3589 if (count($result) > 0) {
3591 $entries_html = array();
3593 $entries_inline = array();
3595 foreach ($result as $line) {
3597 $url = $line["content_url"];
3598 $ctype = $line["content_type"];
3600 if (!$ctype) $ctype = __("unknown type");
3602 $filename = substr($url, strrpos($url, "/")+
1);
3604 $player = format_inline_player($link, $url, $ctype);
3606 if ($player) array_push($entries_inline, $player);
3608 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3609 # $filename . " (" . $ctype . ")" . "</a>";
3611 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3612 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3614 array_push($entries_html, $entry);
3618 $entry["type"] = $ctype;
3619 $entry["filename"] = $filename;
3620 $entry["url"] = $url;
3622 array_push($entries, $entry);
3625 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3626 if ($always_display_enclosures ||
3627 !preg_match("/<img/i", $article_content)) {
3629 foreach ($entries as $entry) {
3631 if (preg_match("/image/", $entry["type"]) ||
3632 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3634 if (!$hide_images) {
3636 alt=\"".htmlspecialchars($entry["filename"])."\"
3637 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3639 $rv .= "<p><a target=\"_blank\"
3640 href=\"".htmlspecialchars($entry["url"])."\"
3641 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3649 if (count($entries_inline) > 0) {
3650 $rv .= "<hr clear='both'/>";
3651 foreach ($entries_inline as $entry) { $rv .= $entry; };
3652 $rv .= "<hr clear='both'/>";
3655 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3656 "<option value=''>" . __('Attachments')."</option>";
3658 foreach ($entries as $entry) {
3659 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3669 function getLastArticleId($link) {
3670 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3671 WHERE owner_uid = " . $_SESSION["uid"]);
3673 if (db_num_rows($result) == 1) {
3674 return db_fetch_result($result, 0, "id");
3680 function build_url($parts) {
3681 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3685 * Converts a (possibly) relative URL to a absolute one.
3687 * @param string $url Base URL (i.e. from where the document is)
3688 * @param string $rel_url Possibly relative URL in the document
3690 * @return string Absolute URL
3692 function rewrite_relative_url($url, $rel_url) {
3693 if (strpos($rel_url, "magnet:") === 0) {
3695 } else if (strpos($rel_url, "://") !== false) {
3697 } else if (strpos($rel_url, "//") === 0) {
3698 # protocol-relative URL (rare but they exist)
3700 } else if (strpos($rel_url, "/") === 0)
3702 $parts = parse_url($url);
3703 $parts['path'] = $rel_url;
3705 return build_url($parts);
3708 $parts = parse_url($url);
3709 if (!isset($parts['path'])) {
3710 $parts['path'] = '/';
3712 $dir = $parts['path'];
3713 if (substr($dir, -1) !== '/') {
3714 $dir = dirname($parts['path']);
3715 $dir !== '/' && $dir .= '/';
3717 $parts['path'] = $dir . $rel_url;
3719 return build_url($parts);
3723 function sphinx_search($query, $offset = 0, $limit = 30) {
3724 require_once 'lib/sphinxapi.php';
3726 $sphinxClient = new SphinxClient();
3728 $sphinxClient->SetServer('localhost', 9312);
3729 $sphinxClient->SetConnectTimeout(1);
3731 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3732 'feed_title' => 20));
3734 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3735 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3736 $sphinxClient->SetLimits($offset, $limit, 1000);
3737 $sphinxClient->SetArrayResult(false);
3738 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3740 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3744 if (is_array($result['matches'])) {
3745 foreach (array_keys($result['matches']) as $int_id) {
3746 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3747 array_push($ids, $ref_id);
3754 function cleanup_tags($link, $days = 14, $limit = 1000) {
3756 if (DB_TYPE
== "pgsql") {
3757 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3758 } else if (DB_TYPE
== "mysql") {
3759 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3764 while ($limit > 0) {
3767 $query = "SELECT ttrss_tags.id AS id
3768 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3769 WHERE post_int_id = int_id AND $interval_query AND
3770 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3772 $result = db_query($link, $query);
3776 while ($line = db_fetch_assoc($result)) {
3777 array_push($ids, $line['id']);
3780 if (count($ids) > 0) {
3781 $ids = join(",", $ids);
3783 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3784 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3789 $limit -= $limit_part;
3792 return $tags_deleted;
3795 function print_user_stylesheet($link) {
3796 $value = get_pref($link, 'USER_STYLESHEET');
3799 print "<style type=\"text/css\">";
3800 print str_replace("<br/>", "\n", $value);
3806 function rewrite_urls($html) {
3807 libxml_use_internal_errors(true);
3809 $charset_hack = '<head>
3810 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3813 $doc = new DOMDocument();
3814 $doc->loadHTML($charset_hack . $html);
3815 $xpath = new DOMXPath($doc);
3817 $entries = $xpath->query('//*/text()');
3819 foreach ($entries as $entry) {
3820 if (strstr($entry->wholeText
, "://") !== false) {
3821 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3822 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3824 if ($text != $entry->wholeText
) {
3825 $cdoc = new DOMDocument();
3826 $cdoc->loadHTML($charset_hack . $text);
3829 foreach ($cdoc->childNodes
as $cnode) {
3830 $cnode = $doc->importNode($cnode, true);
3833 $entry->parentNode
->insertBefore($cnode);
3837 $entry->parentNode
->removeChild($entry);
3843 $node = $doc->getElementsByTagName('body')->item(0);
3845 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3847 return $doc->saveXML($node);
3852 function filter_to_sql($link, $filter, $owner_uid) {
3855 if (DB_TYPE
== "pgsql")
3858 $reg_qpart = "REGEXP";
3860 foreach ($filter["rules"] AS $rule) {
3861 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3862 $rule['reg_exp']) !== FALSE;
3864 if ($regexp_valid) {
3866 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3868 switch ($rule["type"]) {
3870 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3871 $rule['reg_exp'] . "')";
3874 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3875 $rule['reg_exp'] . "')";
3878 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3879 $rule['reg_exp'] . "') OR LOWER(" .
3880 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3883 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3884 $rule['reg_exp'] . "')";
3887 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3888 $rule['reg_exp'] . "')";
3891 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3892 $rule['reg_exp'] . "')";
3896 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3898 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3899 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3902 if (isset($rule["cat_id"])) {
3904 if ($rule["cat_id"] > 0) {
3905 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3906 array_push($children, $rule["cat_id"]);
3908 $children = join(",", $children);
3910 $cat_qpart = "cat_id IN ($children)";
3912 $cat_qpart = "cat_id IS NULL";
3915 $qpart .= " AND $cat_qpart";
3918 array_push($query, "($qpart)");
3923 if (count($query) > 0) {
3924 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3926 $fullquery = "(false)";
3929 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3934 if (!function_exists('gzdecode')) {
3935 function gzdecode($string) { // no support for 2nd argument
3936 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3937 base64_encode($string));
3941 function get_random_bytes($length) {
3942 if (function_exists('openssl_random_pseudo_bytes')) {
3943 return openssl_random_pseudo_bytes($length);
3947 for ($i = 0; $i < $length; $i++
)
3948 $output .= chr(mt_rand(0, 255));
3954 function read_stdin() {
3955 $fp = fopen("php://stdin", "r");
3958 $line = trim(fgets($fp));
3966 function tmpdirname($path, $prefix) {
3967 // Use PHP's tmpfile function to create a temporary
3968 // directory name. Delete the file and keep the name.
3969 $tempname = tempnam($path,$prefix);
3973 if (!unlink($tempname))
3979 function getFeedCategory($link, $feed) {
3980 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3981 WHERE id = '$feed'");
3983 if (db_num_rows($result) > 0) {
3984 return db_fetch_result($result, 0, "cat_id");
3991 function implements_interface($class, $interface) {
3992 return in_array($interface, class_implements($class));
3995 function geturl($url){
3997 (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');
3999 $curl = curl_init();
4000 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4001 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4002 $header[] = "Cache-Control: max-age=0";
4003 $header[] = "Connection: keep-alive";
4004 $header[] = "Keep-Alive: 300";
4005 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4006 $header[] = "Accept-Language: en-us,en;q=0.5";
4007 $header[] = "Pragma: ";
4009 curl_setopt($curl, CURLOPT_URL
, $url);
4010 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4011 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4012 curl_setopt($curl, CURLOPT_HEADER
, true);
4013 curl_setopt($curl, CURLOPT_REFERER
, $url);
4014 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4015 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4016 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4017 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4018 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4020 $html = curl_exec($curl);
4022 $status = curl_getinfo($curl);
4025 if($status['http_code']!=200){
4026 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4027 list($header) = explode("\r\n\r\n", $html, 2);
4029 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4030 $url = trim(str_replace($matches[1],"",$matches[0]));
4031 $url_parsed = parse_url($url);
4032 return (isset($url_parsed))?
geturl($url, $referer):'';
4035 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4036 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4037 # $handle = @fopen('./curl.error.log', 'a');
4038 # fwrite($handle, $line);
4044 function get_minified_js($files) {
4045 require_once 'lib/jshrink/Minifier.php';
4049 foreach ($files as $js) {
4050 if (!isset($_GET['debug'])) {
4051 $cached_file = CACHE_DIR
. "/js/$js.js";
4053 if (file_exists($cached_file) &&
4054 is_readable($cached_file) &&
4055 filemtime($cached_file) >= filemtime("js/$js.js")) {
4057 $rv .= file_get_contents($cached_file);
4060 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4061 file_put_contents($cached_file, $minified);
4065 $rv .= file_get_contents("js/$js.js");
4072 function stylesheet_tag($filename) {
4073 $timestamp = filemtime($filename);
4075 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4078 function javascript_tag($filename) {
4081 if (!(strpos($filename, "?") === FALSE)) {
4082 $query = substr($filename, strpos($filename, "?")+
1);
4083 $filename = substr($filename, 0, strpos($filename, "?"));
4086 $timestamp = filemtime($filename);
4088 if ($query) $timestamp .= "&$query";
4090 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4093 function calculate_dep_timestamp() {
4094 $files = array_merge(glob("js/*.js"), glob("*.css"));
4098 foreach ($files as $file) {
4099 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4105 function T_js_decl($s1, $s2) {
4107 $s1 = preg_replace("/\n/", "", $s1);
4108 $s2 = preg_replace("/\n/", "", $s2);
4110 $s1 = preg_replace("/\"/", "\\\"", $s1);
4111 $s2 = preg_replace("/\"/", "\\\"", $s2);
4113 return "T_messages[\"$s1\"] = \"$s2\";\n";
4117 function init_js_translations() {
4119 print 'var T_messages = new Object();
4122 if (T_messages[msg]) {
4123 return T_messages[msg];
4129 function ngettext(msg1, msg2, n) {
4130 return (parseInt(n) > 1) ? msg2 : msg1;
4133 $l10n = _get_reader();
4135 for ($i = 0; $i < $l10n->total
; $i++
) {
4136 $orig = $l10n->get_original_string($i);
4137 $translation = __($orig);
4139 print T_js_decl($orig, $translation);
4143 function label_to_feed_id($label) {
4144 return LABEL_BASE_INDEX
- 1 - abs($label);
4147 function feed_to_label_id($feed) {
4148 return LABEL_BASE_INDEX
- 1 +
abs($feed);