2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 118);
5 define('LABEL_BASE_INDEX', -1024);
6 define('PLUGIN_FEED_BASE_INDEX', -128);
8 $fetch_last_error = false;
9 $fetch_last_error_code = false;
10 $fetch_last_content_type = false;
11 $fetch_curl_used = false;
13 mb_internal_encoding("UTF-8");
14 date_default_timezone_set('UTC');
15 if (defined('E_DEPRECATED')) {
16 error_reporting(E_ALL
& ~E_NOTICE
& ~E_DEPRECATED
);
18 error_reporting(E_ALL
& ~E_NOTICE
);
21 require_once 'config.php';
24 * Define a constant if not already defined
26 * @param string $name The constant name.
27 * @param mixed $value The constant value.
29 * @return boolean True if defined successfully or not.
31 function define_default($name, $value) {
32 defined($name) or define($name, $value);
35 ///// Some defaults that you can override in config.php //////
37 define_default('FEED_FETCH_TIMEOUT', 45);
38 // How may seconds to wait for response when requesting feed from a site
39 define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
40 // How may seconds to wait for response when requesting feed from a
41 // site when that feed wasn't cached before
42 define_default('FILE_FETCH_TIMEOUT', 45);
43 // Default timeout when fetching files from remote sites
44 define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
45 // How many seconds to wait for initial response from website when
46 // fetching files from remote sites
48 if (DB_TYPE
== "pgsql") {
49 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
51 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
55 * Return available translations names.
58 * @return array A array of available translations.
60 function get_translations() {
62 "auto" => "Detect automatically",
68 "fr_FR" => "Français",
69 "hu_HU" => "Magyar (Hungarian)",
70 "it_IT" => "Italiano",
71 "ja_JP" => "日本語 (Japanese)",
72 "lv_LV" => "Latviešu",
73 "nb_NO" => "Norwegian bokmål",
77 "pt_BR" => "Portuguese/Brazil",
78 "zh_CN" => "Simplified Chinese",
85 require_once "lib/accept-to-gettext.php";
86 require_once "lib/gettext/gettext.inc";
89 function startup_gettext() {
91 # Get locale from Accept-Language header
92 $lang = al2gt(array_keys(get_translations()), "text/html");
94 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
95 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
98 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
99 $lang = $_SESSION["language"];
103 if (defined('LC_MESSAGES')) {
104 _setlocale(LC_MESSAGES
, $lang);
105 } else if (defined('LC_ALL')) {
106 _setlocale(LC_ALL
, $lang);
109 _bindtextdomain("messages", "locale");
111 _textdomain("messages");
112 _bind_textdomain_codeset("messages", "UTF-8");
118 require_once 'db-prefs.php';
119 require_once 'version.php';
120 require_once 'ccache.php';
121 require_once 'labels.php';
123 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
124 ini_set('user_agent', SELF_USER_AGENT
);
126 require_once 'lib/pubsubhubbub/publisher.php';
129 $utc_tz = new DateTimeZone('UTC');
130 $schema_version = false;
133 * Print a timestamped debug message.
135 * @param string $msg The debug message.
138 function _debug($msg) {
139 $ts = strftime("%H:%M:%S", time());
140 if (function_exists('posix_getpid')) {
141 $ts = "$ts/" . posix_getpid();
144 if (!(defined('QUIET') && QUIET
)) {
145 print "[$ts] $msg\n";
148 if (defined('LOGFILE')) {
149 $fp = fopen(LOGFILE
, 'a+');
152 fputs($fp, "[$ts] $msg\n");
160 * Purge a feed old posts.
162 * @param mixed $link A database connection.
163 * @param mixed $feed_id The id of the purged feed.
164 * @param mixed $purge_interval Olderness of purged posts.
165 * @param boolean $debug Set to True to enable the debug. False by default.
169 function purge_feed($feed_id, $purge_interval, $debug = false) {
171 if (!$purge_interval) $purge_interval = feed_purge_interval($feed_id);
176 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
180 if (db_num_rows($result) == 1) {
181 $owner_uid = db_fetch_result($result, 0, "owner_uid");
184 if ($purge_interval == -1 ||
!$purge_interval) {
186 ccache_update($feed_id, $owner_uid);
191 if (!$owner_uid) return;
193 if (FORCE_ARTICLE_PURGE
== 0) {
194 $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
197 $purge_unread = true;
198 $purge_interval = FORCE_ARTICLE_PURGE
;
201 if (!$purge_unread) $query_limit = " unread = false AND ";
203 if (DB_TYPE
== "pgsql") {
204 $pg_version = get_pgsql_version();
206 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
208 $result = db_query("DELETE FROM ttrss_user_entries WHERE
209 ttrss_entries.id = ref_id AND
211 feed_id = '$feed_id' AND
213 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
217 $result = db_query("DELETE FROM ttrss_user_entries
219 WHERE ttrss_entries.id = ref_id AND
221 feed_id = '$feed_id' AND
223 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
228 /* $result = db_query("DELETE FROM ttrss_user_entries WHERE
229 marked = false AND feed_id = '$feed_id' AND
230 (SELECT date_updated FROM ttrss_entries WHERE
231 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
233 $result = db_query("DELETE FROM ttrss_user_entries
234 USING ttrss_user_entries, ttrss_entries
235 WHERE ttrss_entries.id = ref_id AND
237 feed_id = '$feed_id' AND
239 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
242 $rows = db_affected_rows($result);
244 ccache_update($feed_id, $owner_uid);
247 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
251 } // function purge_feed
253 function feed_purge_interval($feed_id) {
255 $result = db_query("SELECT purge_interval, owner_uid FROM ttrss_feeds
256 WHERE id = '$feed_id'");
258 if (db_num_rows($result) == 1) {
259 $purge_interval = db_fetch_result($result, 0, "purge_interval");
260 $owner_uid = db_fetch_result($result, 0, "owner_uid");
262 if ($purge_interval == 0) $purge_interval = get_pref(
263 'PURGE_OLD_DAYS', $owner_uid);
265 return $purge_interval;
272 function purge_orphans($do_output = false) {
274 // purge orphaned posts in main content table
275 $result = db_query("DELETE FROM ttrss_entries WHERE
276 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
279 $rows = db_affected_rows($result);
280 _debug("Purged $rows orphaned posts.");
284 function get_feed_update_interval($feed_id) {
285 $result = db_query("SELECT owner_uid, update_interval FROM
286 ttrss_feeds WHERE id = '$feed_id'");
288 if (db_num_rows($result) == 1) {
289 $update_interval = db_fetch_result($result, 0, "update_interval");
290 $owner_uid = db_fetch_result($result, 0, "owner_uid");
292 if ($update_interval != 0) {
293 return $update_interval;
295 return get_pref('DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
303 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
305 global $fetch_last_error;
306 global $fetch_last_error_code;
307 global $fetch_last_content_type;
308 global $fetch_curl_used;
310 $url = str_replace(' ', '%20', $url);
312 if (!defined('NO_CURL') && function_exists('curl_init')) {
314 $fetch_curl_used = true;
316 if (ini_get("safe_mode") ||
ini_get("open_basedir")) {
317 $ch = curl_init(geturl($url));
319 $ch = curl_init($url);
323 curl_setopt($ch, CURLOPT_HTTPHEADER
,
324 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
327 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : FILE_FETCH_CONNECT_TIMEOUT
);
328 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : FILE_FETCH_TIMEOUT
);
329 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode") && !ini_get("open_basedir"));
330 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
331 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
332 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
333 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
334 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
335 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
336 curl_setopt($ch, CURLOPT_ENCODING
, "");
337 curl_setopt($ch, CURLOPT_REFERER
, $url);
340 curl_setopt($ch, CURLOPT_POST
, true);
341 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
345 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
347 $contents = @curl_exec
($ch);
349 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
350 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
351 $contents = @curl_exec
($ch);
354 if ($contents === false) {
355 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
360 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
361 $fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
363 $fetch_last_error_code = $http_code;
365 if ($http_code != 200 ||
$type && strpos($fetch_last_content_type, "$type") === false) {
366 if (curl_errno($ch) != 0) {
367 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
369 $fetch_last_error = "HTTP Code: $http_code";
380 $fetch_curl_used = false;
382 if ($login && $pass){
383 $url_parts = array();
385 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
387 $pass = urlencode($pass);
389 if ($url_parts[1] && $url_parts[2]) {
390 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
394 $data = @file_get_contents
($url);
396 $fetch_last_content_type = false; // reset if no type was sent from server
397 if (is_array($http_response_header)) {
398 foreach ($http_response_header as $h) {
399 if (substr(strtolower($h), 0, 13) == 'content-type:') {
400 $fetch_last_content_type = substr($h, 14);
401 // don't abort here b/c there might be more than one
402 // e.g. if we were being redirected -- last one is the right one
407 if (!$data && function_exists('error_get_last')) {
408 $error = error_get_last();
409 $fetch_last_error = $error["message"];
417 * Try to determine the favicon URL for a feed.
418 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
419 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
421 * @param string $url A feed or page URL
423 * @return mixed The favicon URL, or false if none was found.
425 function get_favicon_url($url) {
427 $favicon_url = false;
429 if ($html = @fetch_file_contents
($url)) {
431 libxml_use_internal_errors(true);
433 $doc = new DOMDocument();
434 $doc->loadHTML($html);
435 $xpath = new DOMXPath($doc);
437 $base = $xpath->query('/html/head/base');
438 foreach ($base as $b) {
439 $url = $b->getAttribute("href");
443 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
444 if (count($entries) > 0) {
445 foreach ($entries as $entry) {
446 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
453 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
456 } // function get_favicon_url
458 function check_feed_favicon($site_url, $feed) {
459 # print "FAVICON [$site_url]: $favicon_url\n";
461 $icon_file = ICONS_DIR
. "/$feed.ico";
463 if (!file_exists($icon_file)) {
464 $favicon_url = get_favicon_url($site_url);
467 // Limiting to "image" type misses those served with text/plain
468 $contents = fetch_file_contents($favicon_url); // , "image");
471 // Crude image type matching.
472 // Patterns gleaned from the file(1) source code.
473 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
474 // 0 string \000\000\001\000 MS Windows icon resource
475 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
477 elseif (preg_match('/^GIF8/', $contents)) {
478 // 0 string GIF8 GIF image data
479 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
481 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
482 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
483 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
485 elseif (preg_match('/^\xff\xd8/', $contents)) {
486 // 0 beshort 0xffd8 JPEG image data
487 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
490 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
496 $fp = @fopen
($icon_file, "w");
499 fwrite($fp, $contents);
501 chmod($icon_file, 0644);
509 function print_select($id, $default, $values, $attributes = "") {
510 print "<select name=\"$id\" id=\"$id\" $attributes>";
511 foreach ($values as $v) {
513 $sel = "selected=\"1\"";
519 print "<option value=\"$v\" $sel>$v</option>";
524 function print_select_hash($id, $default, $values, $attributes = "") {
525 print "<select name=\"$id\" id='$id' $attributes>";
526 foreach (array_keys($values) as $v) {
528 $sel = 'selected="selected"';
534 print "<option $sel value=\"$v\">".$values[$v]."</option>";
540 function print_radio($id, $default, $true_is, $values, $attributes = "") {
541 foreach ($values as $v) {
548 if ($v == $true_is) {
549 $sel .= " value=\"1\"";
551 $sel .= " value=\"0\"";
554 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
555 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
560 function initialize_user_prefs($uid, $profile = false) {
562 $uid = db_escape_string($uid);
566 $profile_qpart = "AND profile IS NULL";
568 $profile_qpart = "AND profile = '$profile'";
571 if (get_schema_version() < 63) $profile_qpart = "";
575 $result = db_query("SELECT pref_name,def_value FROM ttrss_prefs");
577 $u_result = db_query("SELECT pref_name
578 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
580 $active_prefs = array();
582 while ($line = db_fetch_assoc($u_result)) {
583 array_push($active_prefs, $line["pref_name"]);
586 while ($line = db_fetch_assoc($result)) {
587 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
588 // print "adding " . $line["pref_name"] . "<br>";
590 $line["def_value"] = db_escape_string($line["def_value"]);
591 $line["pref_name"] = db_escape_string($line["pref_name"]);
593 if (get_schema_version() < 63) {
594 db_query("INSERT INTO ttrss_user_prefs
595 (owner_uid,pref_name,value) VALUES
596 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
599 db_query("INSERT INTO ttrss_user_prefs
600 (owner_uid,pref_name,value, profile) VALUES
601 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
611 function get_ssl_certificate_id() {
612 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
613 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
614 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
615 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
616 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
621 function authenticate_user($login, $password, $check_only = false) {
623 if (!SINGLE_USER_MODE
) {
626 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_AUTH_USER
) as $plugin) {
628 $user_id = (int) $plugin->authenticate($login, $password);
631 $_SESSION["auth_module"] = strtolower(get_class($plugin));
636 if ($user_id && !$check_only) {
639 $_SESSION["uid"] = $user_id;
640 $_SESSION["version"] = VERSION
;
642 $result = db_query("SELECT login,access_level,pwd_hash FROM ttrss_users
643 WHERE id = '$user_id'");
645 $_SESSION["name"] = db_fetch_result($result, 0, "login");
646 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
647 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
649 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
652 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
653 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
654 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
656 $_SESSION["last_version_check"] = time();
658 initialize_user_prefs($_SESSION["uid"]);
667 $_SESSION["uid"] = 1;
668 $_SESSION["name"] = "admin";
669 $_SESSION["access_level"] = 10;
671 $_SESSION["hide_hello"] = true;
672 $_SESSION["hide_logout"] = true;
674 $_SESSION["auth_module"] = false;
676 if (!$_SESSION["csrf_token"]) {
677 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
680 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
682 initialize_user_prefs($_SESSION["uid"]);
688 function make_password($length = 8) {
691 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
695 while ($i < $length) {
696 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
698 if (!strstr($password, $char)) {
706 // this is called after user is created to initialize default feeds, labels
709 // user preferences are checked on every login, not here
711 function initialize_user($uid) {
713 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
714 values ('$uid', 'Tiny Tiny RSS: New Releases',
715 'http://tt-rss.org/releases.rss')");
717 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
718 values ('$uid', 'Tiny Tiny RSS: Forum',
719 'http://tt-rss.org/forum/rss.php')");
722 function logout_user() {
724 if (isset($_COOKIE[session_name()])) {
725 setcookie(session_name(), '', time()-42000, '/');
729 function validate_csrf($csrf_token) {
730 return $csrf_token == $_SESSION['csrf_token'];
733 function load_user_plugins($owner_uid) {
735 $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
737 PluginHost
::getInstance()->load($plugins, PluginHost
::KIND_USER
, $owner_uid);
739 if (get_schema_version() > 100) {
740 PluginHost
::getInstance()->load_data();
745 function login_sequence() {
746 if (SINGLE_USER_MODE
) {
748 authenticate_user("admin", null);
749 load_user_plugins($_SESSION["uid"]);
751 if (!validate_session()) $_SESSION["uid"] = false;
753 if (!$_SESSION["uid"]) {
755 if (AUTH_AUTO_LOGIN
&& authenticate_user(null, null)) {
756 $_SESSION["ref_schema_version"] = get_schema_version(true);
758 authenticate_user(null, null, true);
761 if (!$_SESSION["uid"]) {
763 setcookie(session_name(), '', time()-42000, '/');
770 /* bump login timestamp */
771 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
773 $_SESSION["last_login_update"] = time();
776 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
777 setcookie("ttrss_lang", $_SESSION["language"],
778 time() + SESSION_COOKIE_LIFETIME
);
781 if ($_SESSION["uid"]) {
782 load_user_plugins($_SESSION["uid"]);
786 db_query("DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
787 $_SESSION["uid"] . " AND
788 (SELECT COUNT(id) FROM ttrss_feeds WHERE
789 ttrss_feeds.id = feed_id) = 0");
791 db_query("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
792 $_SESSION["uid"] . " AND
793 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
794 ttrss_feed_categories.id = feed_id) = 0");
801 function truncate_string($str, $max_len, $suffix = '…') {
802 if (mb_strlen($str, "utf-8") > $max_len - 3) {
803 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
809 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
812 $source_tz = new DateTimeZone($source_tz);
813 } catch (Exception
$e) {
814 $source_tz = new DateTimeZone('UTC');
818 $dest_tz = new DateTimeZone($dest_tz);
819 } catch (Exception
$e) {
820 $dest_tz = new DateTimeZone('UTC');
823 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
824 return $dt->format('U') +
$dest_tz->getOffset($dt);
827 function make_local_datetime($timestamp, $long, $owner_uid = false,
828 $no_smart_dt = false) {
830 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
831 if (!$timestamp) $timestamp = '1970-01-01 0:00';
836 # We store date in UTC internally
837 $dt = new DateTime($timestamp, $utc_tz);
839 if ($tz_offset == -1) {
841 $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
844 $user_tz = new DateTimeZone($user_tz_string);
845 } catch (Exception
$e) {
849 $tz_offset = $user_tz->getOffset($dt);
852 $user_timestamp = $dt->format('U') +
$tz_offset;
855 return smart_date_time($user_timestamp,
856 $tz_offset, $owner_uid);
859 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
861 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
863 return date($format, $user_timestamp);
867 function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false) {
868 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
870 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
871 return date("G:i", $timestamp);
872 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
873 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
874 return date($format, $timestamp);
876 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
877 return date($format, $timestamp);
881 function sql_bool_to_bool($s) {
882 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
889 function bool_to_sql_bool($s) {
897 // Session caching removed due to causing wrong redirects to upgrade
898 // script when get_schema_version() is called on an obsolete session
899 // created on a previous schema version.
900 function get_schema_version($nocache = false) {
901 global $schema_version;
903 if (!$schema_version) {
904 $result = db_query("SELECT schema_version FROM ttrss_version");
905 $version = db_fetch_result($result, 0, "schema_version");
906 $schema_version = $version;
909 return $schema_version;
913 function sanity_check() {
914 require_once 'errors.php';
917 $schema_version = get_schema_version(true);
919 if ($schema_version != SCHEMA_VERSION
) {
923 if (DB_TYPE
== "mysql") {
924 $result = db_query("SELECT true", false);
925 if (db_num_rows($result) != 1) {
930 if (db_escape_string("testTEST") != "testTEST") {
934 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
937 function file_is_locked($filename) {
938 if (function_exists('flock')) {
939 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
941 if (flock($fp, LOCK_EX | LOCK_NB
)) {
952 return true; // consider the file always locked and skip the test
955 function make_lockfile($filename) {
956 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
958 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
959 if (function_exists('posix_getpid')) {
960 fwrite($fp, posix_getpid() . "\n");
968 function make_stampfile($filename) {
969 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
971 if (flock($fp, LOCK_EX | LOCK_NB
)) {
972 fwrite($fp, time() . "\n");
981 function sql_random_function() {
982 if (DB_TYPE
== "mysql") {
989 function catchup_feed($feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
991 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
993 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
995 // Todo: all this interval stuff needs some generic generator function
997 $date_qpart = "false";
1001 if (DB_TYPE
== "pgsql") {
1002 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
1004 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
1008 if (DB_TYPE
== "pgsql") {
1009 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
1011 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
1015 if (DB_TYPE
== "pgsql") {
1016 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
1018 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
1022 $date_qpart = "true";
1025 if (is_numeric($feed)) {
1031 $children = getChildCategories($feed, $owner_uid);
1032 array_push($children, $feed);
1034 $children = join(",", $children);
1036 $cat_qpart = "cat_id IN ($children)";
1038 $cat_qpart = "cat_id IS NULL";
1041 db_query("UPDATE ttrss_user_entries
1042 SET unread = false, last_read = NOW() WHERE ref_id IN
1044 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1045 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1046 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1048 } else if ($feed == -2) {
1050 db_query("UPDATE ttrss_user_entries
1051 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1052 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1053 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1056 } else if ($feed > 0) {
1058 db_query("UPDATE ttrss_user_entries
1059 SET unread = false, last_read = NOW() WHERE ref_id IN
1061 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1062 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1064 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1067 db_query("UPDATE ttrss_user_entries
1068 SET unread = false, last_read = NOW() WHERE ref_id IN
1070 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1071 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1075 db_query("UPDATE ttrss_user_entries
1076 SET unread = false, last_read = NOW() WHERE ref_id IN
1078 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1079 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1084 $intl = get_pref("FRESH_ARTICLE_MAX_AGE");
1086 if (DB_TYPE
== "pgsql") {
1087 $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
1089 $match_part = "date_entered > DATE_SUB(NOW(),
1090 INTERVAL $intl HOUR) ";
1093 db_query("UPDATE ttrss_user_entries
1094 SET unread = false, last_read = NOW() WHERE ref_id IN
1096 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1097 AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
1101 db_query("UPDATE ttrss_user_entries
1102 SET unread = false, last_read = NOW() WHERE ref_id IN
1104 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1105 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1108 } else if ($feed < LABEL_BASE_INDEX
) { // label
1110 $label_id = feed_to_label_id($feed);
1112 db_query("UPDATE ttrss_user_entries
1113 SET unread = false, last_read = NOW() WHERE ref_id IN
1115 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1116 AND label_id = '$label_id' AND ref_id = article_id
1117 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1121 ccache_update($feed, $owner_uid, $cat_view);
1124 db_query("UPDATE ttrss_user_entries
1125 SET unread = false, last_read = NOW() WHERE ref_id IN
1127 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1128 AND post_int_id = int_id AND tag_name = '$feed'
1129 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1134 function getAllCounters() {
1135 $data = getGlobalCounters();
1137 $data = array_merge($data, getVirtCounters());
1138 $data = array_merge($data, getLabelCounters());
1139 $data = array_merge($data, getFeedCounters($active_feed));
1140 $data = array_merge($data, getCategoryCounters());
1145 function getCategoryTitle($cat_id) {
1147 if ($cat_id == -1) {
1148 return __("Special");
1149 } else if ($cat_id == -2) {
1150 return __("Labels");
1153 $result = db_query("SELECT title FROM ttrss_feed_categories WHERE
1156 if (db_num_rows($result) == 1) {
1157 return db_fetch_result($result, 0, "title");
1159 return __("Uncategorized");
1165 function getCategoryCounters() {
1168 /* Labels category */
1170 $cv = array("id" => -2, "kind" => "cat",
1171 "counter" => getCategoryUnread(-2));
1173 array_push($ret_arr, $cv);
1175 $result = db_query("SELECT id AS cat_id, value AS unread,
1176 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1177 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1178 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1179 WHERE ttrss_cat_counters_cache.feed_id = id AND
1180 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1181 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1183 while ($line = db_fetch_assoc($result)) {
1184 $line["cat_id"] = (int) $line["cat_id"];
1186 if ($line["num_children"] > 0) {
1187 $child_counter = getCategoryChildrenUnread($line["cat_id"], $_SESSION["uid"]);
1192 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1193 "counter" => $line["unread"] +
$child_counter);
1195 array_push($ret_arr, $cv);
1198 /* Special case: NULL category doesn't actually exist in the DB */
1200 $cv = array("id" => 0, "kind" => "cat",
1201 "counter" => (int) ccache_find(0, $_SESSION["uid"], true));
1203 array_push($ret_arr, $cv);
1208 // only accepts real cats (>= 0)
1209 function getCategoryChildrenUnread($cat, $owner_uid = false) {
1210 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1212 $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1213 AND owner_uid = $owner_uid");
1217 while ($line = db_fetch_assoc($result)) {
1218 $unread +
= getCategoryUnread($line["id"], $owner_uid);
1219 $unread +
= getCategoryChildrenUnread($line["id"], $owner_uid);
1225 function getCategoryUnread($cat, $owner_uid = false) {
1227 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1232 $cat_query = "cat_id = '$cat'";
1234 $cat_query = "cat_id IS NULL";
1237 $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query
1238 AND owner_uid = " . $owner_uid);
1240 $cat_feeds = array();
1241 while ($line = db_fetch_assoc($result)) {
1242 array_push($cat_feeds, "feed_id = " . $line["id"]);
1245 if (count($cat_feeds) == 0) return 0;
1247 $match_part = implode(" OR ", $cat_feeds);
1249 $result = db_query("SELECT COUNT(int_id) AS unread
1250 FROM ttrss_user_entries
1251 WHERE unread = true AND ($match_part)
1252 AND owner_uid = " . $owner_uid);
1256 # this needs to be rewritten
1257 while ($line = db_fetch_assoc($result)) {
1258 $unread +
= $line["unread"];
1262 } else if ($cat == -1) {
1263 return getFeedUnread(-1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1264 } else if ($cat == -2) {
1266 $result = db_query("
1267 SELECT COUNT(unread) AS unread FROM
1268 ttrss_user_entries, ttrss_user_labels2
1269 WHERE article_id = ref_id AND unread = true
1270 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1272 $unread = db_fetch_result($result, 0, "unread");
1279 function getFeedUnread($feed, $is_cat = false) {
1280 return getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
1283 function getLabelUnread($label_id, $owner_uid = false) {
1284 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1286 $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1287 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1289 if (db_num_rows($result) != 0) {
1290 return db_fetch_result($result, 0, "unread");
1296 function getFeedArticles($feed, $is_cat = false, $unread_only = false,
1297 $owner_uid = false) {
1299 $n_feed = (int) $feed;
1300 $need_entries = false;
1302 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1305 $unread_qpart = "unread = true";
1307 $unread_qpart = "true";
1311 return getCategoryUnread($n_feed, $owner_uid);
1312 } else if ($n_feed == -6) {
1314 } else if ($feed != "0" && $n_feed == 0) {
1316 $feed = db_escape_string($feed);
1318 $result = db_query("SELECT SUM((SELECT COUNT(int_id)
1319 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1320 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1321 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1322 return db_fetch_result($result, 0, "count");
1324 } else if ($n_feed == -1) {
1325 $match_part = "marked = true";
1326 } else if ($n_feed == -2) {
1327 $match_part = "published = true";
1328 } else if ($n_feed == -3) {
1329 $match_part = "unread = true AND score >= 0";
1331 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1333 if (DB_TYPE
== "pgsql") {
1334 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1336 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1339 $need_entries = true;
1341 } else if ($n_feed == -4) {
1342 $match_part = "true";
1343 } else if ($n_feed >= 0) {
1346 $match_part = "feed_id = '$n_feed'";
1348 $match_part = "feed_id IS NULL";
1351 } else if ($feed < LABEL_BASE_INDEX
) {
1353 $label_id = feed_to_label_id($feed);
1355 return getLabelUnread($label_id, $owner_uid);
1361 if ($need_entries) {
1362 $from_qpart = "ttrss_user_entries,ttrss_entries";
1363 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1365 $from_qpart = "ttrss_user_entries";
1368 $query = "SELECT count(int_id) AS unread
1369 FROM $from_qpart WHERE
1370 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1372 //echo "[$feed/$query]\n";
1374 $result = db_query($query);
1378 $result = db_query("SELECT COUNT(post_int_id) AS unread
1379 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1380 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1381 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1384 $unread = db_fetch_result($result, 0, "unread");
1389 function getGlobalUnread($user_id = false) {
1392 $user_id = $_SESSION["uid"];
1395 $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1396 WHERE owner_uid = '$user_id' AND feed_id > 0");
1398 $c_id = db_fetch_result($result, 0, "c_id");
1403 function getGlobalCounters($global_unread = -1) {
1406 if ($global_unread == -1) {
1407 $global_unread = getGlobalUnread();
1410 $cv = array("id" => "global-unread",
1411 "counter" => (int) $global_unread);
1413 array_push($ret_arr, $cv);
1415 $result = db_query("SELECT COUNT(id) AS fn FROM
1416 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1418 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1420 $cv = array("id" => "subscribed-feeds",
1421 "counter" => (int) $subscribed_feeds);
1423 array_push($ret_arr, $cv);
1428 function getVirtCounters() {
1432 for ($i = 0; $i >= -4; $i--) {
1434 $count = getFeedUnread($i);
1436 $cv = array("id" => $i,
1437 "counter" => (int) $count);
1439 // if (get_pref('EXTENDED_FEEDLIST'))
1440 // $cv["xmsg"] = getFeedArticles($i)." ".__("total");
1442 array_push($ret_arr, $cv);
1445 $feeds = PluginHost
::getInstance()->get_feeds(-1);
1447 if (is_array($feeds)) {
1448 foreach ($feeds as $feed) {
1449 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1450 "counter" => $feed['sender']->get_unread($feed['id']));
1451 array_push($ret_arr, $cv);
1458 function getLabelCounters($descriptions = false) {
1462 $owner_uid = $_SESSION["uid"];
1464 $result = db_query("SELECT id,caption,COUNT(unread) AS unread
1465 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1466 (ttrss_labels2.id = label_id)
1467 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true
1468 AND ttrss_user_entries.owner_uid = $owner_uid)
1469 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1470 ttrss_labels2.caption");
1472 while ($line = db_fetch_assoc($result)) {
1474 $id = label_to_feed_id($line["id"]);
1476 $label_name = $line["caption"];
1477 $count = $line["unread"];
1479 $cv = array("id" => $id,
1480 "counter" => (int) $count);
1483 $cv["description"] = $label_name;
1485 // if (get_pref('EXTENDED_FEEDLIST'))
1486 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1488 array_push($ret_arr, $cv);
1494 function getFeedCounters($active_feed = false) {
1498 $query = "SELECT ttrss_feeds.id,
1500 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1501 last_error, value AS count
1502 FROM ttrss_feeds, ttrss_counters_cache
1503 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1504 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1505 AND ttrss_counters_cache.feed_id = id";
1507 $result = db_query($query);
1508 $fctrs_modified = false;
1510 while ($line = db_fetch_assoc($result)) {
1513 $count = $line["count"];
1514 $last_error = htmlspecialchars($line["last_error"]);
1516 $last_updated = make_local_datetime($line['last_updated'], false);
1518 $has_img = feed_has_icon($id);
1520 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1523 $cv = array("id" => $id,
1524 "updated" => $last_updated,
1525 "counter" => (int) $count,
1526 "has_img" => (int) $has_img);
1529 $cv["error"] = $last_error;
1531 // if (get_pref('EXTENDED_FEEDLIST'))
1532 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1534 if ($active_feed && $id == $active_feed)
1535 $cv["title"] = truncate_string($line["title"], 30);
1537 array_push($ret_arr, $cv);
1544 function get_pgsql_version() {
1545 $result = db_query("SELECT version() AS version");
1546 $version = explode(" ", db_fetch_result($result, 0, "version"));
1551 * @return array (code => Status code, message => error message if available)
1553 * 0 - OK, Feed already exists
1554 * 1 - OK, Feed added
1556 * 3 - URL content is HTML, no feeds available
1557 * 4 - URL content is HTML which contains multiple feeds.
1558 * Here you should call extractfeedurls in rpc-backend
1559 * to get all possible feeds.
1560 * 5 - Couldn't download the URL content.
1561 * 6 - Content is an invalid XML.
1563 function subscribe_to_feed($url, $cat_id = 0,
1564 $auth_login = '', $auth_pass = '') {
1566 global $fetch_last_error;
1568 require_once "include/rssfuncs.php";
1570 $url = fix_url($url);
1572 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1574 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1577 return array("code" => 5, "message" => $fetch_last_error);
1580 if (is_html($contents)) {
1581 $feedUrls = get_feeds_from_html($url, $contents);
1583 if (count($feedUrls) == 0) {
1584 return array("code" => 3);
1585 } else if (count($feedUrls) > 1) {
1586 return array("code" => 4, "feeds" => $feedUrls);
1588 //use feed url as new URL
1589 $url = key($feedUrls);
1592 libxml_use_internal_errors(true);
1593 $doc = new DOMDocument();
1594 $doc->loadXML(html_entity_decode($contents));
1595 $error = libxml_get_last_error();
1596 libxml_clear_errors();
1599 $error_message = format_libxml_error($error);
1601 return array("code" => 6, "message" => $error_message);
1604 if ($cat_id == "0" ||
!$cat_id) {
1605 $cat_qpart = "NULL";
1607 $cat_qpart = "'$cat_id'";
1611 "SELECT id FROM ttrss_feeds
1612 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1614 if (strlen(FEED_CRYPT_KEY
) > 0) {
1615 require_once "crypt.php";
1616 $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
1617 $auth_pass_encrypted = 'true';
1619 $auth_pass_encrypted = 'false';
1622 $auth_pass = db_escape_string($auth_pass);
1624 if (db_num_rows($result) == 0) {
1626 "INSERT INTO ttrss_feeds
1627 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
1628 VALUES ('".$_SESSION["uid"]."', '$url',
1629 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, $auth_pass_encrypted)");
1632 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1633 AND owner_uid = " . $_SESSION["uid"]);
1635 $feed_id = db_fetch_result($result, 0, "id");
1638 update_rss_feed($feed_id, true);
1641 return array("code" => 1);
1643 return array("code" => 0);
1647 function print_feed_select($id, $default_id = "",
1648 $attributes = "", $include_all_feeds = true,
1649 $root_id = false, $nest_level = 0) {
1652 print "<select id=\"$id\" name=\"$id\" $attributes>";
1653 if ($include_all_feeds) {
1654 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1655 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1659 if (get_pref('ENABLE_FEED_CATS')) {
1662 $parent_qpart = "parent_cat = '$root_id'";
1664 $parent_qpart = "parent_cat IS NULL";
1666 $result = db_query("SELECT id,title,
1667 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1668 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1669 FROM ttrss_feed_categories
1670 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1672 while ($line = db_fetch_assoc($result)) {
1674 for ($i = 0; $i < $nest_level; $i++
)
1675 $line["title"] = " - " . $line["title"];
1677 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1679 printf("<option $is_selected value='CAT:%d'>%s</option>",
1680 $line["id"], htmlspecialchars($line["title"]));
1682 if ($line["num_children"] > 0)
1683 print_feed_select($id, $default_id, $attributes,
1684 $include_all_feeds, $line["id"], $nest_level+
1);
1686 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1687 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1689 while ($fline = db_fetch_assoc($feed_result)) {
1690 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1692 $fline["title"] = " + " . $fline["title"];
1694 for ($i = 0; $i < $nest_level; $i++
)
1695 $fline["title"] = " - " . $fline["title"];
1697 printf("<option $is_selected value='%d'>%s</option>",
1698 $fline["id"], htmlspecialchars($fline["title"]));
1703 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1705 printf("<option $is_selected value='CAT:0'>%s</option>",
1706 __("Uncategorized"));
1708 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1709 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1711 while ($fline = db_fetch_assoc($feed_result)) {
1712 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1714 $fline["title"] = " + " . $fline["title"];
1716 for ($i = 0; $i < $nest_level; $i++
)
1717 $fline["title"] = " - " . $fline["title"];
1719 printf("<option $is_selected value='%d'>%s</option>",
1720 $fline["id"], htmlspecialchars($fline["title"]));
1725 $result = db_query("SELECT id,title FROM ttrss_feeds
1726 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1728 while ($line = db_fetch_assoc($result)) {
1730 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1732 printf("<option $is_selected value='%d'>%s</option>",
1733 $line["id"], htmlspecialchars($line["title"]));
1742 function print_feed_cat_select($id, $default_id,
1743 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1746 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1750 $parent_qpart = "parent_cat = '$root_id'";
1752 $parent_qpart = "parent_cat IS NULL";
1754 $result = db_query("SELECT id,title,
1755 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1756 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1757 FROM ttrss_feed_categories
1758 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1760 while ($line = db_fetch_assoc($result)) {
1761 if ($line["id"] == $default_id) {
1762 $is_selected = "selected=\"1\"";
1767 for ($i = 0; $i < $nest_level; $i++
)
1768 $line["title"] = " - " . $line["title"];
1771 printf("<option $is_selected value='%d'>%s</option>",
1772 $line["id"], htmlspecialchars($line["title"]));
1774 if ($line["num_children"] > 0)
1775 print_feed_cat_select($id, $default_id, $attributes,
1776 $include_all_cats, $line["id"], $nest_level+
1);
1780 if ($include_all_cats) {
1781 if (db_num_rows($result) > 0) {
1782 print "<option disabled=\"1\">--------</option>";
1785 if ($default_id == 0) {
1786 $is_selected = "selected=\"1\"";
1791 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1797 function checkbox_to_sql_bool($val) {
1798 return ($val == "on") ?
"true" : "false";
1801 function getFeedCatTitle($id) {
1803 return __("Special");
1804 } else if ($id < LABEL_BASE_INDEX
) {
1805 return __("Labels");
1806 } else if ($id > 0) {
1807 $result = db_query("SELECT ttrss_feed_categories.title
1808 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1809 cat_id = ttrss_feed_categories.id");
1810 if (db_num_rows($result) == 1) {
1811 return db_fetch_result($result, 0, "title");
1813 return __("Uncategorized");
1816 return "getFeedCatTitle($id) failed";
1821 function getFeedIcon($id) {
1824 return "images/archive.png";
1827 return "images/mark_set.svg";
1830 return "images/pub_set.svg";
1833 return "images/fresh.png";
1836 return "images/tag.png";
1839 return "images/recently_read.png";
1842 if ($id < LABEL_BASE_INDEX
) {
1843 return "images/label.png";
1845 if (file_exists(ICONS_DIR
. "/$id.ico"))
1846 return ICONS_URL
. "/$id.ico";
1854 function getFeedTitle($id, $cat = false) {
1856 return getCategoryTitle($id);
1857 } else if ($id == -1) {
1858 return __("Starred articles");
1859 } else if ($id == -2) {
1860 return __("Published articles");
1861 } else if ($id == -3) {
1862 return __("Fresh articles");
1863 } else if ($id == -4) {
1864 return __("All articles");
1865 } else if ($id === 0 ||
$id === "0") {
1866 return __("Archived articles");
1867 } else if ($id == -6) {
1868 return __("Recently read");
1869 } else if ($id < LABEL_BASE_INDEX
) {
1870 $label_id = feed_to_label_id($id);
1871 $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1872 if (db_num_rows($result) == 1) {
1873 return db_fetch_result($result, 0, "caption");
1875 return "Unknown label ($label_id)";
1878 } else if (is_numeric($id) && $id > 0) {
1879 $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'");
1880 if (db_num_rows($result) == 1) {
1881 return db_fetch_result($result, 0, "title");
1883 return "Unknown feed ($id)";
1890 function make_init_params() {
1893 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1894 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1895 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
1896 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1898 $params[strtolower($param)] = (int) get_pref($param);
1901 $params["icons_url"] = ICONS_URL
;
1902 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1903 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
1904 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
1905 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
1906 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1907 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1909 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1910 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1912 $max_feed_id = db_fetch_result($result, 0, "mid");
1913 $num_feeds = db_fetch_result($result, 0, "nf");
1915 $params["max_feed_id"] = (int) $max_feed_id;
1916 $params["num_feeds"] = (int) $num_feeds;
1918 $params["collapsed_feedlist"] = (int) get_pref("_COLLAPSED_FEEDLIST");
1919 $params["hotkeys"] = get_hotkeys_map();
1921 $params["csrf_token"] = $_SESSION["csrf_token"];
1922 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1924 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1929 function get_hotkeys_info() {
1931 __("Navigation") => array(
1932 "next_feed" => __("Open next feed"),
1933 "prev_feed" => __("Open previous feed"),
1934 "next_article" => __("Open next article"),
1935 "prev_article" => __("Open previous article"),
1936 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1937 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1938 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
1939 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
1940 "search_dialog" => __("Show search dialog")),
1941 __("Article") => array(
1942 "toggle_mark" => __("Toggle starred"),
1943 "toggle_publ" => __("Toggle published"),
1944 "toggle_unread" => __("Toggle unread"),
1945 "edit_tags" => __("Edit tags"),
1946 "dismiss_selected" => __("Dismiss selected"),
1947 "dismiss_read" => __("Dismiss read"),
1948 "open_in_new_window" => __("Open in new window"),
1949 "catchup_below" => __("Mark below as read"),
1950 "catchup_above" => __("Mark above as read"),
1951 "article_scroll_down" => __("Scroll down"),
1952 "article_scroll_up" => __("Scroll up"),
1953 "select_article_cursor" => __("Select article under cursor"),
1954 "email_article" => __("Email article"),
1955 "close_article" => __("Close/collapse article"),
1956 "toggle_expand" => __("Toggle article expansion (combined mode)"),
1957 "toggle_widescreen" => __("Toggle widescreen mode"),
1958 "toggle_embed_original" => __("Toggle embed original")),
1959 __("Article selection") => array(
1960 "select_all" => __("Select all articles"),
1961 "select_unread" => __("Select unread"),
1962 "select_marked" => __("Select starred"),
1963 "select_published" => __("Select published"),
1964 "select_invert" => __("Invert selection"),
1965 "select_none" => __("Deselect everything")),
1966 __("Feed") => array(
1967 "feed_refresh" => __("Refresh current feed"),
1968 "feed_unhide_read" => __("Un/hide read feeds"),
1969 "feed_subscribe" => __("Subscribe to feed"),
1970 "feed_edit" => __("Edit feed"),
1971 "feed_catchup" => __("Mark as read"),
1972 "feed_reverse" => __("Reverse headlines"),
1973 "feed_debug_update" => __("Debug feed update"),
1974 "catchup_all" => __("Mark all feeds as read"),
1975 "cat_toggle_collapse" => __("Un/collapse current category"),
1976 "toggle_combined_mode" => __("Toggle combined mode"),
1977 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1978 __("Go to") => array(
1979 "goto_all" => __("All articles"),
1980 "goto_fresh" => __("Fresh"),
1981 "goto_marked" => __("Starred"),
1982 "goto_published" => __("Published"),
1983 "goto_tagcloud" => __("Tag cloud"),
1984 "goto_prefs" => __("Preferences")),
1985 __("Other") => array(
1986 "create_label" => __("Create label"),
1987 "create_filter" => __("Create filter"),
1988 "collapse_sidebar" => __("Un/collapse sidebar"),
1989 "help_dialog" => __("Show help dialog"))
1992 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_INFO
) as $plugin) {
1993 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
1999 function get_hotkeys_map() {
2001 // "navigation" => array(
2004 "n" => "next_article",
2005 "p" => "prev_article",
2006 "(38)|up" => "prev_article",
2007 "(40)|down" => "next_article",
2008 // "^(38)|Ctrl-up" => "prev_article_noscroll",
2009 // "^(40)|Ctrl-down" => "next_article_noscroll",
2010 "(191)|/" => "search_dialog",
2011 // "article" => array(
2012 "s" => "toggle_mark",
2013 "*s" => "toggle_publ",
2014 "u" => "toggle_unread",
2015 "*t" => "edit_tags",
2016 "*d" => "dismiss_selected",
2017 "*x" => "dismiss_read",
2018 "o" => "open_in_new_window",
2019 "c p" => "catchup_below",
2020 "c n" => "catchup_above",
2021 "*n" => "article_scroll_down",
2022 "*p" => "article_scroll_up",
2023 "*(38)|Shift+up" => "article_scroll_up",
2024 "*(40)|Shift+down" => "article_scroll_down",
2025 "a *w" => "toggle_widescreen",
2026 "a e" => "toggle_embed_original",
2027 "e" => "email_article",
2028 "a q" => "close_article",
2029 // "article_selection" => array(
2030 "a a" => "select_all",
2031 "a u" => "select_unread",
2032 "a *u" => "select_marked",
2033 "a p" => "select_published",
2034 "a i" => "select_invert",
2035 "a n" => "select_none",
2037 "f r" => "feed_refresh",
2038 "f a" => "feed_unhide_read",
2039 "f s" => "feed_subscribe",
2040 "f e" => "feed_edit",
2041 "f q" => "feed_catchup",
2042 "f x" => "feed_reverse",
2043 "f *d" => "feed_debug_update",
2044 "f *c" => "toggle_combined_mode",
2045 "f c" => "toggle_cdm_expanded",
2046 "*q" => "catchup_all",
2047 "x" => "cat_toggle_collapse",
2049 "g a" => "goto_all",
2050 "g f" => "goto_fresh",
2051 "g s" => "goto_marked",
2052 "g p" => "goto_published",
2053 "g t" => "goto_tagcloud",
2054 "g *p" => "goto_prefs",
2055 // "other" => array(
2056 "(9)|Tab" => "select_article_cursor", // tab
2057 "c l" => "create_label",
2058 "c f" => "create_filter",
2059 "c s" => "collapse_sidebar",
2060 "^(191)|Ctrl+/" => "help_dialog",
2063 if (get_pref('COMBINED_DISPLAY_MODE')) {
2064 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2065 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2068 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_MAP
) as $plugin) {
2069 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2072 $prefixes = array();
2074 foreach (array_keys($hotkeys) as $hotkey) {
2075 $pair = explode(" ", $hotkey, 2);
2077 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2078 array_push($prefixes, $pair[0]);
2082 return array($prefixes, $hotkeys);
2085 function make_runtime_info() {
2088 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2089 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2091 $max_feed_id = db_fetch_result($result, 0, "mid");
2092 $num_feeds = db_fetch_result($result, 0, "nf");
2094 $data["max_feed_id"] = (int) $max_feed_id;
2095 $data["num_feeds"] = (int) $num_feeds;
2097 $data['last_article_id'] = getLastArticleId();
2098 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
2100 $data['dep_ts'] = calculate_dep_timestamp();
2101 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2103 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2105 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2107 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2109 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2112 $stamp_delta = time() - $stamp;
2114 if ($stamp_delta > 1800) {
2118 $_SESSION["daemon_stamp_check"] = time();
2121 $data['daemon_stamp_ok'] = $stamp_check;
2123 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2125 $data['daemon_stamp'] = $stamp_fmt;
2130 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2131 $new_version_details = @check_for_update
();
2133 $data['new_version_available'] = (int) ($new_version_details != false);
2135 $_SESSION["last_version_check"] = time();
2136 $_SESSION["version_data"] = $new_version_details;
2142 function search_to_sql($search) {
2144 $search_query_part = "";
2146 $keywords = explode(" ", $search);
2147 $query_keywords = array();
2149 foreach ($keywords as $k) {
2150 if (strpos($k, "-") === 0) {
2157 $commandpair = explode(":", mb_strtolower($k), 2);
2159 switch ($commandpair[0]) {
2161 if ($commandpair[1]) {
2162 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
2163 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2165 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2166 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2170 if ($commandpair[1]) {
2171 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
2172 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2174 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2175 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2179 if ($commandpair[1]) {
2180 if ($commandpair[1] == "true")
2181 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2182 else if ($commandpair[1] == "false")
2183 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2185 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
2186 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2188 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2189 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2194 if ($commandpair[1]) {
2195 if ($commandpair[1] == "true")
2196 array_push($query_keywords, "($not (marked = true))");
2198 array_push($query_keywords, "($not (marked = false))");
2200 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2201 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2205 if ($commandpair[1]) {
2206 if ($commandpair[1] == "true")
2207 array_push($query_keywords, "($not (published = true))");
2209 array_push($query_keywords, "($not (published = false))");
2212 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2213 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2217 if (strpos($k, "@") === 0) {
2219 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
2220 $orig_ts = strtotime(substr($k, 1));
2221 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2223 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2225 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2227 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2228 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2233 $search_query_part = implode("AND", $query_keywords);
2235 return $search_query_part;
2238 function getParentCategories($cat, $owner_uid) {
2241 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
2242 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2244 while ($line = db_fetch_assoc($result)) {
2245 array_push($rv, $line["parent_cat"]);
2246 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
2252 function getChildCategories($cat, $owner_uid) {
2255 $result = db_query("SELECT id FROM ttrss_feed_categories
2256 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2258 while ($line = db_fetch_assoc($result)) {
2259 array_push($rv, $line["id"]);
2260 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
2266 function queryFeedHeadlines($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) {
2268 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2270 $ext_tables_part = "";
2274 if (SPHINX_ENABLED
) {
2275 $ids = join(",", @sphinx_search
($search, 0, 500));
2278 $search_query_part = "ref_id IN ($ids) AND ";
2280 $search_query_part = "ref_id = -1 AND ";
2283 $search_query_part = search_to_sql($search);
2284 $search_query_part .= " AND ";
2288 $search_query_part = "";
2293 if (DB_TYPE
== "pgsql") {
2294 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2296 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2299 $override_order = "updated DESC";
2301 $filter_query_part = filter_to_sql($filter, $owner_uid);
2303 // Try to check if SQL regexp implementation chokes on a valid regexp
2304 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
2305 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2306 WHERE $filter_query_part LIMIT 1", false);
2309 $test = db_fetch_result($result, 0, "true_val");
2312 $filter_query_part = "false AND";
2314 $filter_query_part .= " AND";
2317 $filter_query_part = "false AND";
2321 $filter_query_part = "";
2325 $since_id_part = "ttrss_entries.id > $since_id AND ";
2327 $since_id_part = "";
2330 $view_query_part = "";
2332 if ($view_mode == "adaptive") {
2334 $view_query_part = " ";
2335 } else if ($feed != -1) {
2337 $unread = getFeedUnread($feed, $cat_view);
2339 if ($cat_view && $feed > 0 && $include_children)
2340 $unread +
= getCategoryChildrenUnread($feed);
2343 $view_query_part = " unread = true AND ";
2348 if ($view_mode == "marked") {
2349 $view_query_part = " marked = true AND ";
2352 if ($view_mode == "has_note") {
2353 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2356 if ($view_mode == "published") {
2357 $view_query_part = " published = true AND ";
2360 if ($view_mode == "unread" && $feed != -6) {
2361 $view_query_part = " unread = true AND ";
2365 $limit_query_part = "LIMIT " . $limit;
2368 $allow_archived = false;
2370 $vfeed_query_part = "";
2372 // override query strategy and enable feed display when searching globally
2373 if ($search && $search_mode == "all_feeds") {
2374 $query_strategy_part = "true";
2375 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2377 } else if (!is_numeric($feed)) {
2378 $query_strategy_part = "true";
2379 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2380 id = feed_id) as feed_title,";
2381 } else if ($search && $search_mode == "this_cat") {
2382 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2385 if ($include_children) {
2386 $subcats = getChildCategories($feed, $owner_uid);
2387 array_push($subcats, $feed);
2388 $cats_qpart = join(",", $subcats);
2390 $cats_qpart = $feed;
2393 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2396 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2399 } else if ($feed > 0) {
2404 if ($include_children) {
2406 $subcats = getChildCategories($feed, $owner_uid);
2408 array_push($subcats, $feed);
2409 $query_strategy_part = "cat_id IN (".
2410 implode(",", $subcats).")";
2413 $query_strategy_part = "cat_id = '$feed'";
2417 $query_strategy_part = "cat_id IS NULL";
2420 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2423 $query_strategy_part = "feed_id = '$feed'";
2425 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2426 $query_strategy_part = "feed_id IS NULL";
2427 $allow_archived = true;
2428 } else if ($feed == 0 && $cat_view) { // uncategorized
2429 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2430 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2431 } else if ($feed == -1) { // starred virtual feed
2432 $query_strategy_part = "marked = true";
2433 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2434 $allow_archived = true;
2436 if (!$override_order) {
2437 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2440 } else if ($feed == -2) { // published virtual feed OR labels category
2443 $query_strategy_part = "published = true";
2444 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2445 $allow_archived = true;
2447 if (!$override_order) {
2448 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2452 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2454 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2456 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2457 ttrss_user_labels2.article_id = ref_id";
2460 } else if ($feed == -6) { // recently read
2461 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2462 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2463 $allow_archived = true;
2465 if (!$override_order) $override_order = "last_read DESC";
2466 } else if ($feed == -3) { // fresh virtual feed
2467 $query_strategy_part = "unread = true AND score >= 0";
2469 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
2471 if (DB_TYPE
== "pgsql") {
2472 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2474 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2477 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2478 } else if ($feed == -4) { // all articles virtual feed
2479 $allow_archived = true;
2480 $query_strategy_part = "true";
2481 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2482 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2483 $label_id = feed_to_label_id($feed);
2485 $query_strategy_part = "label_id = '$label_id' AND
2486 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2487 ttrss_user_labels2.article_id = ref_id";
2489 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2490 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2491 $allow_archived = true;
2494 $query_strategy_part = "true";
2497 $order_by = "score DESC, date_entered DESC, updated DESC";
2499 if ($view_mode == "unread_first") {
2500 $order_by = "unread DESC, $order_by";
2503 if ($override_order) {
2504 $order_by = $override_order;
2510 $feed_title = T_sprintf("Search results: %s", $search);
2513 $feed_title = getCategoryTitle($feed);
2515 if (is_numeric($feed) && $feed > 0) {
2516 $result = db_query("SELECT title,site_url,last_error
2517 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2519 $feed_title = db_fetch_result($result, 0, "title");
2520 $feed_site_url = db_fetch_result($result, 0, "site_url");
2521 $last_error = db_fetch_result($result, 0, "last_error");
2523 $feed_title = getFeedTitle($feed);
2528 $content_query_part = "content as content_preview, cached_content, ";
2530 if (is_numeric($feed)) {
2533 $feed_kind = "Feeds";
2535 $feed_kind = "Labels";
2538 if ($limit_query_part) {
2539 $offset_query_part = "OFFSET $offset";
2542 // proper override_order applied above
2543 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
2544 if (!$override_order) {
2545 $order_by = "ttrss_feeds.title, $order_by";
2547 $order_by = "ttrss_feeds.title, $override_order";
2551 if (!$allow_archived) {
2552 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2553 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2556 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2557 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2560 if ($vfeed_query_part)
2561 $vfeed_query_part .= "favicon_avg_color,";
2563 $query = "SELECT DISTINCT
2566 ttrss_entries.id,ttrss_entries.title,
2570 always_display_enclosures,
2577 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2578 last_marked, last_published,
2586 ttrss_user_entries.ref_id = ttrss_entries.id AND
2587 ttrss_user_entries.owner_uid = '$owner_uid' AND
2592 $query_strategy_part ORDER BY $order_by
2593 $limit_query_part $offset_query_part";
2595 if ($_REQUEST["debug"]) print $query;
2597 $result = db_query($query);
2602 $select_qpart = "SELECT DISTINCT " .
2606 "ttrss_entries.id as id," .
2619 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2620 "last_marked, last_published, " .
2623 $content_query_part .
2626 $feed_kind = "Tags";
2627 $all_tags = explode(",", $feed);
2628 if ($search_mode == 'any') {
2629 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2630 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2631 $where_qpart = " WHERE " .
2632 "ref_id = ttrss_entries.id AND " .
2633 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2634 "post_int_id = int_id AND $tag_sql AND " .
2636 $search_query_part .
2637 $query_strategy_part . " ORDER BY $order_by " .
2642 $sub_selects = array();
2643 $sub_ands = array();
2644 foreach ($all_tags as $term) {
2645 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");
2652 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2657 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2658 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2659 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2660 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2662 // error_log("TAG SQL: " . $tag_sql);
2663 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2665 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2666 $result = db_query($select_qpart . $from_qpart . $where_qpart);
2669 return array($result, $feed_title, $feed_site_url, $last_error);
2673 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false) {
2674 if (!$owner) $owner = $_SESSION["uid"];
2676 $res = trim($str); if (!$res) return '';
2678 if (strpos($res, "href=") === false)
2679 $res = rewrite_urls($res);
2681 $charset_hack = '<head>
2682 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2685 $res = trim($res); if (!$res) return '';
2687 libxml_use_internal_errors(true);
2689 $doc = new DOMDocument();
2690 $doc->loadHTML($charset_hack . $res);
2691 $xpath = new DOMXPath($doc);
2693 $entries = $xpath->query('(//a[@href]|//img[@src])');
2695 foreach ($entries as $entry) {
2699 if ($entry->hasAttribute('href'))
2700 $entry->setAttribute('href',
2701 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2703 if ($entry->hasAttribute('src')) {
2704 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2706 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2708 if (file_exists($cached_filename)) {
2709 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2712 $entry->setAttribute('src', $src);
2715 if ($entry->nodeName
== 'img') {
2716 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
2717 $force_remove_images ||
$_SESSION["bw_limit"]) {
2719 $p = $doc->createElement('p');
2721 $a = $doc->createElement('a');
2722 $a->setAttribute('href', $entry->getAttribute('src'));
2724 $a->appendChild(new DOMText($entry->getAttribute('src')));
2725 $a->setAttribute('target', '_blank');
2727 $p->appendChild($a);
2729 $entry->parentNode
->replaceChild($p, $entry);
2734 if (strtolower($entry->nodeName
) == "a") {
2735 $entry->setAttribute("target", "_blank");
2739 $entries = $xpath->query('//iframe');
2740 foreach ($entries as $entry) {
2741 $entry->setAttribute('sandbox', 'allow-scripts');
2745 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
2746 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
2747 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
2748 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
2749 'dt', 'em', 'footer', 'figure', 'figcaption',
2750 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
2751 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
2752 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
2753 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2754 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
2755 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2757 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2759 $disallowed_attributes = array('id', 'style', 'class');
2761 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_SANITIZE
) as $plugin) {
2762 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2763 if (is_array($retval)) {
2765 $allowed_elements = $retval[1];
2766 $disallowed_attributes = $retval[2];
2772 $doc->removeChild($doc->firstChild
); //remove doctype
2773 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2774 $res = $doc->saveHTML();
2778 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2779 $entries = $doc->getElementsByTagName("*");
2781 foreach ($entries as $entry) {
2782 if (!in_array($entry->nodeName
, $allowed_elements)) {
2783 $entry->parentNode
->removeChild($entry);
2786 if ($entry->hasAttributes()) {
2787 $attrs_to_remove = array();
2789 foreach ($entry->attributes
as $attr) {
2791 if (strpos($attr->nodeName
, 'on') === 0) {
2792 array_push($attrs_to_remove, $attr);
2795 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2796 array_push($attrs_to_remove, $attr);
2800 foreach ($attrs_to_remove as $attr) {
2801 $entry->removeAttributeNode($attr);
2809 function check_for_update() {
2810 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2811 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2812 "&iid=" . sha1(SELF_URL_PATH
);
2814 $version_data = @fetch_file_contents
($version_url);
2816 if ($version_data) {
2817 $version_data = json_decode($version_data, true);
2818 if ($version_data && $version_data['version']) {
2820 if (version_compare(VERSION
, $version_data['version']) == -1) {
2821 return $version_data;
2829 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
2831 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2832 if (count($ids) == 0) return;
2836 foreach ($ids as $id) {
2837 array_push($tmp_ids, "ref_id = '$id'");
2840 $ids_qpart = join(" OR ", $tmp_ids);
2843 db_query("UPDATE ttrss_user_entries SET
2844 unread = false,last_read = NOW()
2845 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2846 } else if ($cmode == 1) {
2847 db_query("UPDATE ttrss_user_entries SET
2849 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2851 db_query("UPDATE ttrss_user_entries SET
2852 unread = NOT unread,last_read = NOW()
2853 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2858 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
2859 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2861 while ($line = db_fetch_assoc($result)) {
2862 ccache_update($line["feed_id"], $owner_uid);
2866 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
2868 $a_id = db_escape_string($id);
2870 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2872 $query = "SELECT DISTINCT tag_name,
2873 owner_uid as owner FROM
2874 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2875 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2877 $obj_id = md5("TAGS:$owner_uid:$id");
2880 /* check cache first */
2882 if ($tag_cache === false) {
2883 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
2884 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2886 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2890 $tags = explode(",", $tag_cache);
2893 /* do it the hard way */
2895 $tmp_result = db_query($query);
2897 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2898 array_push($tags, $tmp_line["tag_name"]);
2901 /* update the cache */
2903 $tags_str = db_escape_string(join(",", $tags));
2905 db_query("UPDATE ttrss_user_entries
2906 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2907 AND owner_uid = $owner_uid");
2913 function trim_array($array) {
2915 array_walk($tmp, 'trim');
2919 function tag_is_valid($tag) {
2920 if ($tag == '') return false;
2921 if (preg_match("/^[0-9]*$/", $tag)) return false;
2922 if (mb_strlen($tag) > 250) return false;
2924 if (function_exists('iconv')) {
2925 $tag = iconv("utf-8", "utf-8", $tag);
2928 if (!$tag) return false;
2933 function render_login_form() {
2934 header('Cache-Control: public');
2936 require_once "login_form.php";
2940 function format_warning($msg, $id = "") {
2942 return "<div class=\"warning\" id=\"$id\">
2943 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2946 function format_notice($msg, $id = "") {
2948 return "<div class=\"notice\" id=\"$id\">
2949 <span><img src=\"images/sign_info.svg\"></span><span>$msg</span></div>";
2952 function format_error($msg, $id = "") {
2954 return "<div class=\"error\" id=\"$id\">
2955 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2958 function print_notice($msg) {
2959 return print format_notice($msg);
2962 function print_warning($msg) {
2963 return print format_warning($msg);
2966 function print_error($msg) {
2967 return print format_error($msg);
2971 function T_sprintf() {
2972 $args = func_get_args();
2973 return vsprintf(__(array_shift($args)), $args);
2976 function format_inline_player($url, $ctype) {
2980 $url = htmlspecialchars($url);
2982 if (strpos($ctype, "audio/") === 0) {
2984 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
2985 $_SESSION["hasMp3"])) {
2987 $entry .= "<audio controls>
2988 <source type=\"$ctype\" src=\"$url\"></source>
2993 $entry .= "<object type=\"application/x-shockwave-flash\"
2994 data=\"lib/button/musicplayer.swf?song_url=$url\"
2995 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
2996 <param name=\"movie\"
2997 value=\"lib/button/musicplayer.swf?song_url=$url\" />
3001 if ($entry) $entry .= " <a target=\"_blank\"
3002 href=\"$url\">" . basename($url) . "</a>";
3010 /* $filename = substr($url, strrpos($url, "/")+1);
3012 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3013 $filename . " (" . $ctype . ")" . "</a>"; */
3017 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
3018 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3024 /* we can figure out feed_id from article id anyway, why do we
3025 * pass feed_id here? let's ignore the argument :(*/
3027 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3028 WHERE ref_id = '$id'");
3030 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
3032 $rv['feed_id'] = $feed_id;
3034 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3036 if ($mark_as_read) {
3037 $result = db_query("UPDATE ttrss_user_entries
3038 SET unread = false,last_read = NOW()
3039 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
3041 ccache_update($feed_id, $owner_uid);
3044 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,
3045 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
3046 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
3047 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
3048 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
3055 FROM ttrss_entries,ttrss_user_entries
3056 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3060 $line = db_fetch_assoc($result);
3062 $tag_cache = $line["tag_cache"];
3064 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
3065 unset($line["tag_cache"]);
3067 $line["content"] = sanitize($line["content"], false, $owner_uid, $line["site_url"]);
3069 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_RENDER_ARTICLE
) as $p) {
3070 $line = $p->hook_render_article($line);
3073 $num_comments = $line["num_comments"];
3074 $entry_comments = "";
3076 if ($num_comments > 0) {
3077 if ($line["comments"]) {
3078 $comments_url = htmlspecialchars($line["comments"]);
3080 $comments_url = htmlspecialchars($line["link"]);
3082 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3084 if ($line["comments"] && $line["link"] != $line["comments"]) {
3085 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3090 header("Content-Type: text/html");
3091 $rv['content'] .= "<html><head>
3092 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3093 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3094 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3095 </head><body id=\"ttrssZoom\">";
3098 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3100 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3102 $entry_author = $line["author"];
3104 if ($entry_author) {
3105 $entry_author = __(" - ") . $entry_author;
3108 $parsed_updated = make_local_datetime($line["updated"], true,
3111 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3113 if ($line["link"]) {
3114 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3115 title=\"".htmlspecialchars($line['title'])."\"
3117 htmlspecialchars($line["link"]) . "\">" .
3118 $line["title"] . "</a>" .
3119 "<span class='author'>$entry_author</span></div>";
3121 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3124 $tags_str = format_tags_string($line["tags"], $id);
3125 $tags_str_full = join(", ", $line["tags"]);
3127 if (!$tags_str_full) $tags_str_full = __("no tags");
3129 if (!$entry_comments) $entry_comments = " "; # placeholder
3131 $rv['content'] .= "<div class='postTags' style='float : right'>
3132 <img src='images/tag.png'
3133 class='tagsPic' alt='Tags' title='Tags'> ";
3136 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3137 <a title=\"".__('Edit tags for this article')."\"
3138 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3140 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3141 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3142 position=\"below\">$tags_str_full</div>";
3144 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_BUTTON
) as $p) {
3145 $rv['content'] .= $p->hook_article_button($line);
3149 $tags_str = strip_tags($tags_str);
3150 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3152 $rv['content'] .= "</div>";
3153 $rv['content'] .= "<div clear='both'>";
3155 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_LEFT_BUTTON
) as $p) {
3156 $rv['content'] .= $p->hook_article_left_button($line);
3159 $rv['content'] .= "$entry_comments</div>";
3161 if ($line["orig_feed_id"]) {
3163 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
3164 WHERE id = ".$line["orig_feed_id"]);
3166 if (db_num_rows($tmp_result) != 0) {
3168 $rv['content'] .= "<div clear='both'>";
3169 $rv['content'] .= __("Originally from:");
3171 $rv['content'] .= " ";
3173 $tmp_line = db_fetch_assoc($tmp_result);
3175 $rv['content'] .= "<a target='_blank'
3176 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3177 $tmp_line['title'] . "</a>";
3179 $rv['content'] .= " ";
3181 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3182 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3184 $rv['content'] .= "</div>";
3188 $rv['content'] .= "</div>";
3190 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3191 if ($line['note']) {
3192 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3194 $rv['content'] .= "</div>";
3196 $rv['content'] .= "<div class=\"postContent\">";
3198 $rv['content'] .= $line["content"];
3199 $rv['content'] .= format_article_enclosures($id,
3200 sql_bool_to_bool($line["always_display_enclosures"]),
3202 sql_bool_to_bool($line["hide_images"]));
3204 $rv['content'] .= "</div>";
3206 $rv['content'] .= "</div>";
3212 <div class='footer'>
3213 <button onclick=\"return window.close()\">".
3214 __("Close this window")."</button></div>";
3215 $rv['content'] .= "</body></html>";
3222 function print_checkpoint($n, $s) {
3223 $ts = microtime(true);
3224 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3228 function sanitize_tag($tag) {
3231 $tag = mb_strtolower($tag, 'utf-8');
3233 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3235 // $tag = str_replace('"', "", $tag);
3236 // $tag = str_replace("+", " ", $tag);
3237 $tag = str_replace("technorati tag: ", "", $tag);
3242 function get_self_url_prefix() {
3243 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3244 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3246 return SELF_URL_PATH
;
3251 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3253 * @return string The Mozilla Firefox feed adding URL.
3255 function add_feed_url() {
3256 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3258 $url_path = get_self_url_prefix() .
3259 "/public.php?op=subscribe&feed_url=%s";
3261 } // function add_feed_url
3263 function encrypt_password($pass, $salt = '', $mode2 = false) {
3264 if ($salt && $mode2) {
3265 return "MODE2:" . hash('sha256', $salt . $pass);
3267 return "SHA1X:" . sha1("$salt:$pass");
3269 return "SHA1:" . sha1($pass);
3271 } // function encrypt_password
3273 function load_filters($feed_id, $owner_uid, $action_id = false) {
3276 $cat_id = (int)getFeedCategory($feed_id);
3278 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
3279 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3281 $check_cats = join(",", array_merge(
3282 getParentCategories($cat_id, $owner_uid),
3285 while ($line = db_fetch_assoc($result)) {
3286 $filter_id = $line["id"];
3288 $result2 = db_query("SELECT
3289 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3290 FROM ttrss_filters2_rules AS r,
3291 ttrss_filter_types AS t
3293 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3294 (feed_id IS NULL OR feed_id = '$feed_id') AND
3295 filter_type = t.id AND filter_id = '$filter_id'");
3300 while ($rule_line = db_fetch_assoc($result2)) {
3301 # print_r($rule_line);
3304 $rule["reg_exp"] = $rule_line["reg_exp"];
3305 $rule["type"] = $rule_line["type_name"];
3306 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3308 array_push($rules, $rule);
3311 $result2 = db_query("SELECT a.action_param,t.name AS type_name
3312 FROM ttrss_filters2_actions AS a,
3313 ttrss_filter_actions AS t
3315 action_id = t.id AND filter_id = '$filter_id'");
3317 while ($action_line = db_fetch_assoc($result2)) {
3318 # print_r($action_line);
3321 $action["type"] = $action_line["type_name"];
3322 $action["param"] = $action_line["action_param"];
3324 array_push($actions, $action);
3329 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3330 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3331 $filter["rules"] = $rules;
3332 $filter["actions"] = $actions;
3334 if (count($rules) > 0 && count($actions) > 0) {
3335 array_push($filters, $filter);
3342 function get_score_pic($score) {
3344 return "score_high.png";
3345 } else if ($score > 0) {
3346 return "score_half_high.png";
3347 } else if ($score < -100) {
3348 return "score_low.png";
3349 } else if ($score < 0) {
3350 return "score_half_low.png";
3352 return "score_neutral.png";
3356 function feed_has_icon($id) {
3357 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3360 function init_plugins() {
3361 PluginHost
::getInstance()->load(PLUGINS
, PluginHost
::KIND_ALL
);
3366 function format_tags_string($tags, $id) {
3369 $tags_nolinks_str = "";
3375 $formatted_tags = array();
3377 foreach ($tags as $tag) {
3379 $tag_escaped = str_replace("'", "\\'", $tag);
3381 if (mb_strlen($tag) > 30) {
3382 $tag = truncate_string($tag, 30);
3385 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3387 array_push($formatted_tags, $tag_str);
3389 $tmp_tags_str = implode(", ", $formatted_tags);
3391 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3396 $tags_str = implode(", ", $formatted_tags);
3398 if ($num_tags < count($tags)) {
3399 $tags_str .= ", …";
3402 if ($num_tags == 0) {
3403 $tags_str = __("no tags");
3410 function format_article_labels($labels, $id) {
3412 if (is_array($labels)) return '';
3416 foreach ($labels as $l) {
3417 $labels_str .= sprintf("<span class='hlLabelRef'
3418 style='color : %s; background-color : %s'>%s</span>",
3419 $l[2], $l[3], $l[1]);
3426 function format_article_note($id, $note, $allow_edit = true) {
3428 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3429 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3430 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3436 function get_feed_category($feed_cat, $parent_cat_id = false) {
3437 if ($parent_cat_id) {
3438 $parent_qpart = "parent_cat = '$parent_cat_id'";
3439 $parent_insert = "'$parent_cat_id'";
3441 $parent_qpart = "parent_cat IS NULL";
3442 $parent_insert = "NULL";
3446 "SELECT id FROM ttrss_feed_categories
3447 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3449 if (db_num_rows($result) == 0) {
3452 return db_fetch_result($result, 0, "id");
3456 function add_feed_category($feed_cat, $parent_cat_id = false) {
3458 if (!$feed_cat) return false;
3462 if ($parent_cat_id) {
3463 $parent_qpart = "parent_cat = '$parent_cat_id'";
3464 $parent_insert = "'$parent_cat_id'";
3466 $parent_qpart = "parent_cat IS NULL";
3467 $parent_insert = "NULL";
3470 $feed_cat = mb_substr($feed_cat, 0, 250);
3473 "SELECT id FROM ttrss_feed_categories
3474 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3476 if (db_num_rows($result) == 0) {
3479 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3480 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3490 function getArticleFeed($id) {
3491 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3492 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3494 if (db_num_rows($result) != 0) {
3495 return db_fetch_result($result, 0, "feed_id");
3502 * Fixes incomplete URLs by prepending "http://".
3503 * Also replaces feed:// with http://, and
3504 * prepends a trailing slash if the url is a domain name only.
3506 * @param string $url Possibly incomplete URL
3508 * @return string Fixed URL.
3510 function fix_url($url) {
3511 if (strpos($url, '://') === false) {
3512 $url = 'http://' . $url;
3513 } else if (substr($url, 0, 5) == 'feed:') {
3514 $url = 'http:' . substr($url, 5);
3517 //prepend slash if the URL has no slash in it
3518 // "http://www.example" -> "http://www.example/"
3519 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3523 if ($url != "http:///")
3529 function validate_feed_url($url) {
3530 $parts = parse_url($url);
3532 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3536 function get_article_enclosures($id) {
3538 $query = "SELECT * FROM ttrss_enclosures
3539 WHERE post_id = '$id' AND content_url != ''";
3543 $result = db_query($query);
3545 if (db_num_rows($result) > 0) {
3546 while ($line = db_fetch_assoc($result)) {
3547 array_push($rv, $line);
3554 function save_email_address($email) {
3555 // FIXME: implement persistent storage of emails
3557 if (!$_SESSION['stored_emails'])
3558 $_SESSION['stored_emails'] = array();
3560 if (!in_array($email, $_SESSION['stored_emails']))
3561 array_push($_SESSION['stored_emails'], $email);
3565 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
3567 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3569 $sql_is_cat = bool_to_sql_bool($is_cat);
3571 $result = db_query("SELECT access_key FROM ttrss_access_keys
3572 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3573 AND owner_uid = " . $owner_uid);
3575 if (db_num_rows($result) == 1) {
3576 return db_fetch_result($result, 0, "access_key");
3578 $key = db_escape_string(sha1(uniqid(rand(), true)));
3580 $result = db_query("INSERT INTO ttrss_access_keys
3581 (access_key, feed_id, is_cat, owner_uid)
3582 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3589 function get_feeds_from_html($url, $content)
3591 $url = fix_url($url);
3592 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3594 libxml_use_internal_errors(true);
3596 $doc = new DOMDocument();
3597 $doc->loadHTML($content);
3598 $xpath = new DOMXPath($doc);
3599 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3600 $feedUrls = array();
3601 foreach ($entries as $entry) {
3602 if ($entry->hasAttribute('href')) {
3603 $title = $entry->getAttribute('title');
3605 $title = $entry->getAttribute('type');
3607 $feedUrl = rewrite_relative_url(
3608 $baseUrl, $entry->getAttribute('href')
3610 $feedUrls[$feedUrl] = $title;
3616 function is_html($content) {
3617 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3620 function url_is_html($url, $login = false, $pass = false) {
3621 return is_html(fetch_file_contents($url, false, $login, $pass));
3624 function print_label_select($name, $value, $attributes = "") {
3626 $result = db_query("SELECT caption FROM ttrss_labels2
3627 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3629 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3630 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3632 while ($line = db_fetch_assoc($result)) {
3634 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3636 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3637 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3641 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3648 function format_article_enclosures($id, $always_display_enclosures,
3649 $article_content, $hide_images = false) {
3651 $result = get_article_enclosures($id);
3654 if (count($result) > 0) {
3656 $entries_html = array();
3658 $entries_inline = array();
3660 foreach ($result as $line) {
3662 $url = $line["content_url"];
3663 $ctype = $line["content_type"];
3665 if (!$ctype) $ctype = __("unknown type");
3667 $filename = substr($url, strrpos($url, "/")+
1);
3669 $player = format_inline_player($url, $ctype);
3671 if ($player) array_push($entries_inline, $player);
3673 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3674 # $filename . " (" . $ctype . ")" . "</a>";
3676 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3677 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3679 array_push($entries_html, $entry);
3683 $entry["type"] = $ctype;
3684 $entry["filename"] = $filename;
3685 $entry["url"] = $url;
3687 array_push($entries, $entry);
3690 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3691 if ($always_display_enclosures ||
3692 !preg_match("/<img/i", $article_content)) {
3694 foreach ($entries as $entry) {
3696 if (preg_match("/image/", $entry["type"]) ||
3697 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3699 if (!$hide_images) {
3701 alt=\"".htmlspecialchars($entry["filename"])."\"
3702 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3704 $rv .= "<p><a target=\"_blank\"
3705 href=\"".htmlspecialchars($entry["url"])."\"
3706 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3714 if (count($entries_inline) > 0) {
3715 $rv .= "<hr clear='both'/>";
3716 foreach ($entries_inline as $entry) { $rv .= $entry; };
3717 $rv .= "<hr clear='both'/>";
3720 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3721 "<option value=''>" . __('Attachments')."</option>";
3723 foreach ($entries as $entry) {
3724 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3734 function getLastArticleId() {
3735 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3736 WHERE owner_uid = " . $_SESSION["uid"]);
3738 if (db_num_rows($result) == 1) {
3739 return db_fetch_result($result, 0, "id");
3745 function build_url($parts) {
3746 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3750 * Converts a (possibly) relative URL to a absolute one.
3752 * @param string $url Base URL (i.e. from where the document is)
3753 * @param string $rel_url Possibly relative URL in the document
3755 * @return string Absolute URL
3757 function rewrite_relative_url($url, $rel_url) {
3758 if (strpos($rel_url, "magnet:") === 0) {
3760 } else if (strpos($rel_url, "://") !== false) {
3762 } else if (strpos($rel_url, "//") === 0) {
3763 # protocol-relative URL (rare but they exist)
3765 } else if (strpos($rel_url, "/") === 0)
3767 $parts = parse_url($url);
3768 $parts['path'] = $rel_url;
3770 return build_url($parts);
3773 $parts = parse_url($url);
3774 if (!isset($parts['path'])) {
3775 $parts['path'] = '/';
3777 $dir = $parts['path'];
3778 if (substr($dir, -1) !== '/') {
3779 $dir = dirname($parts['path']);
3780 $dir !== '/' && $dir .= '/';
3782 $parts['path'] = $dir . $rel_url;
3784 return build_url($parts);
3788 function sphinx_search($query, $offset = 0, $limit = 30) {
3789 require_once 'lib/sphinxapi.php';
3791 $sphinxClient = new SphinxClient();
3793 $sphinxpair = explode(":", SPHINX_SERVER
, 2);
3795 $sphinxClient->SetServer($sphinxpair[0], $sphinxpair[1]);
3796 $sphinxClient->SetConnectTimeout(1);
3798 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3799 'feed_title' => 20));
3801 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3802 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3803 $sphinxClient->SetLimits($offset, $limit, 1000);
3804 $sphinxClient->SetArrayResult(false);
3805 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3807 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3811 if (is_array($result['matches'])) {
3812 foreach (array_keys($result['matches']) as $int_id) {
3813 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3814 array_push($ids, $ref_id);
3821 function cleanup_tags($days = 14, $limit = 1000) {
3823 if (DB_TYPE
== "pgsql") {
3824 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3825 } else if (DB_TYPE
== "mysql") {
3826 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3831 while ($limit > 0) {
3834 $query = "SELECT ttrss_tags.id AS id
3835 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3836 WHERE post_int_id = int_id AND $interval_query AND
3837 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3839 $result = db_query($query);
3843 while ($line = db_fetch_assoc($result)) {
3844 array_push($ids, $line['id']);
3847 if (count($ids) > 0) {
3848 $ids = join(",", $ids);
3850 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
3851 $tags_deleted +
= db_affected_rows($tmp_result);
3856 $limit -= $limit_part;
3859 return $tags_deleted;
3862 function print_user_stylesheet() {
3863 $value = get_pref('USER_STYLESHEET');
3866 print "<style type=\"text/css\">";
3867 print str_replace("<br/>", "\n", $value);
3873 function rewrite_urls($html) {
3874 libxml_use_internal_errors(true);
3876 $charset_hack = '<head>
3877 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3880 $doc = new DOMDocument();
3881 $doc->loadHTML($charset_hack . $html);
3882 $xpath = new DOMXPath($doc);
3884 $entries = $xpath->query('//*/text()');
3886 foreach ($entries as $entry) {
3887 if (strstr($entry->wholeText
, "://") !== false) {
3888 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3889 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3891 if ($text != $entry->wholeText
) {
3892 $cdoc = new DOMDocument();
3893 $cdoc->loadHTML($charset_hack . $text);
3896 foreach ($cdoc->childNodes
as $cnode) {
3897 $cnode = $doc->importNode($cnode, true);
3900 $entry->parentNode
->insertBefore($cnode);
3904 $entry->parentNode
->removeChild($entry);
3910 $node = $doc->getElementsByTagName('body')->item(0);
3912 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3914 return $doc->saveXML($node);
3919 function filter_to_sql($filter, $owner_uid) {
3922 if (DB_TYPE
== "pgsql")
3925 $reg_qpart = "REGEXP";
3927 foreach ($filter["rules"] AS $rule) {
3928 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3929 $rule['reg_exp']) !== FALSE;
3931 if ($regexp_valid) {
3933 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
3935 switch ($rule["type"]) {
3937 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3938 $rule['reg_exp'] . "')";
3941 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3942 $rule['reg_exp'] . "')";
3945 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3946 $rule['reg_exp'] . "') OR LOWER(" .
3947 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3950 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3951 $rule['reg_exp'] . "')";
3954 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3955 $rule['reg_exp'] . "')";
3958 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3959 $rule['reg_exp'] . "')";
3963 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3965 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3966 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
3969 if (isset($rule["cat_id"])) {
3971 if ($rule["cat_id"] > 0) {
3972 $children = getChildCategories($rule["cat_id"], $owner_uid);
3973 array_push($children, $rule["cat_id"]);
3975 $children = join(",", $children);
3977 $cat_qpart = "cat_id IN ($children)";
3979 $cat_qpart = "cat_id IS NULL";
3982 $qpart .= " AND $cat_qpart";
3985 array_push($query, "($qpart)");
3990 if (count($query) > 0) {
3991 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
3993 $fullquery = "(false)";
3996 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
4001 if (!function_exists('gzdecode')) {
4002 function gzdecode($string) { // no support for 2nd argument
4003 return file_get_contents('compress.zlib://data:who/cares;base64,'.
4004 base64_encode($string));
4008 function get_random_bytes($length) {
4009 if (function_exists('openssl_random_pseudo_bytes')) {
4010 return openssl_random_pseudo_bytes($length);
4014 for ($i = 0; $i < $length; $i++
)
4015 $output .= chr(mt_rand(0, 255));
4021 function read_stdin() {
4022 $fp = fopen("php://stdin", "r");
4025 $line = trim(fgets($fp));
4033 function tmpdirname($path, $prefix) {
4034 // Use PHP's tmpfile function to create a temporary
4035 // directory name. Delete the file and keep the name.
4036 $tempname = tempnam($path,$prefix);
4040 if (!unlink($tempname))
4046 function getFeedCategory($feed) {
4047 $result = db_query("SELECT cat_id FROM ttrss_feeds
4048 WHERE id = '$feed'");
4050 if (db_num_rows($result) > 0) {
4051 return db_fetch_result($result, 0, "cat_id");
4058 function implements_interface($class, $interface) {
4059 return in_array($interface, class_implements($class));
4062 function geturl($url){
4064 if (!function_exists('curl_init'))
4065 return user_error('CURL Must be installed for geturl function to work. Ask your host to enable it or uncomment extension=php_curl.dll in php.ini', E_USER_ERROR
);
4067 $curl = curl_init();
4068 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4069 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4070 $header[] = "Cache-Control: max-age=0";
4071 $header[] = "Connection: keep-alive";
4072 $header[] = "Keep-Alive: 300";
4073 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4074 $header[] = "Accept-Language: en-us,en;q=0.5";
4075 $header[] = "Pragma: ";
4077 curl_setopt($curl, CURLOPT_URL
, $url);
4078 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4079 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4080 curl_setopt($curl, CURLOPT_HEADER
, true);
4081 curl_setopt($curl, CURLOPT_REFERER
, $url);
4082 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4083 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4084 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4085 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4086 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4088 $html = curl_exec($curl);
4090 $status = curl_getinfo($curl);
4093 if($status['http_code']!=200){
4094 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4095 list($header) = explode("\r\n\r\n", $html, 2);
4097 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4098 $url = trim(str_replace($matches[1],"",$matches[0]));
4099 $url_parsed = parse_url($url);
4100 return (isset($url_parsed))?
geturl($url, $referer):'';
4103 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4104 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4105 # $handle = @fopen('./curl.error.log', 'a');
4106 # fwrite($handle, $line);
4112 function get_minified_js($files) {
4113 require_once 'lib/jshrink/Minifier.php';
4117 foreach ($files as $js) {
4118 if (!isset($_GET['debug'])) {
4119 $cached_file = CACHE_DIR
. "/js/$js.js";
4121 if (file_exists($cached_file) &&
4122 is_readable($cached_file) &&
4123 filemtime($cached_file) >= filemtime("js/$js.js")) {
4125 $rv .= file_get_contents($cached_file);
4128 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4129 file_put_contents($cached_file, $minified);
4133 $rv .= file_get_contents("js/$js.js");
4140 function stylesheet_tag($filename) {
4141 $timestamp = filemtime($filename);
4143 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4146 function javascript_tag($filename) {
4149 if (!(strpos($filename, "?") === FALSE)) {
4150 $query = substr($filename, strpos($filename, "?")+
1);
4151 $filename = substr($filename, 0, strpos($filename, "?"));
4154 $timestamp = filemtime($filename);
4156 if ($query) $timestamp .= "&$query";
4158 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4161 function calculate_dep_timestamp() {
4162 $files = array_merge(glob("js/*.js"), glob("*.css"));
4166 foreach ($files as $file) {
4167 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4173 function T_js_decl($s1, $s2) {
4175 $s1 = preg_replace("/\n/", "", $s1);
4176 $s2 = preg_replace("/\n/", "", $s2);
4178 $s1 = preg_replace("/\"/", "\\\"", $s1);
4179 $s2 = preg_replace("/\"/", "\\\"", $s2);
4181 return "T_messages[\"$s1\"] = \"$s2\";\n";
4185 function init_js_translations() {
4187 print 'var T_messages = new Object();
4190 if (T_messages[msg]) {
4191 return T_messages[msg];
4197 function ngettext(msg1, msg2, n) {
4198 return (parseInt(n) > 1) ? msg2 : msg1;
4201 $l10n = _get_reader();
4203 for ($i = 0; $i < $l10n->total
; $i++
) {
4204 $orig = $l10n->get_original_string($i);
4205 $translation = __($orig);
4207 print T_js_decl($orig, $translation);
4211 function label_to_feed_id($label) {
4212 return LABEL_BASE_INDEX
- 1 - abs($label);
4215 function feed_to_label_id($feed) {
4216 return LABEL_BASE_INDEX
- 1 +
abs($feed);
4219 function format_libxml_error($error) {
4220 return T_sprintf("LibXML error %s at line %d (column %d): %s",
4221 $error->code
, $error->line
, $error->column
,