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",
72 require_once "lib/accept-to-gettext.php";
73 require_once "lib/gettext/gettext.inc";
76 function startup_gettext() {
78 # Get locale from Accept-Language header
79 $lang = al2gt(array_keys(get_translations()), "text/html");
81 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
82 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
85 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
86 $lang = $_SESSION["language"];
90 if (defined('LC_MESSAGES')) {
91 _setlocale(LC_MESSAGES
, $lang);
92 } else if (defined('LC_ALL')) {
93 _setlocale(LC_ALL
, $lang);
96 _bindtextdomain("messages", "locale");
98 _textdomain("messages");
99 _bind_textdomain_codeset("messages", "UTF-8");
105 require_once 'db-prefs.php';
106 require_once 'version.php';
107 require_once 'ccache.php';
108 require_once 'labels.php';
110 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
111 ini_set('user_agent', SELF_USER_AGENT
);
113 require_once 'lib/pubsubhubbub/publisher.php';
116 $utc_tz = new DateTimeZone('UTC');
117 $schema_version = false;
120 * Print a timestamped debug message.
122 * @param string $msg The debug message.
125 function _debug($msg) {
126 $ts = strftime("%H:%M:%S", time());
127 if (function_exists('posix_getpid')) {
128 $ts = "$ts/" . posix_getpid();
131 if (!(defined('QUIET') && QUIET
)) {
132 print "[$ts] $msg\n";
135 if (defined('LOGFILE')) {
136 $fp = fopen(LOGFILE
, 'a+');
139 fputs($fp, "[$ts] $msg\n");
147 * Purge a feed old posts.
149 * @param mixed $link A database connection.
150 * @param mixed $feed_id The id of the purged feed.
151 * @param mixed $purge_interval Olderness of purged posts.
152 * @param boolean $debug Set to True to enable the debug. False by default.
156 function purge_feed($link, $feed_id, $purge_interval, $debug = false) {
158 if (!$purge_interval) $purge_interval = feed_purge_interval($link, $feed_id);
162 $result = db_query($link,
163 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
167 if (db_num_rows($result) == 1) {
168 $owner_uid = db_fetch_result($result, 0, "owner_uid");
171 if ($purge_interval == -1 ||
!$purge_interval) {
173 ccache_update($link, $feed_id, $owner_uid);
178 if (!$owner_uid) return;
180 if (FORCE_ARTICLE_PURGE
== 0) {
181 $purge_unread = get_pref($link, "PURGE_UNREAD_ARTICLES",
184 $purge_unread = true;
185 $purge_interval = FORCE_ARTICLE_PURGE
;
188 if (!$purge_unread) $query_limit = " unread = false AND ";
190 if (DB_TYPE
== "pgsql") {
191 $pg_version = get_pgsql_version($link);
193 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
195 $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
196 ttrss_entries.id = ref_id AND
198 feed_id = '$feed_id' AND
200 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
204 $result = db_query($link, "DELETE FROM ttrss_user_entries
206 WHERE ttrss_entries.id = ref_id AND
208 feed_id = '$feed_id' AND
210 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
213 $rows = pg_affected_rows($result);
217 /* $result = db_query($link, "DELETE FROM ttrss_user_entries WHERE
218 marked = false AND feed_id = '$feed_id' AND
219 (SELECT date_updated FROM ttrss_entries WHERE
220 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
222 $result = db_query($link, "DELETE FROM ttrss_user_entries
223 USING ttrss_user_entries, ttrss_entries
224 WHERE ttrss_entries.id = ref_id AND
226 feed_id = '$feed_id' AND
228 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
230 $rows = mysql_affected_rows($link);
234 ccache_update($link, $feed_id, $owner_uid);
237 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
241 } // function purge_feed
243 function feed_purge_interval($link, $feed_id) {
245 $result = db_query($link, "SELECT purge_interval, owner_uid FROM ttrss_feeds
246 WHERE id = '$feed_id'");
248 if (db_num_rows($result) == 1) {
249 $purge_interval = db_fetch_result($result, 0, "purge_interval");
250 $owner_uid = db_fetch_result($result, 0, "owner_uid");
252 if ($purge_interval == 0) $purge_interval = get_pref($link,
253 'PURGE_OLD_DAYS', $owner_uid);
255 return $purge_interval;
262 function purge_orphans($link, $do_output = false) {
264 // purge orphaned posts in main content table
265 $result = db_query($link, "DELETE FROM ttrss_entries WHERE
266 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
269 $rows = db_affected_rows($link, $result);
270 _debug("Purged $rows orphaned posts.");
274 function get_feed_update_interval($link, $feed_id) {
275 $result = db_query($link, "SELECT owner_uid, update_interval FROM
276 ttrss_feeds WHERE id = '$feed_id'");
278 if (db_num_rows($result) == 1) {
279 $update_interval = db_fetch_result($result, 0, "update_interval");
280 $owner_uid = db_fetch_result($result, 0, "owner_uid");
282 if ($update_interval != 0) {
283 return $update_interval;
285 return get_pref($link, 'DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
293 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
295 global $fetch_last_error;
296 global $fetch_last_error_code;
298 if (function_exists('curl_init') && !ini_get("open_basedir")) {
300 if (ini_get("safe_mode")) {
301 $ch = curl_init(geturl($url));
303 $ch = curl_init($url);
307 curl_setopt($ch, CURLOPT_HTTPHEADER
,
308 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
311 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : 15);
312 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : 45);
313 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode"));
314 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
315 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
316 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
317 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
318 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
319 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
320 curl_setopt($ch, CURLOPT_ENCODING
, "gzip");
321 curl_setopt($ch, CURLOPT_REFERER
, $url);
324 curl_setopt($ch, CURLOPT_POST
, true);
325 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
329 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
331 $contents = @curl_exec
($ch);
333 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
334 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
335 $contents = @curl_exec
($ch);
338 if ($contents === false) {
339 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
344 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
345 $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
347 $fetch_last_error_code = $http_code;
349 if ($http_code != 200 ||
$type && strpos($content_type, "$type") === false) {
350 if (curl_errno($ch) != 0) {
351 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
353 $fetch_last_error = "HTTP Code: $http_code";
363 if ($login && $pass){
364 $url_parts = array();
366 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
368 $pass = urlencode($pass);
370 if ($url_parts[1] && $url_parts[2]) {
371 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
375 $data = @file_get_contents
($url);
377 @$gzdecoded = gzdecode($data);
378 if ($gzdecoded) $data = $gzdecoded;
380 if (!$data && function_exists('error_get_last')) {
381 $error = error_get_last();
382 $fetch_last_error = $error["message"];
390 * Try to determine the favicon URL for a feed.
391 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
392 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
394 * @param string $url A feed or page URL
396 * @return mixed The favicon URL, or false if none was found.
398 function get_favicon_url($url) {
400 $favicon_url = false;
402 if ($html = @fetch_file_contents
($url)) {
404 libxml_use_internal_errors(true);
406 $doc = new DOMDocument();
407 $doc->loadHTML($html);
408 $xpath = new DOMXPath($doc);
410 $base = $xpath->query('/html/head/base');
411 foreach ($base as $b) {
412 $url = $b->getAttribute("href");
416 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
417 if (count($entries) > 0) {
418 foreach ($entries as $entry) {
419 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
426 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
429 } // function get_favicon_url
431 function check_feed_favicon($site_url, $feed, $link) {
432 # print "FAVICON [$site_url]: $favicon_url\n";
434 $icon_file = ICONS_DIR
. "/$feed.ico";
436 if (!file_exists($icon_file)) {
437 $favicon_url = get_favicon_url($site_url);
440 // Limiting to "image" type misses those served with text/plain
441 $contents = fetch_file_contents($favicon_url); // , "image");
444 // Crude image type matching.
445 // Patterns gleaned from the file(1) source code.
446 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
447 // 0 string \000\000\001\000 MS Windows icon resource
448 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
450 elseif (preg_match('/^GIF8/', $contents)) {
451 // 0 string GIF8 GIF image data
452 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
454 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
455 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
456 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
458 elseif (preg_match('/^\xff\xd8/', $contents)) {
459 // 0 beshort 0xffd8 JPEG image data
460 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
463 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
469 $fp = @fopen
($icon_file, "w");
472 fwrite($fp, $contents);
474 chmod($icon_file, 0644);
481 function print_select($id, $default, $values, $attributes = "") {
482 print "<select name=\"$id\" id=\"$id\" $attributes>";
483 foreach ($values as $v) {
485 $sel = "selected=\"1\"";
491 print "<option value=\"$v\" $sel>$v</option>";
496 function print_select_hash($id, $default, $values, $attributes = "") {
497 print "<select name=\"$id\" id='$id' $attributes>";
498 foreach (array_keys($values) as $v) {
500 $sel = 'selected="selected"';
506 print "<option $sel value=\"$v\">".$values[$v]."</option>";
512 function print_radio($id, $default, $true_is, $values, $attributes = "") {
513 foreach ($values as $v) {
520 if ($v == $true_is) {
521 $sel .= " value=\"1\"";
523 $sel .= " value=\"0\"";
526 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
527 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
532 function initialize_user_prefs($link, $uid, $profile = false) {
534 $uid = db_escape_string($link, $uid);
538 $profile_qpart = "AND profile IS NULL";
540 $profile_qpart = "AND profile = '$profile'";
543 if (get_schema_version($link) < 63) $profile_qpart = "";
545 db_query($link, "BEGIN");
547 $result = db_query($link, "SELECT pref_name,def_value FROM ttrss_prefs");
549 $u_result = db_query($link, "SELECT pref_name
550 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
552 $active_prefs = array();
554 while ($line = db_fetch_assoc($u_result)) {
555 array_push($active_prefs, $line["pref_name"]);
558 while ($line = db_fetch_assoc($result)) {
559 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
560 // print "adding " . $line["pref_name"] . "<br>";
562 $line["def_value"] = db_escape_string($link, $line["def_value"]);
563 $line["pref_name"] = db_escape_string($link, $line["pref_name"]);
565 if (get_schema_version($link) < 63) {
566 db_query($link, "INSERT INTO ttrss_user_prefs
567 (owner_uid,pref_name,value) VALUES
568 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
571 db_query($link, "INSERT INTO ttrss_user_prefs
572 (owner_uid,pref_name,value, profile) VALUES
573 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
579 db_query($link, "COMMIT");
583 function get_ssl_certificate_id() {
584 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
585 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
586 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
587 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
588 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
593 function authenticate_user($link, $login, $password, $check_only = false) {
595 if (!SINGLE_USER_MODE
) {
599 foreach ($pluginhost->get_hooks($pluginhost::HOOK_AUTH_USER
) as $plugin) {
601 $user_id = (int) $plugin->authenticate($login, $password);
604 $_SESSION["auth_module"] = strtolower(get_class($plugin));
609 if ($user_id && !$check_only) {
612 $_SESSION["uid"] = $user_id;
614 $result = db_query($link, "SELECT login,access_level,pwd_hash FROM ttrss_users
615 WHERE id = '$user_id'");
617 $_SESSION["name"] = db_fetch_result($result, 0, "login");
618 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
619 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
621 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
624 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
625 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
626 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
628 $_SESSION["last_version_check"] = time();
630 initialize_user_prefs($link, $_SESSION["uid"]);
639 $_SESSION["uid"] = 1;
640 $_SESSION["name"] = "admin";
641 $_SESSION["access_level"] = 10;
643 $_SESSION["hide_hello"] = true;
644 $_SESSION["hide_logout"] = true;
646 $_SESSION["auth_module"] = false;
648 if (!$_SESSION["csrf_token"]) {
649 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
652 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
654 initialize_user_prefs($link, $_SESSION["uid"]);
660 function make_password($length = 8) {
663 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
667 while ($i < $length) {
668 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
670 if (!strstr($password, $char)) {
678 // this is called after user is created to initialize default feeds, labels
681 // user preferences are checked on every login, not here
683 function initialize_user($link, $uid) {
685 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
686 values ('$uid', 'Tiny Tiny RSS: New Releases',
687 'http://tt-rss.org/releases.rss')");
689 db_query($link, "insert into ttrss_feeds (owner_uid,title,feed_url)
690 values ('$uid', 'Tiny Tiny RSS: Forum',
691 'http://tt-rss.org/forum/rss.php')");
694 function logout_user() {
696 if (isset($_COOKIE[session_name()])) {
697 setcookie(session_name(), '', time()-42000, '/');
701 function validate_csrf($csrf_token) {
702 return $csrf_token == $_SESSION['csrf_token'];
705 function load_user_plugins($link, $owner_uid) {
707 $plugins = get_pref($link, "_ENABLED_PLUGINS", $owner_uid);
710 $pluginhost->load($plugins, $pluginhost::KIND_USER
, $owner_uid);
712 if (get_schema_version($link) > 100) {
713 $pluginhost->load_data();
718 function login_sequence($link) {
719 $_SESSION["prefs_cache"] = false;
721 if (SINGLE_USER_MODE
) {
723 authenticate_user($link, "admin", null);
725 load_user_plugins($link, $_SESSION["uid"]);
727 if (!$_SESSION["uid"] ||
!validate_session($link)) {
729 if (AUTH_AUTO_LOGIN
&& authenticate_user($link, null, null)) {
730 $_SESSION["ref_schema_version"] = get_schema_version($link, true);
732 authenticate_user($link, null, null, true);
735 if (!$_SESSION["uid"]) render_login_form($link);
738 /* bump login timestamp */
739 db_query($link, "UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
741 $_SESSION["last_login_update"] = time();
744 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
745 setcookie("ttrss_lang", $_SESSION["language"],
746 time() + SESSION_COOKIE_LIFETIME
);
749 if ($_SESSION["uid"]) {
751 load_user_plugins($link, $_SESSION["uid"]);
755 db_query($link, "DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
756 $_SESSION["uid"] . " AND
757 (SELECT COUNT(id) FROM ttrss_feeds WHERE
758 ttrss_feeds.id = feed_id) = 0");
760 db_query($link, "DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
761 $_SESSION["uid"] . " AND
762 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
763 ttrss_feed_categories.id = feed_id) = 0");
770 function truncate_string($str, $max_len, $suffix = '…') {
771 if (mb_strlen($str, "utf-8") > $max_len - 3) {
772 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
778 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
781 $source_tz = new DateTimeZone($source_tz);
782 } catch (Exception
$e) {
783 $source_tz = new DateTimeZone('UTC');
787 $dest_tz = new DateTimeZone($dest_tz);
788 } catch (Exception
$e) {
789 $dest_tz = new DateTimeZone('UTC');
792 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
793 return $dt->format('U') +
$dest_tz->getOffset($dt);
796 function make_local_datetime($link, $timestamp, $long, $owner_uid = false,
797 $no_smart_dt = false) {
799 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
800 if (!$timestamp) $timestamp = '1970-01-01 0:00';
805 # We store date in UTC internally
806 $dt = new DateTime($timestamp, $utc_tz);
808 if ($tz_offset == -1) {
810 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $owner_uid);
813 $user_tz = new DateTimeZone($user_tz_string);
814 } catch (Exception
$e) {
818 $tz_offset = $user_tz->getOffset($dt);
821 $user_timestamp = $dt->format('U') +
$tz_offset;
824 return smart_date_time($link, $user_timestamp,
825 $tz_offset, $owner_uid);
828 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
830 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
832 return date($format, $user_timestamp);
836 function smart_date_time($link, $timestamp, $tz_offset = 0, $owner_uid = false) {
837 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
839 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
840 return date("G:i", $timestamp);
841 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
842 $format = get_pref($link, 'SHORT_DATE_FORMAT', $owner_uid);
843 return date($format, $timestamp);
845 $format = get_pref($link, 'LONG_DATE_FORMAT', $owner_uid);
846 return date($format, $timestamp);
850 function sql_bool_to_bool($s) {
851 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
858 function bool_to_sql_bool($s) {
866 // Session caching removed due to causing wrong redirects to upgrade
867 // script when get_schema_version() is called on an obsolete session
868 // created on a previous schema version.
869 function get_schema_version($link, $nocache = false) {
870 global $schema_version;
872 if (!$schema_version) {
873 $result = db_query($link, "SELECT schema_version FROM ttrss_version");
874 $version = db_fetch_result($result, 0, "schema_version");
875 $schema_version = $version;
878 return $schema_version;
882 function sanity_check($link) {
883 require_once 'errors.php';
886 $schema_version = get_schema_version($link, true);
888 if ($schema_version != SCHEMA_VERSION
) {
892 if (DB_TYPE
== "mysql") {
893 $result = db_query($link, "SELECT true", false);
894 if (db_num_rows($result) != 1) {
899 if (db_escape_string($link, "testTEST") != "testTEST") {
903 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
906 function file_is_locked($filename) {
907 if (function_exists('flock')) {
908 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
910 if (flock($fp, LOCK_EX | LOCK_NB
)) {
921 return true; // consider the file always locked and skip the test
924 function make_lockfile($filename) {
925 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
927 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
928 if (function_exists('posix_getpid')) {
929 fwrite($fp, posix_getpid() . "\n");
937 function make_stampfile($filename) {
938 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
940 if (flock($fp, LOCK_EX | LOCK_NB
)) {
941 fwrite($fp, time() . "\n");
950 function sql_random_function() {
951 if (DB_TYPE
== "mysql") {
958 function catchup_feed($link, $feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
960 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
962 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
964 // Todo: all this interval stuff needs some generic generator function
966 $date_qpart = "false";
970 if (DB_TYPE
== "pgsql") {
971 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
973 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
977 if (DB_TYPE
== "pgsql") {
978 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
980 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
984 if (DB_TYPE
== "pgsql") {
985 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
987 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
991 $date_qpart = "true";
994 if (is_numeric($feed)) {
1000 $children = getChildCategories($link, $feed, $owner_uid);
1001 array_push($children, $feed);
1003 $children = join(",", $children);
1005 $cat_qpart = "cat_id IN ($children)";
1007 $cat_qpart = "cat_id IS NULL";
1010 db_query($link, "UPDATE ttrss_user_entries
1011 SET unread = false, last_read = NOW() WHERE ref_id IN
1013 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1014 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1015 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1017 } else if ($feed == -2) {
1019 db_query($link, "UPDATE ttrss_user_entries
1020 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1021 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1022 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1025 } else if ($feed > 0) {
1027 db_query($link, "UPDATE ttrss_user_entries
1028 SET unread = false, last_read = NOW() WHERE ref_id IN
1030 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1031 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1033 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1036 db_query($link, "UPDATE ttrss_user_entries
1037 SET unread = false, last_read = NOW() WHERE ref_id IN
1039 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1040 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1044 db_query($link, "UPDATE ttrss_user_entries
1045 SET unread = false, last_read = NOW() WHERE ref_id IN
1047 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1048 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1053 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE");
1055 if (DB_TYPE
== "pgsql") {
1056 $match_part = "updated > NOW() - INTERVAL '$intl hour' ";
1058 $match_part = "updated > DATE_SUB(NOW(),
1059 INTERVAL $intl HOUR) ";
1062 db_query($link, "UPDATE ttrss_user_entries
1063 SET unread = false, last_read = NOW() WHERE ref_id IN
1065 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1066 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart AND $match_part) as tmp)");
1070 db_query($link, "UPDATE ttrss_user_entries
1071 SET unread = false, last_read = NOW() WHERE ref_id IN
1073 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1074 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1077 } else if ($feed < LABEL_BASE_INDEX
) { // label
1079 $label_id = feed_to_label_id($feed);
1081 db_query($link, "UPDATE ttrss_user_entries
1082 SET unread = false, last_read = NOW() WHERE ref_id IN
1084 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1085 AND label_id = '$label_id' AND ref_id = article_id
1086 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1090 ccache_update($link, $feed, $owner_uid, $cat_view);
1093 db_query($link, "UPDATE ttrss_user_entries
1094 SET unread = false, last_read = NOW() WHERE ref_id IN
1096 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1097 AND post_int_id = int_id AND tag_name = '$feed'
1098 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1103 function getAllCounters($link) {
1104 $data = getGlobalCounters($link);
1106 $data = array_merge($data, getVirtCounters($link));
1107 $data = array_merge($data, getLabelCounters($link));
1108 $data = array_merge($data, getFeedCounters($link, $active_feed));
1109 $data = array_merge($data, getCategoryCounters($link));
1114 function getCategoryTitle($link, $cat_id) {
1116 if ($cat_id == -1) {
1117 return __("Special");
1118 } else if ($cat_id == -2) {
1119 return __("Labels");
1122 $result = db_query($link, "SELECT title FROM ttrss_feed_categories WHERE
1125 if (db_num_rows($result) == 1) {
1126 return db_fetch_result($result, 0, "title");
1128 return __("Uncategorized");
1134 function getCategoryCounters($link) {
1137 /* Labels category */
1139 $cv = array("id" => -2, "kind" => "cat",
1140 "counter" => getCategoryUnread($link, -2));
1142 array_push($ret_arr, $cv);
1144 $result = db_query($link, "SELECT id AS cat_id, value AS unread,
1145 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1146 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1147 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1148 WHERE ttrss_cat_counters_cache.feed_id = id AND
1149 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1150 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1152 while ($line = db_fetch_assoc($result)) {
1153 $line["cat_id"] = (int) $line["cat_id"];
1155 if ($line["num_children"] > 0) {
1156 $child_counter = getCategoryChildrenUnread($link, $line["cat_id"], $_SESSION["uid"]);
1161 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1162 "counter" => $line["unread"] +
$child_counter);
1164 array_push($ret_arr, $cv);
1167 /* Special case: NULL category doesn't actually exist in the DB */
1169 $cv = array("id" => 0, "kind" => "cat",
1170 "counter" => (int) ccache_find($link, 0, $_SESSION["uid"], true));
1172 array_push($ret_arr, $cv);
1177 // only accepts real cats (>= 0)
1178 function getCategoryChildrenUnread($link, $cat, $owner_uid = false) {
1179 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1181 $result = db_query($link, "SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1182 AND owner_uid = $owner_uid");
1186 while ($line = db_fetch_assoc($result)) {
1187 $unread +
= getCategoryUnread($link, $line["id"], $owner_uid);
1188 $unread +
= getCategoryChildrenUnread($link, $line["id"], $owner_uid);
1194 function getCategoryUnread($link, $cat, $owner_uid = false) {
1196 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1201 $cat_query = "cat_id = '$cat'";
1203 $cat_query = "cat_id IS NULL";
1206 $result = db_query($link, "SELECT id FROM ttrss_feeds WHERE $cat_query
1207 AND owner_uid = " . $owner_uid);
1209 $cat_feeds = array();
1210 while ($line = db_fetch_assoc($result)) {
1211 array_push($cat_feeds, "feed_id = " . $line["id"]);
1214 if (count($cat_feeds) == 0) return 0;
1216 $match_part = implode(" OR ", $cat_feeds);
1218 $result = db_query($link, "SELECT COUNT(int_id) AS unread
1219 FROM ttrss_user_entries
1220 WHERE unread = true AND ($match_part)
1221 AND owner_uid = " . $owner_uid);
1225 # this needs to be rewritten
1226 while ($line = db_fetch_assoc($result)) {
1227 $unread +
= $line["unread"];
1231 } else if ($cat == -1) {
1232 return getFeedUnread($link, -1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1233 } else if ($cat == -2) {
1235 $result = db_query($link, "
1236 SELECT COUNT(unread) AS unread FROM
1237 ttrss_user_entries, ttrss_user_labels2
1238 WHERE article_id = ref_id AND unread = true
1239 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1241 $unread = db_fetch_result($result, 0, "unread");
1248 function getFeedUnread($link, $feed, $is_cat = false) {
1249 return getFeedArticles($link, $feed, $is_cat, true, $_SESSION["uid"]);
1252 function getLabelUnread($link, $label_id, $owner_uid = false) {
1253 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1255 $result = db_query($link, "SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1256 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1258 if (db_num_rows($result) != 0) {
1259 return db_fetch_result($result, 0, "unread");
1265 function getFeedArticles($link, $feed, $is_cat = false, $unread_only = false,
1266 $owner_uid = false) {
1268 $n_feed = (int) $feed;
1269 $need_entries = false;
1271 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1274 $unread_qpart = "unread = true";
1276 $unread_qpart = "true";
1280 return getCategoryUnread($link, $n_feed, $owner_uid);
1281 } else if ($n_feed == -6) {
1283 } else if ($feed != "0" && $n_feed == 0) {
1285 $feed = db_escape_string($link, $feed);
1287 $result = db_query($link, "SELECT SUM((SELECT COUNT(int_id)
1288 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1289 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1290 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1291 return db_fetch_result($result, 0, "count");
1293 } else if ($n_feed == -1) {
1294 $match_part = "marked = true";
1295 } else if ($n_feed == -2) {
1296 $match_part = "published = true";
1297 } else if ($n_feed == -3) {
1298 $match_part = "unread = true AND score >= 0";
1300 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
1302 if (DB_TYPE
== "pgsql") {
1303 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1305 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1308 $need_entries = true;
1310 } else if ($n_feed == -4) {
1311 $match_part = "true";
1312 } else if ($n_feed >= 0) {
1315 $match_part = "feed_id = '$n_feed'";
1317 $match_part = "feed_id IS NULL";
1320 } else if ($feed < LABEL_BASE_INDEX
) {
1322 $label_id = feed_to_label_id($feed);
1324 return getLabelUnread($link, $label_id, $owner_uid);
1330 if ($need_entries) {
1331 $from_qpart = "ttrss_user_entries,ttrss_entries";
1332 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1334 $from_qpart = "ttrss_user_entries";
1337 $query = "SELECT count(int_id) AS unread
1338 FROM $from_qpart WHERE
1339 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1341 //echo "[$feed/$query]\n";
1343 $result = db_query($link, $query);
1347 $result = db_query($link, "SELECT COUNT(post_int_id) AS unread
1348 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1349 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1350 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1353 $unread = db_fetch_result($result, 0, "unread");
1358 function getGlobalUnread($link, $user_id = false) {
1361 $user_id = $_SESSION["uid"];
1364 $result = db_query($link, "SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1365 WHERE owner_uid = '$user_id' AND feed_id > 0");
1367 $c_id = db_fetch_result($result, 0, "c_id");
1372 function getGlobalCounters($link, $global_unread = -1) {
1375 if ($global_unread == -1) {
1376 $global_unread = getGlobalUnread($link);
1379 $cv = array("id" => "global-unread",
1380 "counter" => (int) $global_unread);
1382 array_push($ret_arr, $cv);
1384 $result = db_query($link, "SELECT COUNT(id) AS fn FROM
1385 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1387 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1389 $cv = array("id" => "subscribed-feeds",
1390 "counter" => (int) $subscribed_feeds);
1392 array_push($ret_arr, $cv);
1397 function getVirtCounters($link) {
1401 for ($i = 0; $i >= -4; $i--) {
1403 $count = getFeedUnread($link, $i);
1405 $cv = array("id" => $i,
1406 "counter" => (int) $count);
1408 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1409 // $cv["xmsg"] = getFeedArticles($link, $i)." ".__("total");
1411 array_push($ret_arr, $cv);
1417 $feeds = $pluginhost->get_feeds(-1);
1419 if (is_array($feeds)) {
1420 foreach ($feeds as $feed) {
1421 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1422 "counter" => $feed['sender']->get_unread($feed['id']));
1424 array_push($ret_arr, $cv);
1432 function getLabelCounters($link, $descriptions = false) {
1436 $owner_uid = $_SESSION["uid"];
1438 $result = db_query($link, "SELECT id,caption,COUNT(unread) AS unread
1439 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1440 (ttrss_labels2.id = label_id)
1441 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true)
1442 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1443 ttrss_labels2.caption");
1445 while ($line = db_fetch_assoc($result)) {
1447 $id = label_to_feed_id($line["id"]);
1449 $label_name = $line["caption"];
1450 $count = $line["unread"];
1452 $cv = array("id" => $id,
1453 "counter" => (int) $count);
1456 $cv["description"] = $label_name;
1458 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1459 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1461 array_push($ret_arr, $cv);
1467 function getFeedCounters($link, $active_feed = false) {
1471 $query = "SELECT ttrss_feeds.id,
1473 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1474 last_error, value AS count
1475 FROM ttrss_feeds, ttrss_counters_cache
1476 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1477 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1478 AND ttrss_counters_cache.feed_id = id";
1480 $result = db_query($link, $query);
1481 $fctrs_modified = false;
1483 while ($line = db_fetch_assoc($result)) {
1486 $count = $line["count"];
1487 $last_error = htmlspecialchars($line["last_error"]);
1489 $last_updated = make_local_datetime($link, $line['last_updated'], false);
1491 $has_img = feed_has_icon($id);
1493 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1496 $cv = array("id" => $id,
1497 "updated" => $last_updated,
1498 "counter" => (int) $count,
1499 "has_img" => (int) $has_img);
1502 $cv["error"] = $last_error;
1504 // if (get_pref($link, 'EXTENDED_FEEDLIST'))
1505 // $cv["xmsg"] = getFeedArticles($link, $id)." ".__("total");
1507 if ($active_feed && $id == $active_feed)
1508 $cv["title"] = truncate_string($line["title"], 30);
1510 array_push($ret_arr, $cv);
1517 function get_pgsql_version($link) {
1518 $result = db_query($link, "SELECT version() AS version");
1519 $version = explode(" ", db_fetch_result($result, 0, "version"));
1524 * @return array (code => Status code, message => error message if available)
1526 * 0 - OK, Feed already exists
1527 * 1 - OK, Feed added
1529 * 3 - URL content is HTML, no feeds available
1530 * 4 - URL content is HTML which contains multiple feeds.
1531 * Here you should call extractfeedurls in rpc-backend
1532 * to get all possible feeds.
1533 * 5 - Couldn't download the URL content.
1535 function subscribe_to_feed($link, $url, $cat_id = 0,
1536 $auth_login = '', $auth_pass = '') {
1538 global $fetch_last_error;
1540 require_once "include/rssfuncs.php";
1542 $url = fix_url($url);
1544 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1546 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1549 return array("code" => 5, "message" => $fetch_last_error);
1552 if (is_html($contents)) {
1553 $feedUrls = get_feeds_from_html($url, $contents);
1555 if (count($feedUrls) == 0) {
1556 return array("code" => 3);
1557 } else if (count($feedUrls) > 1) {
1558 return array("code" => 4, "feeds" => $feedUrls);
1560 //use feed url as new URL
1561 $url = key($feedUrls);
1564 if ($cat_id == "0" ||
!$cat_id) {
1565 $cat_qpart = "NULL";
1567 $cat_qpart = "'$cat_id'";
1570 $result = db_query($link,
1571 "SELECT id FROM ttrss_feeds
1572 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1574 if (db_num_rows($result) == 0) {
1575 $result = db_query($link,
1576 "INSERT INTO ttrss_feeds
1577 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method)
1578 VALUES ('".$_SESSION["uid"]."', '$url',
1579 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0)");
1581 $result = db_query($link,
1582 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1583 AND owner_uid = " . $_SESSION["uid"]);
1585 $feed_id = db_fetch_result($result, 0, "id");
1588 update_rss_feed($link, $feed_id, true);
1591 return array("code" => 1);
1593 return array("code" => 0);
1597 function print_feed_select($link, $id, $default_id = "",
1598 $attributes = "", $include_all_feeds = true,
1599 $root_id = false, $nest_level = 0) {
1602 print "<select id=\"$id\" name=\"$id\" $attributes>";
1603 if ($include_all_feeds) {
1604 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1605 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1609 if (get_pref($link, 'ENABLE_FEED_CATS')) {
1612 $parent_qpart = "parent_cat = '$root_id'";
1614 $parent_qpart = "parent_cat IS NULL";
1616 $result = db_query($link, "SELECT id,title,
1617 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1618 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1619 FROM ttrss_feed_categories
1620 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1622 while ($line = db_fetch_assoc($result)) {
1624 for ($i = 0; $i < $nest_level; $i++
)
1625 $line["title"] = " - " . $line["title"];
1627 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1629 printf("<option $is_selected value='CAT:%d'>%s</option>",
1630 $line["id"], htmlspecialchars($line["title"]));
1632 if ($line["num_children"] > 0)
1633 print_feed_select($link, $id, $default_id, $attributes,
1634 $include_all_feeds, $line["id"], $nest_level+
1);
1636 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1637 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1639 while ($fline = db_fetch_assoc($feed_result)) {
1640 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1642 $fline["title"] = " + " . $fline["title"];
1644 for ($i = 0; $i < $nest_level; $i++
)
1645 $fline["title"] = " - " . $fline["title"];
1647 printf("<option $is_selected value='%d'>%s</option>",
1648 $fline["id"], htmlspecialchars($fline["title"]));
1653 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1655 printf("<option $is_selected value='CAT:0'>%s</option>",
1656 __("Uncategorized"));
1658 $feed_result = db_query($link, "SELECT id,title FROM ttrss_feeds
1659 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1661 while ($fline = db_fetch_assoc($feed_result)) {
1662 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1664 $fline["title"] = " + " . $fline["title"];
1666 for ($i = 0; $i < $nest_level; $i++
)
1667 $fline["title"] = " - " . $fline["title"];
1669 printf("<option $is_selected value='%d'>%s</option>",
1670 $fline["id"], htmlspecialchars($fline["title"]));
1675 $result = db_query($link, "SELECT id,title FROM ttrss_feeds
1676 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1678 while ($line = db_fetch_assoc($result)) {
1680 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1682 printf("<option $is_selected value='%d'>%s</option>",
1683 $line["id"], htmlspecialchars($line["title"]));
1692 function print_feed_cat_select($link, $id, $default_id,
1693 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1696 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1700 $parent_qpart = "parent_cat = '$root_id'";
1702 $parent_qpart = "parent_cat IS NULL";
1704 $result = db_query($link, "SELECT id,title,
1705 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1706 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1707 FROM ttrss_feed_categories
1708 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1710 while ($line = db_fetch_assoc($result)) {
1711 if ($line["id"] == $default_id) {
1712 $is_selected = "selected=\"1\"";
1717 for ($i = 0; $i < $nest_level; $i++
)
1718 $line["title"] = " - " . $line["title"];
1721 printf("<option $is_selected value='%d'>%s</option>",
1722 $line["id"], htmlspecialchars($line["title"]));
1724 if ($line["num_children"] > 0)
1725 print_feed_cat_select($link, $id, $default_id, $attributes,
1726 $include_all_cats, $line["id"], $nest_level+
1);
1730 if ($include_all_cats) {
1731 if (db_num_rows($result) > 0) {
1732 print "<option disabled=\"1\">--------</option>";
1735 if ($default_id == 0) {
1736 $is_selected = "selected=\"1\"";
1741 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1747 function checkbox_to_sql_bool($val) {
1748 return ($val == "on") ?
"true" : "false";
1751 function getFeedCatTitle($link, $id) {
1753 return __("Special");
1754 } else if ($id < LABEL_BASE_INDEX
) {
1755 return __("Labels");
1756 } else if ($id > 0) {
1757 $result = db_query($link, "SELECT ttrss_feed_categories.title
1758 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1759 cat_id = ttrss_feed_categories.id");
1760 if (db_num_rows($result) == 1) {
1761 return db_fetch_result($result, 0, "title");
1763 return __("Uncategorized");
1766 return "getFeedCatTitle($id) failed";
1771 function getFeedIcon($id) {
1774 return "images/archive.png";
1777 return "images/mark_set.svg";
1780 return "images/pub_set.svg";
1783 return "images/fresh.png";
1786 return "images/tag.png";
1789 return "images/recently_read.png";
1792 if ($id < LABEL_BASE_INDEX
) {
1793 return "images/label.png";
1795 if (file_exists(ICONS_DIR
. "/$id.ico"))
1796 return ICONS_URL
. "/$id.ico";
1802 function getFeedTitle($link, $id, $cat = false) {
1804 return getCategoryTitle($link, $id);
1805 } else if ($id == -1) {
1806 return __("Starred articles");
1807 } else if ($id == -2) {
1808 return __("Published articles");
1809 } else if ($id == -3) {
1810 return __("Fresh articles");
1811 } else if ($id == -4) {
1812 return __("All articles");
1813 } else if ($id === 0 ||
$id === "0") {
1814 return __("Archived articles");
1815 } else if ($id == -6) {
1816 return __("Recently read");
1817 } else if ($id < LABEL_BASE_INDEX
) {
1818 $label_id = feed_to_label_id($id);
1819 $result = db_query($link, "SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1820 if (db_num_rows($result) == 1) {
1821 return db_fetch_result($result, 0, "caption");
1823 return "Unknown label ($label_id)";
1826 } else if (is_numeric($id) && $id > 0) {
1827 $result = db_query($link, "SELECT title FROM ttrss_feeds WHERE id = '$id'");
1828 if (db_num_rows($result) == 1) {
1829 return db_fetch_result($result, 0, "title");
1831 return "Unknown feed ($id)";
1838 function make_init_params($link) {
1841 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1842 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1843 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE", "DEFAULT_ARTICLE_LIMIT",
1844 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1846 $params[strtolower($param)] = (int) get_pref($link, $param);
1849 $params["icons_url"] = ICONS_URL
;
1850 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1851 $params["default_view_mode"] = get_pref($link, "_DEFAULT_VIEW_MODE");
1852 $params["default_view_limit"] = (int) get_pref($link, "_DEFAULT_VIEW_LIMIT");
1853 $params["default_view_order_by"] = get_pref($link, "_DEFAULT_VIEW_ORDER_BY");
1854 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1855 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1857 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1858 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1860 $max_feed_id = db_fetch_result($result, 0, "mid");
1861 $num_feeds = db_fetch_result($result, 0, "nf");
1863 $params["max_feed_id"] = (int) $max_feed_id;
1864 $params["num_feeds"] = (int) $num_feeds;
1866 $params["collapsed_feedlist"] = (int) get_pref($link, "_COLLAPSED_FEEDLIST");
1867 $params["hotkeys"] = get_hotkeys_map($link);
1869 $params["csrf_token"] = $_SESSION["csrf_token"];
1870 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1872 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1877 function get_hotkeys_info($link) {
1879 __("Navigation") => array(
1880 "next_feed" => __("Open next feed"),
1881 "prev_feed" => __("Open previous feed"),
1882 "next_article" => __("Open next article"),
1883 "prev_article" => __("Open previous article"),
1884 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1885 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1886 "search_dialog" => __("Show search dialog")),
1887 __("Article") => array(
1888 "toggle_mark" => __("Toggle starred"),
1889 "toggle_publ" => __("Toggle published"),
1890 "toggle_unread" => __("Toggle unread"),
1891 "edit_tags" => __("Edit tags"),
1892 "dismiss_selected" => __("Dismiss selected"),
1893 "dismiss_read" => __("Dismiss read"),
1894 "open_in_new_window" => __("Open in new window"),
1895 "catchup_below" => __("Mark below as read"),
1896 "catchup_above" => __("Mark above as read"),
1897 "article_scroll_down" => __("Scroll down"),
1898 "article_scroll_up" => __("Scroll up"),
1899 "select_article_cursor" => __("Select article under cursor"),
1900 "email_article" => __("Email article"),
1901 "close_article" => __("Close/collapse article"),
1902 "toggle_widescreen" => __("Toggle widescreen mode"),
1903 "toggle_embed_original" => __("Toggle embed original")),
1904 __("Article selection") => array(
1905 "select_all" => __("Select all articles"),
1906 "select_unread" => __("Select unread"),
1907 "select_marked" => __("Select starred"),
1908 "select_published" => __("Select published"),
1909 "select_invert" => __("Invert selection"),
1910 "select_none" => __("Deselect everything")),
1911 __("Feed") => array(
1912 "feed_refresh" => __("Refresh current feed"),
1913 "feed_unhide_read" => __("Un/hide read feeds"),
1914 "feed_subscribe" => __("Subscribe to feed"),
1915 "feed_edit" => __("Edit feed"),
1916 "feed_catchup" => __("Mark as read"),
1917 "feed_reverse" => __("Reverse headlines"),
1918 "feed_debug_update" => __("Debug feed update"),
1919 "catchup_all" => __("Mark all feeds as read"),
1920 "cat_toggle_collapse" => __("Un/collapse current category"),
1921 "toggle_combined_mode" => __("Toggle combined mode"),
1922 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1923 __("Go to") => array(
1924 "goto_all" => __("All articles"),
1925 "goto_fresh" => __("Fresh"),
1926 "goto_marked" => __("Starred"),
1927 "goto_published" => __("Published"),
1928 "goto_tagcloud" => __("Tag cloud"),
1929 "goto_prefs" => __("Preferences")),
1930 __("Other") => array(
1931 "create_label" => __("Create label"),
1932 "create_filter" => __("Create filter"),
1933 "collapse_sidebar" => __("Un/collapse sidebar"),
1934 "help_dialog" => __("Show help dialog"))
1938 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_INFO
) as $plugin) {
1939 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
1945 function get_hotkeys_map($link) {
1947 // "navigation" => array(
1950 "n" => "next_article",
1951 "p" => "prev_article",
1952 "(38)|up" => "prev_article",
1953 "(40)|down" => "next_article",
1954 // "^(38)|Ctrl-up" => "prev_article_noscroll",
1955 // "^(40)|Ctrl-down" => "next_article_noscroll",
1956 "(191)|/" => "search_dialog",
1957 // "article" => array(
1958 "s" => "toggle_mark",
1959 "*s" => "toggle_publ",
1960 "u" => "toggle_unread",
1961 "*t" => "edit_tags",
1962 "*d" => "dismiss_selected",
1963 "*x" => "dismiss_read",
1964 "o" => "open_in_new_window",
1965 "c p" => "catchup_below",
1966 "c n" => "catchup_above",
1967 "*n" => "article_scroll_down",
1968 "*p" => "article_scroll_up",
1969 "*(38)|Shift+up" => "article_scroll_up",
1970 "*(40)|Shift+down" => "article_scroll_down",
1971 "a *w" => "toggle_widescreen",
1972 "a e" => "toggle_embed_original",
1973 "e" => "email_article",
1974 "a q" => "close_article",
1975 // "article_selection" => array(
1976 "a a" => "select_all",
1977 "a u" => "select_unread",
1978 "a *u" => "select_marked",
1979 "a p" => "select_published",
1980 "a i" => "select_invert",
1981 "a n" => "select_none",
1983 "f r" => "feed_refresh",
1984 "f a" => "feed_unhide_read",
1985 "f s" => "feed_subscribe",
1986 "f e" => "feed_edit",
1987 "f q" => "feed_catchup",
1988 "f x" => "feed_reverse",
1989 "f *d" => "feed_debug_update",
1990 "f *c" => "toggle_combined_mode",
1991 "f c" => "toggle_cdm_expanded",
1992 "*q" => "catchup_all",
1993 "x" => "cat_toggle_collapse",
1995 "g a" => "goto_all",
1996 "g f" => "goto_fresh",
1997 "g s" => "goto_marked",
1998 "g p" => "goto_published",
1999 "g t" => "goto_tagcloud",
2000 "g *p" => "goto_prefs",
2001 // "other" => array(
2002 "(9)|Tab" => "select_article_cursor", // tab
2003 "c l" => "create_label",
2004 "c f" => "create_filter",
2005 "c s" => "collapse_sidebar",
2006 "^(191)|Ctrl+/" => "help_dialog",
2009 if (get_pref($link, 'COMBINED_DISPLAY_MODE')) {
2010 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2011 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2015 foreach ($pluginhost->get_hooks($pluginhost::HOOK_HOTKEY_MAP
) as $plugin) {
2016 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2019 $prefixes = array();
2021 foreach (array_keys($hotkeys) as $hotkey) {
2022 $pair = explode(" ", $hotkey, 2);
2024 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2025 array_push($prefixes, $pair[0]);
2029 return array($prefixes, $hotkeys);
2032 function make_runtime_info($link) {
2035 $result = db_query($link, "SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2036 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2038 $max_feed_id = db_fetch_result($result, 0, "mid");
2039 $num_feeds = db_fetch_result($result, 0, "nf");
2041 $data["max_feed_id"] = (int) $max_feed_id;
2042 $data["num_feeds"] = (int) $num_feeds;
2044 $data['last_article_id'] = getLastArticleId($link);
2045 $data['cdm_expanded'] = get_pref($link, 'CDM_EXPANDED');
2047 $data['dep_ts'] = calculate_dep_timestamp();
2048 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2050 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2052 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2054 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2056 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2059 $stamp_delta = time() - $stamp;
2061 if ($stamp_delta > 1800) {
2065 $_SESSION["daemon_stamp_check"] = time();
2068 $data['daemon_stamp_ok'] = $stamp_check;
2070 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2072 $data['daemon_stamp'] = $stamp_fmt;
2077 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2078 $new_version_details = @check_for_update
($link);
2080 $data['new_version_available'] = (int) ($new_version_details != false);
2082 $_SESSION["last_version_check"] = time();
2083 $_SESSION["version_data"] = $new_version_details;
2089 function search_to_sql($link, $search) {
2091 $search_query_part = "";
2093 $keywords = explode(" ", $search);
2094 $query_keywords = array();
2096 foreach ($keywords as $k) {
2097 if (strpos($k, "-") === 0) {
2104 $commandpair = explode(":", mb_strtolower($k), 2);
2106 if ($commandpair[0] == "note" && $commandpair[1]) {
2108 if ($commandpair[1] == "true")
2109 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2111 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2113 } else if ($commandpair[0] == "star" && $commandpair[1]) {
2115 if ($commandpair[1] == "true")
2116 array_push($query_keywords, "($not (marked = true))");
2118 array_push($query_keywords, "($not (marked = false))");
2120 } else if ($commandpair[0] == "pub" && $commandpair[1]) {
2122 if ($commandpair[1] == "true")
2123 array_push($query_keywords, "($not (published = true))");
2125 array_push($query_keywords, "($not (published = false))");
2127 } else if (strpos($k, "@") === 0) {
2129 $user_tz_string = get_pref($link, 'USER_TIMEZONE', $_SESSION['uid']);
2130 $orig_ts = strtotime(substr($k, 1));
2131 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2133 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2135 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2137 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2138 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2142 $search_query_part = implode("AND", $query_keywords);
2144 return $search_query_part;
2147 function getParentCategories($link, $cat, $owner_uid) {
2150 $result = db_query($link, "SELECT parent_cat FROM ttrss_feed_categories
2151 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2153 while ($line = db_fetch_assoc($result)) {
2154 array_push($rv, $line["parent_cat"]);
2155 $rv = array_merge($rv, getParentCategories($link, $line["parent_cat"], $owner_uid));
2161 function getChildCategories($link, $cat, $owner_uid) {
2164 $result = db_query($link, "SELECT id FROM ttrss_feed_categories
2165 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2167 while ($line = db_fetch_assoc($result)) {
2168 array_push($rv, $line["id"]);
2169 $rv = array_merge($rv, getChildCategories($link, $line["id"], $owner_uid));
2175 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) {
2177 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2179 $ext_tables_part = "";
2183 if (SPHINX_ENABLED
) {
2184 $ids = join(",", @sphinx_search
($search, 0, 500));
2187 $search_query_part = "ref_id IN ($ids) AND ";
2189 $search_query_part = "ref_id = -1 AND ";
2192 $search_query_part = search_to_sql($link, $search);
2193 $search_query_part .= " AND ";
2197 $search_query_part = "";
2202 if (DB_TYPE
== "pgsql") {
2203 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2205 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2208 $override_order = "updated DESC";
2210 $filter_query_part = filter_to_sql($link, $filter, $owner_uid);
2212 // Try to check if SQL regexp implementation chokes on a valid regexp
2213 $result = db_query($link, "SELECT true AS true_val FROM ttrss_entries,
2214 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2215 WHERE $filter_query_part LIMIT 1", false);
2218 $test = db_fetch_result($result, 0, "true_val");
2221 $filter_query_part = "false AND";
2223 $filter_query_part .= " AND";
2226 $filter_query_part = "false AND";
2230 $filter_query_part = "";
2234 $since_id_part = "ttrss_entries.id > $since_id AND ";
2236 $since_id_part = "";
2239 $view_query_part = "";
2241 if ($view_mode == "adaptive") {
2243 $view_query_part = " ";
2244 } else if ($feed != -1) {
2246 $unread = getFeedUnread($link, $feed, $cat_view);
2248 if ($cat_view && $feed > 0 && $include_children)
2249 $unread +
= getCategoryChildrenUnread($link, $feed);
2252 $view_query_part = " unread = true AND ";
2257 if ($view_mode == "marked") {
2258 $view_query_part = " marked = true AND ";
2261 if ($view_mode == "has_note") {
2262 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2265 if ($view_mode == "published") {
2266 $view_query_part = " published = true AND ";
2269 if ($view_mode == "unread" && $feed != -6) {
2270 $view_query_part = " unread = true AND ";
2274 $limit_query_part = "LIMIT " . $limit;
2277 $allow_archived = false;
2279 $vfeed_query_part = "";
2281 // override query strategy and enable feed display when searching globally
2282 if ($search && $search_mode == "all_feeds") {
2283 $query_strategy_part = "true";
2284 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2286 } else if (!is_numeric($feed)) {
2287 $query_strategy_part = "true";
2288 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2289 id = feed_id) as feed_title,";
2290 } else if ($search && $search_mode == "this_cat") {
2291 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2294 if ($include_children) {
2295 $subcats = getChildCategories($link, $feed, $owner_uid);
2296 array_push($subcats, $feed);
2297 $cats_qpart = join(",", $subcats);
2299 $cats_qpart = $feed;
2302 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2305 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2308 } else if ($feed > 0) {
2313 if ($include_children) {
2315 $subcats = getChildCategories($link, $feed, $owner_uid);
2317 array_push($subcats, $feed);
2318 $query_strategy_part = "cat_id IN (".
2319 implode(",", $subcats).")";
2322 $query_strategy_part = "cat_id = '$feed'";
2326 $query_strategy_part = "cat_id IS NULL";
2329 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2332 $query_strategy_part = "feed_id = '$feed'";
2334 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2335 $query_strategy_part = "feed_id IS NULL";
2336 $allow_archived = true;
2337 } else if ($feed == 0 && $cat_view) { // uncategorized
2338 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2339 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2340 } else if ($feed == -1) { // starred virtual feed
2341 $query_strategy_part = "marked = true";
2342 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2343 $allow_archived = true;
2345 if (!$override_order) {
2346 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2349 } else if ($feed == -2) { // published virtual feed OR labels category
2352 $query_strategy_part = "published = true";
2353 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2354 $allow_archived = true;
2356 if (!$override_order) {
2357 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2361 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2363 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2365 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2366 ttrss_user_labels2.article_id = ref_id";
2369 } else if ($feed == -6) { // recently read
2370 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2371 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2372 $allow_archived = true;
2374 if (!$override_order) $override_order = "last_read DESC";
2375 } else if ($feed == -3) { // fresh virtual feed
2376 $query_strategy_part = "unread = true AND score >= 0";
2378 $intl = get_pref($link, "FRESH_ARTICLE_MAX_AGE", $owner_uid);
2380 if (DB_TYPE
== "pgsql") {
2381 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2383 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2386 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2387 } else if ($feed == -4) { // all articles virtual feed
2388 $query_strategy_part = "true";
2389 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2390 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2391 $label_id = feed_to_label_id($feed);
2393 $query_strategy_part = "label_id = '$label_id' AND
2394 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2395 ttrss_user_labels2.article_id = ref_id";
2397 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2398 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2399 $allow_archived = true;
2402 $query_strategy_part = "true";
2405 if (get_pref($link, "SORT_HEADLINES_BY_FEED_DATE", $owner_uid)) {
2406 $date_sort_field = "updated";
2408 $date_sort_field = "date_entered";
2411 $order_by = "$date_sort_field DESC, updated DESC";
2413 if ($view_mode == "unread_first") {
2414 $order_by = "unread DESC, $order_by";
2417 if ($override_order) {
2418 $order_by = $override_order;
2424 $feed_title = T_sprintf("Search results: %s", $search);
2427 $feed_title = getCategoryTitle($link, $feed);
2429 if (is_numeric($feed) && $feed > 0) {
2430 $result = db_query($link, "SELECT title,site_url,last_error
2431 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2433 $feed_title = db_fetch_result($result, 0, "title");
2434 $feed_site_url = db_fetch_result($result, 0, "site_url");
2435 $last_error = db_fetch_result($result, 0, "last_error");
2437 $feed_title = getFeedTitle($link, $feed);
2442 $content_query_part = "content as content_preview, cached_content, ";
2444 if (is_numeric($feed)) {
2447 $feed_kind = "Feeds";
2449 $feed_kind = "Labels";
2452 if ($limit_query_part) {
2453 $offset_query_part = "OFFSET $offset";
2456 // proper override_order applied above
2457 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref($link, 'VFEED_GROUP_BY_FEED', $owner_uid)) {
2458 if (!$override_order) {
2459 $order_by = "ttrss_feeds.title, $order_by";
2461 $order_by = "ttrss_feeds.title, $override_order";
2465 if (!$allow_archived) {
2466 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2467 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2470 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2471 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2474 $query = "SELECT DISTINCT
2477 ttrss_entries.id,ttrss_entries.title,
2481 always_display_enclosures,
2488 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2489 last_marked, last_published,
2497 ttrss_user_entries.ref_id = ttrss_entries.id AND
2498 ttrss_user_entries.owner_uid = '$owner_uid' AND
2503 $query_strategy_part ORDER BY $order_by
2504 $limit_query_part $offset_query_part";
2506 if ($_REQUEST["debug"]) print $query;
2508 $result = db_query($link, $query);
2513 $select_qpart = "SELECT DISTINCT " .
2517 "ttrss_entries.id as id," .
2530 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2531 "last_marked, last_published, " .
2534 $content_query_part .
2537 $feed_kind = "Tags";
2538 $all_tags = explode(",", $feed);
2539 if ($search_mode == 'any') {
2540 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2541 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2542 $where_qpart = " WHERE " .
2543 "ref_id = ttrss_entries.id AND " .
2544 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2545 "post_int_id = int_id AND $tag_sql AND " .
2547 $search_query_part .
2548 $query_strategy_part . " ORDER BY $order_by " .
2553 $sub_selects = array();
2554 $sub_ands = array();
2555 foreach ($all_tags as $term) {
2556 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");
2563 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2568 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2569 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2570 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2571 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2573 // error_log("TAG SQL: " . $tag_sql);
2574 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2576 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2577 $result = db_query($link, $select_qpart . $from_qpart . $where_qpart);
2580 return array($result, $feed_title, $feed_site_url, $last_error);
2584 function sanitize($link, $str, $force_remove_images = false, $owner = false, $site_url = false) {
2585 if (!$owner) $owner = $_SESSION["uid"];
2587 $res = trim($str); if (!$res) return '';
2589 if (strpos($res, "href=") === false)
2590 $res = rewrite_urls($res);
2592 $charset_hack = '<head>
2593 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2596 $res = trim($res); if (!$res) return '';
2598 libxml_use_internal_errors(true);
2600 $doc = new DOMDocument();
2601 $doc->loadHTML($charset_hack . $res);
2602 $xpath = new DOMXPath($doc);
2604 $entries = $xpath->query('(//a[@href]|//img[@src])');
2606 foreach ($entries as $entry) {
2610 if ($entry->hasAttribute('href'))
2611 $entry->setAttribute('href',
2612 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2614 if ($entry->hasAttribute('src')) {
2615 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2617 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2619 if (file_exists($cached_filename)) {
2620 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2623 $entry->setAttribute('src', $src);
2626 if ($entry->nodeName
== 'img') {
2627 if (($owner && get_pref($link, "STRIP_IMAGES", $owner)) ||
2628 $force_remove_images ||
$_SESSION["bw_limit"]) {
2630 $p = $doc->createElement('p');
2632 $a = $doc->createElement('a');
2633 $a->setAttribute('href', $entry->getAttribute('src'));
2635 $a->appendChild(new DOMText($entry->getAttribute('src')));
2636 $a->setAttribute('target', '_blank');
2638 $p->appendChild($a);
2640 $entry->parentNode
->replaceChild($p, $entry);
2645 if (strtolower($entry->nodeName
) == "a") {
2646 $entry->setAttribute("target", "_blank");
2650 $entries = $xpath->query('//iframe');
2651 foreach ($entries as $entry) {
2652 $entry->setAttribute('sandbox', 'allow-scripts');
2656 $allowed_elements = array('a', 'address', 'audio', 'article',
2657 'b', 'big', 'blockquote', 'body', 'br', 'cite', 'center',
2658 'code', 'dd', 'del', 'details', 'div', 'dl', 'font',
2659 'dt', 'em', 'footer', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
2660 'header', 'html', 'i', 'img', 'ins', 'kbd',
2661 'li', 'nav', 'noscript', 'ol', 'p', 'pre', 'q', 's','small',
2662 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2663 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead',
2664 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2666 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2668 $disallowed_attributes = array('id', 'style', 'class');
2672 if (isset($pluginhost)) {
2673 foreach ($pluginhost->get_hooks($pluginhost::HOOK_SANITIZE
) as $plugin) {
2674 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2675 if (is_array($retval)) {
2677 $allowed_elements = $retval[1];
2678 $disallowed_attributes = $retval[2];
2685 $doc->removeChild($doc->firstChild
); //remove doctype
2686 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2687 $res = $doc->saveHTML();
2691 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2692 $entries = $doc->getElementsByTagName("*");
2694 foreach ($entries as $entry) {
2695 if (!in_array($entry->nodeName
, $allowed_elements)) {
2696 $entry->parentNode
->removeChild($entry);
2699 if ($entry->hasAttributes()) {
2700 $attrs_to_remove = array();
2702 foreach ($entry->attributes
as $attr) {
2704 if (strpos($attr->nodeName
, 'on') === 0) {
2705 array_push($attrs_to_remove, $attr);
2708 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2709 array_push($attrs_to_remove, $attr);
2713 foreach ($attrs_to_remove as $attr) {
2714 $entry->removeAttributeNode($attr);
2722 function check_for_update($link) {
2723 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2724 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2725 "&iid=" . sha1(SELF_URL_PATH
);
2727 $version_data = @fetch_file_contents
($version_url);
2729 if ($version_data) {
2730 $version_data = json_decode($version_data, true);
2731 if ($version_data && $version_data['version']) {
2733 if (version_compare(VERSION
, $version_data['version']) == -1) {
2734 return $version_data;
2742 function catchupArticlesById($link, $ids, $cmode, $owner_uid = false) {
2744 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2745 if (count($ids) == 0) return;
2749 foreach ($ids as $id) {
2750 array_push($tmp_ids, "ref_id = '$id'");
2753 $ids_qpart = join(" OR ", $tmp_ids);
2756 db_query($link, "UPDATE ttrss_user_entries SET
2757 unread = false,last_read = NOW()
2758 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2759 } else if ($cmode == 1) {
2760 db_query($link, "UPDATE ttrss_user_entries SET
2762 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2764 db_query($link, "UPDATE ttrss_user_entries SET
2765 unread = NOT unread,last_read = NOW()
2766 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2771 $result = db_query($link, "SELECT DISTINCT feed_id FROM ttrss_user_entries
2772 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2774 while ($line = db_fetch_assoc($result)) {
2775 ccache_update($link, $line["feed_id"], $owner_uid);
2779 function get_article_tags($link, $id, $owner_uid = 0, $tag_cache = false) {
2781 $a_id = db_escape_string($link, $id);
2783 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2785 $query = "SELECT DISTINCT tag_name,
2786 owner_uid as owner FROM
2787 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2788 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2790 $obj_id = md5("TAGS:$owner_uid:$id");
2793 /* check cache first */
2795 if ($tag_cache === false) {
2796 $result = db_query($link, "SELECT tag_cache FROM ttrss_user_entries
2797 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2799 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2803 $tags = explode(",", $tag_cache);
2806 /* do it the hard way */
2808 $tmp_result = db_query($link, $query);
2810 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2811 array_push($tags, $tmp_line["tag_name"]);
2814 /* update the cache */
2816 $tags_str = db_escape_string($link, join(",", $tags));
2818 db_query($link, "UPDATE ttrss_user_entries
2819 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2820 AND owner_uid = $owner_uid");
2826 function trim_array($array) {
2828 array_walk($tmp, 'trim');
2832 function tag_is_valid($tag) {
2833 if ($tag == '') return false;
2834 if (preg_match("/^[0-9]*$/", $tag)) return false;
2835 if (mb_strlen($tag) > 250) return false;
2837 if (function_exists('iconv')) {
2838 $tag = iconv("utf-8", "utf-8", $tag);
2841 if (!$tag) return false;
2846 function render_login_form($link) {
2847 require_once "login_form.php";
2851 // from http://developer.apple.com/internet/safari/faq.html
2852 function no_cache_incantation() {
2853 header("Expires: Mon, 22 Dec 1980 00:00:00 GMT"); // Happy birthday to me :)
2854 header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
2855 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); // HTTP/1.1
2856 header("Cache-Control: post-check=0, pre-check=0", false);
2857 header("Pragma: no-cache"); // HTTP/1.0
2860 function format_warning($msg, $id = "") {
2862 return "<div class=\"warning\" id=\"$id\">
2863 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2866 function format_notice($msg, $id = "") {
2868 return "<div class=\"notice\" id=\"$id\">
2869 <img src=\"images/sign_info.svg\"><div class='inner'>$msg</div></div>";
2872 function format_error($msg, $id = "") {
2874 return "<div class=\"error\" id=\"$id\">
2875 <img src=\"images/sign_excl.svg\"><div class='inner'>$msg</div></div>";
2878 function print_notice($msg) {
2879 return print format_notice($msg);
2882 function print_warning($msg) {
2883 return print format_warning($msg);
2886 function print_error($msg) {
2887 return print format_error($msg);
2891 function T_sprintf() {
2892 $args = func_get_args();
2893 return vsprintf(__(array_shift($args)), $args);
2896 function format_inline_player($link, $url, $ctype) {
2900 $url = htmlspecialchars($url);
2902 if (strpos($ctype, "audio/") === 0) {
2904 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2905 strpos($_SERVER['HTTP_USER_AGENT'], "Chrome") !== false ||
2906 strpos($_SERVER['HTTP_USER_AGENT'], "Safari") !== false )) {
2908 $id = 'AUDIO-' . uniqid();
2910 $entry .= "<audio id=\"$id\"\" controls style='display : none'>
2911 <source type=\"$ctype\" src=\"$url\"></source>
2914 $entry .= "<span onclick=\"player(this)\"
2915 title=\"".__("Click to play")."\" status=\"0\"
2916 class=\"player\" audio-id=\"$id\">".__("Play")."</span>";
2920 $entry .= "<object type=\"application/x-shockwave-flash\"
2921 data=\"lib/button/musicplayer.swf?song_url=$url\"
2922 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2923 <param name=\"movie\"
2924 value=\"lib/button/musicplayer.swf?song_url=$url\" />
2928 if ($entry) $entry .= " <a target=\"_blank\"
2929 href=\"$url\">" . basename($url) . "</a>";
2937 /* $filename = substr($url, strrpos($url, "/")+1);
2939 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
2940 $filename . " (" . $ctype . ")" . "</a>"; */
2944 function format_article($link, $id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
2945 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2951 /* we can figure out feed_id from article id anyway, why do we
2952 * pass feed_id here? let's ignore the argument :( */
2954 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
2955 WHERE ref_id = '$id'");
2957 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
2959 $rv['feed_id'] = $feed_id;
2961 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
2963 if ($mark_as_read) {
2964 $result = db_query($link, "UPDATE ttrss_user_entries
2965 SET unread = false,last_read = NOW()
2966 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2968 ccache_update($link, $feed_id, $owner_uid);
2971 $result = db_query($link, "SELECT id,title,link,content,feed_id,comments,int_id,
2972 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
2973 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
2974 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
2981 FROM ttrss_entries,ttrss_user_entries
2982 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
2986 $line = db_fetch_assoc($result);
2988 $tag_cache = $line["tag_cache"];
2990 $line["tags"] = get_article_tags($link, $id, $owner_uid, $line["tag_cache"]);
2991 unset($line["tag_cache"]);
2993 $line["content"] = sanitize($link, $line["content"], false, $owner_uid, $line["site_url"]);
2997 foreach ($pluginhost->get_hooks($pluginhost::HOOK_RENDER_ARTICLE
) as $p) {
2998 $line = $p->hook_render_article($line);
3001 $num_comments = $line["num_comments"];
3002 $entry_comments = "";
3004 if ($num_comments > 0) {
3005 if ($line["comments"]) {
3006 $comments_url = htmlspecialchars($line["comments"]);
3008 $comments_url = htmlspecialchars($line["link"]);
3010 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3012 if ($line["comments"] && $line["link"] != $line["comments"]) {
3013 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3018 header("Content-Type: text/html");
3019 $rv['content'] .= "<html><head>
3020 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3021 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3022 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3023 </head><body id=\"ttrssZoom\">";
3026 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3028 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3030 $entry_author = $line["author"];
3032 if ($entry_author) {
3033 $entry_author = __(" - ") . $entry_author;
3036 $parsed_updated = make_local_datetime($link, $line["updated"], true,
3039 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3041 if ($line["link"]) {
3042 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3043 title=\"".htmlspecialchars($line['title'])."\"
3045 htmlspecialchars($line["link"]) . "\">" .
3046 $line["title"] . "</a>" .
3047 "<span class='author'>$entry_author</span></div>";
3049 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3052 $tags_str = format_tags_string($line["tags"], $id);
3053 $tags_str_full = join(", ", $line["tags"]);
3055 if (!$tags_str_full) $tags_str_full = __("no tags");
3057 if (!$entry_comments) $entry_comments = " "; # placeholder
3059 $rv['content'] .= "<div class='postTags' style='float : right'>
3060 <img src='images/tag.png'
3061 class='tagsPic' alt='Tags' title='Tags'> ";
3064 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3065 <a title=\"".__('Edit tags for this article')."\"
3066 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3068 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3069 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3070 position=\"below\">$tags_str_full</div>";
3074 foreach ($pluginhost->get_hooks($pluginhost::HOOK_ARTICLE_BUTTON
) as $p) {
3075 $rv['content'] .= $p->hook_article_button($line);
3080 $tags_str = strip_tags($tags_str);
3081 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3083 $rv['content'] .= "</div>";
3084 $rv['content'] .= "<div clear='both'>$entry_comments</div>";
3086 if ($line["orig_feed_id"]) {
3088 $tmp_result = db_query($link, "SELECT * FROM ttrss_archived_feeds
3089 WHERE id = ".$line["orig_feed_id"]);
3091 if (db_num_rows($tmp_result) != 0) {
3093 $rv['content'] .= "<div clear='both'>";
3094 $rv['content'] .= __("Originally from:");
3096 $rv['content'] .= " ";
3098 $tmp_line = db_fetch_assoc($tmp_result);
3100 $rv['content'] .= "<a target='_blank'
3101 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3102 $tmp_line['title'] . "</a>";
3104 $rv['content'] .= " ";
3106 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3107 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3109 $rv['content'] .= "</div>";
3113 $rv['content'] .= "</div>";
3115 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3116 if ($line['note']) {
3117 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3119 $rv['content'] .= "</div>";
3121 $rv['content'] .= "<div class=\"postContent\">";
3123 $rv['content'] .= $line["content"];
3125 $rv['content'] .= format_article_enclosures($link, $id,
3126 $always_display_enclosures, $line["content"], $line["hide_images"]);
3128 $rv['content'] .= "</div>";
3130 $rv['content'] .= "</div>";
3136 <div class='footer'>
3137 <button onclick=\"return window.close()\">".
3138 __("Close this window")."</button></div>";
3139 $rv['content'] .= "</body></html>";
3146 function print_checkpoint($n, $s) {
3147 $ts = microtime(true);
3148 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3152 function sanitize_tag($tag) {
3155 $tag = mb_strtolower($tag, 'utf-8');
3157 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3159 // $tag = str_replace('"', "", $tag);
3160 // $tag = str_replace("+", " ", $tag);
3161 $tag = str_replace("technorati tag: ", "", $tag);
3166 function get_self_url_prefix() {
3167 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3168 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3170 return SELF_URL_PATH
;
3175 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3177 * @return string The Mozilla Firefox feed adding URL.
3179 function add_feed_url() {
3180 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3182 $url_path = get_self_url_prefix() .
3183 "/public.php?op=subscribe&feed_url=%s";
3185 } // function add_feed_url
3187 function encrypt_password($pass, $salt = '', $mode2 = false) {
3188 if ($salt && $mode2) {
3189 return "MODE2:" . hash('sha256', $salt . $pass);
3191 return "SHA1X:" . sha1("$salt:$pass");
3193 return "SHA1:" . sha1($pass);
3195 } // function encrypt_password
3197 function load_filters($link, $feed_id, $owner_uid, $action_id = false) {
3200 $cat_id = (int)getFeedCategory($link, $feed_id);
3202 $result = db_query($link, "SELECT * FROM ttrss_filters2 WHERE
3203 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3205 $check_cats = join(",", array_merge(
3206 getParentCategories($link, $cat_id, $owner_uid),
3209 while ($line = db_fetch_assoc($result)) {
3210 $filter_id = $line["id"];
3212 $result2 = db_query($link, "SELECT
3213 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3214 FROM ttrss_filters2_rules AS r,
3215 ttrss_filter_types AS t
3217 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3218 (feed_id IS NULL OR feed_id = '$feed_id') AND
3219 filter_type = t.id AND filter_id = '$filter_id'");
3224 while ($rule_line = db_fetch_assoc($result2)) {
3225 # print_r($rule_line);
3228 $rule["reg_exp"] = $rule_line["reg_exp"];
3229 $rule["type"] = $rule_line["type_name"];
3230 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3232 array_push($rules, $rule);
3235 $result2 = db_query($link, "SELECT a.action_param,t.name AS type_name
3236 FROM ttrss_filters2_actions AS a,
3237 ttrss_filter_actions AS t
3239 action_id = t.id AND filter_id = '$filter_id'");
3241 while ($action_line = db_fetch_assoc($result2)) {
3242 # print_r($action_line);
3245 $action["type"] = $action_line["type_name"];
3246 $action["param"] = $action_line["action_param"];
3248 array_push($actions, $action);
3253 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3254 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3255 $filter["rules"] = $rules;
3256 $filter["actions"] = $actions;
3258 if (count($rules) > 0 && count($actions) > 0) {
3259 array_push($filters, $filter);
3266 function get_score_pic($score) {
3268 return "score_high.png";
3269 } else if ($score > 0) {
3270 return "score_half_high.png";
3271 } else if ($score < -100) {
3272 return "score_low.png";
3273 } else if ($score < 0) {
3274 return "score_half_low.png";
3276 return "score_neutral.png";
3280 function feed_has_icon($id) {
3281 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3284 function init_connection($link) {
3287 if (DB_TYPE
== "pgsql") {
3288 pg_query($link, "set client_encoding = 'UTF-8'");
3289 pg_set_client_encoding("UNICODE");
3290 pg_query($link, "set datestyle = 'ISO, european'");
3291 pg_query($link, "set TIME ZONE 0");
3293 db_query($link, "SET time_zone = '+0:0'");
3295 if (defined('MYSQL_CHARSET') && MYSQL_CHARSET
) {
3296 db_query($link, "SET NAMES " . MYSQL_CHARSET
);
3302 $pluginhost = new PluginHost($link);
3303 $pluginhost->load(PLUGINS
, $pluginhost::KIND_ALL
);
3307 print "Unable to connect to database:" . db_last_error();
3312 function format_tags_string($tags, $id) {
3315 $tags_nolinks_str = "";
3321 $formatted_tags = array();
3323 foreach ($tags as $tag) {
3325 $tag_escaped = str_replace("'", "\\'", $tag);
3327 if (mb_strlen($tag) > 30) {
3328 $tag = truncate_string($tag, 30);
3331 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3333 array_push($formatted_tags, $tag_str);
3335 $tmp_tags_str = implode(", ", $formatted_tags);
3337 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3342 $tags_str = implode(", ", $formatted_tags);
3344 if ($num_tags < count($tags)) {
3345 $tags_str .= ", …";
3348 if ($num_tags == 0) {
3349 $tags_str = __("no tags");
3356 function format_article_labels($labels, $id) {
3360 foreach ($labels as $l) {
3361 $labels_str .= sprintf("<span class='hlLabelRef'
3362 style='color : %s; background-color : %s'>%s</span>",
3363 $l[2], $l[3], $l[1]);
3370 function format_article_note($id, $note, $allow_edit = true) {
3372 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3373 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3374 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3380 function get_feed_category($link, $feed_cat, $parent_cat_id = false) {
3381 if ($parent_cat_id) {
3382 $parent_qpart = "parent_cat = '$parent_cat_id'";
3383 $parent_insert = "'$parent_cat_id'";
3385 $parent_qpart = "parent_cat IS NULL";
3386 $parent_insert = "NULL";
3389 $result = db_query($link,
3390 "SELECT id FROM ttrss_feed_categories
3391 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3393 if (db_num_rows($result) == 0) {
3396 return db_fetch_result($result, 0, "id");
3400 function add_feed_category($link, $feed_cat, $parent_cat_id = false) {
3402 if (!$feed_cat) return false;
3404 db_query($link, "BEGIN");
3406 if ($parent_cat_id) {
3407 $parent_qpart = "parent_cat = '$parent_cat_id'";
3408 $parent_insert = "'$parent_cat_id'";
3410 $parent_qpart = "parent_cat IS NULL";
3411 $parent_insert = "NULL";
3414 $feed_cat = mb_substr($feed_cat, 0, 250);
3416 $result = db_query($link,
3417 "SELECT id FROM ttrss_feed_categories
3418 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3420 if (db_num_rows($result) == 0) {
3422 $result = db_query($link,
3423 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3424 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3426 db_query($link, "COMMIT");
3434 function getArticleFeed($link, $id) {
3435 $result = db_query($link, "SELECT feed_id FROM ttrss_user_entries
3436 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3438 if (db_num_rows($result) != 0) {
3439 return db_fetch_result($result, 0, "feed_id");
3446 * Fixes incomplete URLs by prepending "http://".
3447 * Also replaces feed:// with http://, and
3448 * prepends a trailing slash if the url is a domain name only.
3450 * @param string $url Possibly incomplete URL
3452 * @return string Fixed URL.
3454 function fix_url($url) {
3455 if (strpos($url, '://') === false) {
3456 $url = 'http://' . $url;
3457 } else if (substr($url, 0, 5) == 'feed:') {
3458 $url = 'http:' . substr($url, 5);
3461 //prepend slash if the URL has no slash in it
3462 // "http://www.example" -> "http://www.example/"
3463 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3467 if ($url != "http:///")
3473 function validate_feed_url($url) {
3474 $parts = parse_url($url);
3476 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3480 function get_article_enclosures($link, $id) {
3482 $query = "SELECT * FROM ttrss_enclosures
3483 WHERE post_id = '$id' AND content_url != ''";
3487 $result = db_query($link, $query);
3489 if (db_num_rows($result) > 0) {
3490 while ($line = db_fetch_assoc($result)) {
3491 array_push($rv, $line);
3498 function save_email_address($link, $email) {
3499 // FIXME: implement persistent storage of emails
3501 if (!$_SESSION['stored_emails'])
3502 $_SESSION['stored_emails'] = array();
3504 if (!in_array($email, $_SESSION['stored_emails']))
3505 array_push($_SESSION['stored_emails'], $email);
3509 function get_feed_access_key($link, $feed_id, $is_cat, $owner_uid = false) {
3511 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3513 $sql_is_cat = bool_to_sql_bool($is_cat);
3515 $result = db_query($link, "SELECT access_key FROM ttrss_access_keys
3516 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3517 AND owner_uid = " . $owner_uid);
3519 if (db_num_rows($result) == 1) {
3520 return db_fetch_result($result, 0, "access_key");
3522 $key = db_escape_string($link, sha1(uniqid(rand(), true)));
3524 $result = db_query($link, "INSERT INTO ttrss_access_keys
3525 (access_key, feed_id, is_cat, owner_uid)
3526 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3533 function get_feeds_from_html($url, $content)
3535 $url = fix_url($url);
3536 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3538 libxml_use_internal_errors(true);
3540 $doc = new DOMDocument();
3541 $doc->loadHTML($content);
3542 $xpath = new DOMXPath($doc);
3543 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3544 $feedUrls = array();
3545 foreach ($entries as $entry) {
3546 if ($entry->hasAttribute('href')) {
3547 $title = $entry->getAttribute('title');
3549 $title = $entry->getAttribute('type');
3551 $feedUrl = rewrite_relative_url(
3552 $baseUrl, $entry->getAttribute('href')
3554 $feedUrls[$feedUrl] = $title;
3560 function is_html($content) {
3561 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3564 function url_is_html($url, $login = false, $pass = false) {
3565 return is_html(fetch_file_contents($url, false, $login, $pass));
3568 function print_label_select($link, $name, $value, $attributes = "") {
3570 $result = db_query($link, "SELECT caption FROM ttrss_labels2
3571 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3573 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3574 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3576 while ($line = db_fetch_assoc($result)) {
3578 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3580 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3581 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3585 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3592 function format_article_enclosures($link, $id, $always_display_enclosures,
3593 $article_content, $hide_images = false) {
3595 $result = get_article_enclosures($link, $id);
3598 if (count($result) > 0) {
3600 $entries_html = array();
3602 $entries_inline = array();
3604 foreach ($result as $line) {
3606 $url = $line["content_url"];
3607 $ctype = $line["content_type"];
3609 if (!$ctype) $ctype = __("unknown type");
3611 $filename = substr($url, strrpos($url, "/")+
1);
3613 $player = format_inline_player($link, $url, $ctype);
3615 if ($player) array_push($entries_inline, $player);
3617 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3618 # $filename . " (" . $ctype . ")" . "</a>";
3620 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3621 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3623 array_push($entries_html, $entry);
3627 $entry["type"] = $ctype;
3628 $entry["filename"] = $filename;
3629 $entry["url"] = $url;
3631 array_push($entries, $entry);
3634 if ($_SESSION['uid'] && !get_pref($link, "STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3635 if ($always_display_enclosures ||
3636 !preg_match("/<img/i", $article_content)) {
3638 foreach ($entries as $entry) {
3640 if (preg_match("/image/", $entry["type"]) ||
3641 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3643 if (!$hide_images) {
3645 alt=\"".htmlspecialchars($entry["filename"])."\"
3646 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3648 $rv .= "<p><a target=\"_blank\"
3649 href=\"".htmlspecialchars($entry["url"])."\"
3650 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3658 if (count($entries_inline) > 0) {
3659 $rv .= "<hr clear='both'/>";
3660 foreach ($entries_inline as $entry) { $rv .= $entry; };
3661 $rv .= "<hr clear='both'/>";
3664 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3665 "<option value=''>" . __('Attachments')."</option>";
3667 foreach ($entries as $entry) {
3668 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3678 function getLastArticleId($link) {
3679 $result = db_query($link, "SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3680 WHERE owner_uid = " . $_SESSION["uid"]);
3682 if (db_num_rows($result) == 1) {
3683 return db_fetch_result($result, 0, "id");
3689 function build_url($parts) {
3690 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3694 * Converts a (possibly) relative URL to a absolute one.
3696 * @param string $url Base URL (i.e. from where the document is)
3697 * @param string $rel_url Possibly relative URL in the document
3699 * @return string Absolute URL
3701 function rewrite_relative_url($url, $rel_url) {
3702 if (strpos($rel_url, "magnet:") === 0) {
3704 } else if (strpos($rel_url, "://") !== false) {
3706 } else if (strpos($rel_url, "//") === 0) {
3707 # protocol-relative URL (rare but they exist)
3709 } else if (strpos($rel_url, "/") === 0)
3711 $parts = parse_url($url);
3712 $parts['path'] = $rel_url;
3714 return build_url($parts);
3717 $parts = parse_url($url);
3718 if (!isset($parts['path'])) {
3719 $parts['path'] = '/';
3721 $dir = $parts['path'];
3722 if (substr($dir, -1) !== '/') {
3723 $dir = dirname($parts['path']);
3724 $dir !== '/' && $dir .= '/';
3726 $parts['path'] = $dir . $rel_url;
3728 return build_url($parts);
3732 function sphinx_search($query, $offset = 0, $limit = 30) {
3733 require_once 'lib/sphinxapi.php';
3735 $sphinxClient = new SphinxClient();
3737 $sphinxClient->SetServer('localhost', 9312);
3738 $sphinxClient->SetConnectTimeout(1);
3740 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3741 'feed_title' => 20));
3743 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3744 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3745 $sphinxClient->SetLimits($offset, $limit, 1000);
3746 $sphinxClient->SetArrayResult(false);
3747 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3749 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3753 if (is_array($result['matches'])) {
3754 foreach (array_keys($result['matches']) as $int_id) {
3755 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3756 array_push($ids, $ref_id);
3763 function cleanup_tags($link, $days = 14, $limit = 1000) {
3765 if (DB_TYPE
== "pgsql") {
3766 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3767 } else if (DB_TYPE
== "mysql") {
3768 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3773 while ($limit > 0) {
3776 $query = "SELECT ttrss_tags.id AS id
3777 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3778 WHERE post_int_id = int_id AND $interval_query AND
3779 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3781 $result = db_query($link, $query);
3785 while ($line = db_fetch_assoc($result)) {
3786 array_push($ids, $line['id']);
3789 if (count($ids) > 0) {
3790 $ids = join(",", $ids);
3792 $tmp_result = db_query($link, "DELETE FROM ttrss_tags WHERE id IN ($ids)");
3793 $tags_deleted +
= db_affected_rows($link, $tmp_result);
3798 $limit -= $limit_part;
3801 return $tags_deleted;
3804 function print_user_stylesheet($link) {
3805 $value = get_pref($link, 'USER_STYLESHEET');
3808 print "<style type=\"text/css\">";
3809 print str_replace("<br/>", "\n", $value);
3815 function rewrite_urls($html) {
3816 libxml_use_internal_errors(true);
3818 $charset_hack = '<head>
3819 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3822 $doc = new DOMDocument();
3823 $doc->loadHTML($charset_hack . $html);
3824 $xpath = new DOMXPath($doc);
3826 $entries = $xpath->query('//*/text()');
3828 foreach ($entries as $entry) {
3829 if (strstr($entry->wholeText
, "://") !== false) {
3830 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3831 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3833 if ($text != $entry->wholeText
) {
3834 $cdoc = new DOMDocument();
3835 $cdoc->loadHTML($charset_hack . $text);
3838 foreach ($cdoc->childNodes
as $cnode) {
3839 $cnode = $doc->importNode($cnode, true);
3842 $entry->parentNode
->insertBefore($cnode);
3846 $entry->parentNode
->removeChild($entry);
3852 $node = $doc->getElementsByTagName('body')->item(0);
3854 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3856 return $doc->saveXML($node);
3861 function filter_to_sql($link, $filter, $owner_uid) {
3864 if (DB_TYPE
== "pgsql")
3867 $reg_qpart = "REGEXP";
3869 foreach ($filter["rules"] AS $rule) {
3870 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3871 $rule['reg_exp']) !== FALSE;
3873 if ($regexp_valid) {
3875 $rule['reg_exp'] = db_escape_string($link, $rule['reg_exp']);
3877 switch ($rule["type"]) {
3879 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3880 $rule['reg_exp'] . "')";
3883 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3884 $rule['reg_exp'] . "')";
3887 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3888 $rule['reg_exp'] . "') OR LOWER(" .
3889 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3892 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3893 $rule['reg_exp'] . "')";
3896 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3897 $rule['reg_exp'] . "')";
3900 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3901 $rule['reg_exp'] . "')";
3905 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3907 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3908 $qpart .= " AND feed_id = " . db_escape_string($link, $rule["feed_id"]);
3911 if (isset($rule["cat_id"])) {
3913 if ($rule["cat_id"] > 0) {
3914 $children = getChildCategories($link, $rule["cat_id"], $owner_uid);
3915 array_push($children, $rule["cat_id"]);
3917 $children = join(",", $children);
3919 $cat_qpart = "cat_id IN ($children)";
3921 $cat_qpart = "cat_id IS NULL";
3924 $qpart .= " AND $cat_qpart";
3927 array_push($query, "($qpart)");
3932 if (count($query) > 0) {
3933 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3935 $fullquery = "(false)";
3938 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
3943 if (!function_exists('gzdecode')) {
3944 function gzdecode($string) { // no support for 2nd argument
3945 return file_get_contents('compress.zlib://data:who/cares;base64,'.
3946 base64_encode($string));
3950 function get_random_bytes($length) {
3951 if (function_exists('openssl_random_pseudo_bytes')) {
3952 return openssl_random_pseudo_bytes($length);
3956 for ($i = 0; $i < $length; $i++
)
3957 $output .= chr(mt_rand(0, 255));
3963 function read_stdin() {
3964 $fp = fopen("php://stdin", "r");
3967 $line = trim(fgets($fp));
3975 function tmpdirname($path, $prefix) {
3976 // Use PHP's tmpfile function to create a temporary
3977 // directory name. Delete the file and keep the name.
3978 $tempname = tempnam($path,$prefix);
3982 if (!unlink($tempname))
3988 function getFeedCategory($link, $feed) {
3989 $result = db_query($link, "SELECT cat_id FROM ttrss_feeds
3990 WHERE id = '$feed'");
3992 if (db_num_rows($result) > 0) {
3993 return db_fetch_result($result, 0, "cat_id");
4000 function implements_interface($class, $interface) {
4001 return in_array($interface, class_implements($class));
4004 function geturl($url){
4006 (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');
4008 $curl = curl_init();
4009 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4010 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4011 $header[] = "Cache-Control: max-age=0";
4012 $header[] = "Connection: keep-alive";
4013 $header[] = "Keep-Alive: 300";
4014 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4015 $header[] = "Accept-Language: en-us,en;q=0.5";
4016 $header[] = "Pragma: ";
4018 curl_setopt($curl, CURLOPT_URL
, $url);
4019 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4020 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4021 curl_setopt($curl, CURLOPT_HEADER
, true);
4022 curl_setopt($curl, CURLOPT_REFERER
, $url);
4023 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4024 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4025 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4026 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4027 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4029 $html = curl_exec($curl);
4031 $status = curl_getinfo($curl);
4034 if($status['http_code']!=200){
4035 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4036 list($header) = explode("\r\n\r\n", $html, 2);
4038 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4039 $url = trim(str_replace($matches[1],"",$matches[0]));
4040 $url_parsed = parse_url($url);
4041 return (isset($url_parsed))?
geturl($url, $referer):'';
4044 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4045 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4046 # $handle = @fopen('./curl.error.log', 'a');
4047 # fwrite($handle, $line);
4053 function get_minified_js($files) {
4054 require_once 'lib/jshrink/Minifier.php';
4058 foreach ($files as $js) {
4059 if (!isset($_GET['debug'])) {
4060 $cached_file = CACHE_DIR
. "/js/$js.js";
4062 if (file_exists($cached_file) &&
4063 is_readable($cached_file) &&
4064 filemtime($cached_file) >= filemtime("js/$js.js")) {
4066 $rv .= file_get_contents($cached_file);
4069 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4070 file_put_contents($cached_file, $minified);
4074 $rv .= file_get_contents("js/$js.js");
4081 function stylesheet_tag($filename) {
4082 $timestamp = filemtime($filename);
4084 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4087 function javascript_tag($filename) {
4090 if (!(strpos($filename, "?") === FALSE)) {
4091 $query = substr($filename, strpos($filename, "?")+
1);
4092 $filename = substr($filename, 0, strpos($filename, "?"));
4095 $timestamp = filemtime($filename);
4097 if ($query) $timestamp .= "&$query";
4099 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4102 function calculate_dep_timestamp() {
4103 $files = array_merge(glob("js/*.js"), glob("*.css"));
4107 foreach ($files as $file) {
4108 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4114 function T_js_decl($s1, $s2) {
4116 $s1 = preg_replace("/\n/", "", $s1);
4117 $s2 = preg_replace("/\n/", "", $s2);
4119 $s1 = preg_replace("/\"/", "\\\"", $s1);
4120 $s2 = preg_replace("/\"/", "\\\"", $s2);
4122 return "T_messages[\"$s1\"] = \"$s2\";\n";
4126 function init_js_translations() {
4128 print 'var T_messages = new Object();
4131 if (T_messages[msg]) {
4132 return T_messages[msg];
4138 function ngettext(msg1, msg2, n) {
4139 return (parseInt(n) > 1) ? msg2 : msg1;
4142 $l10n = _get_reader();
4144 for ($i = 0; $i < $l10n->total
; $i++
) {
4145 $orig = $l10n->get_original_string($i);
4146 $translation = __($orig);
4148 print T_js_decl($orig, $translation);
4152 function label_to_feed_id($label) {
4153 return LABEL_BASE_INDEX
- 1 - abs($label);
4156 function feed_to_label_id($feed) {
4157 return LABEL_BASE_INDEX
- 1 +
abs($feed);