2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 119);
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 // startup_gettext() is called before session_start() so we can't rely
99 // on $_SESSION['language'] here.
101 if ($_COOKIE["ttrss_lang"] && $_COOKIE["ttrss_lang"] != "auto") {
102 $lang = $_COOKIE["ttrss_lang"];
106 if (defined('LC_MESSAGES')) {
107 _setlocale(LC_MESSAGES
, $lang);
108 } else if (defined('LC_ALL')) {
109 _setlocale(LC_ALL
, $lang);
112 _bindtextdomain("messages", "locale");
114 _textdomain("messages");
115 _bind_textdomain_codeset("messages", "UTF-8");
121 require_once 'db-prefs.php';
122 require_once 'version.php';
123 require_once 'ccache.php';
124 require_once 'labels.php';
126 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
127 ini_set('user_agent', SELF_USER_AGENT
);
129 require_once 'lib/pubsubhubbub/publisher.php';
131 $schema_version = false;
134 * Print a timestamped debug message.
136 * @param string $msg The debug message.
139 function _debug($msg, $show = true) {
141 $ts = strftime("%H:%M:%S", time());
142 if (function_exists('posix_getpid')) {
143 $ts = "$ts/" . posix_getpid();
146 if ($show && !(defined('QUIET') && QUIET
)) {
147 print "[$ts] $msg\n";
150 if (defined('LOGFILE')) {
151 $fp = fopen(LOGFILE
, 'a+');
154 fputs($fp, "[$ts] $msg\n");
162 * Purge a feed old posts.
164 * @param mixed $link A database connection.
165 * @param mixed $feed_id The id of the purged feed.
166 * @param mixed $purge_interval Olderness of purged posts.
167 * @param boolean $debug Set to True to enable the debug. False by default.
171 function purge_feed($feed_id, $purge_interval, $debug = false) {
173 if (!$purge_interval) $purge_interval = feed_purge_interval($feed_id);
178 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
182 if (db_num_rows($result) == 1) {
183 $owner_uid = db_fetch_result($result, 0, "owner_uid");
186 if ($purge_interval == -1 ||
!$purge_interval) {
188 ccache_update($feed_id, $owner_uid);
193 if (!$owner_uid) return;
195 if (FORCE_ARTICLE_PURGE
== 0) {
196 $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
199 $purge_unread = true;
200 $purge_interval = FORCE_ARTICLE_PURGE
;
203 if (!$purge_unread) $query_limit = " unread = false AND ";
205 if (DB_TYPE
== "pgsql") {
206 $pg_version = get_pgsql_version();
208 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
210 $result = db_query("DELETE FROM ttrss_user_entries WHERE
211 ttrss_entries.id = ref_id AND
213 feed_id = '$feed_id' AND
215 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
219 $result = db_query("DELETE FROM ttrss_user_entries
221 WHERE ttrss_entries.id = ref_id AND
223 feed_id = '$feed_id' AND
225 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
230 /* $result = db_query("DELETE FROM ttrss_user_entries WHERE
231 marked = false AND feed_id = '$feed_id' AND
232 (SELECT date_updated FROM ttrss_entries WHERE
233 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
235 $result = db_query("DELETE FROM ttrss_user_entries
236 USING ttrss_user_entries, ttrss_entries
237 WHERE ttrss_entries.id = ref_id AND
239 feed_id = '$feed_id' AND
241 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
244 $rows = db_affected_rows($result);
246 ccache_update($feed_id, $owner_uid);
249 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
253 } // function purge_feed
255 function feed_purge_interval($feed_id) {
257 $result = db_query("SELECT purge_interval, owner_uid FROM ttrss_feeds
258 WHERE id = '$feed_id'");
260 if (db_num_rows($result) == 1) {
261 $purge_interval = db_fetch_result($result, 0, "purge_interval");
262 $owner_uid = db_fetch_result($result, 0, "owner_uid");
264 if ($purge_interval == 0) $purge_interval = get_pref(
265 'PURGE_OLD_DAYS', $owner_uid);
267 return $purge_interval;
274 function purge_orphans($do_output = false) {
276 // purge orphaned posts in main content table
277 $result = db_query("DELETE FROM ttrss_entries WHERE
278 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
281 $rows = db_affected_rows($result);
282 _debug("Purged $rows orphaned posts.");
286 function get_feed_update_interval($feed_id) {
287 $result = db_query("SELECT owner_uid, update_interval FROM
288 ttrss_feeds WHERE id = '$feed_id'");
290 if (db_num_rows($result) == 1) {
291 $update_interval = db_fetch_result($result, 0, "update_interval");
292 $owner_uid = db_fetch_result($result, 0, "owner_uid");
294 if ($update_interval != 0) {
295 return $update_interval;
297 return get_pref('DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
305 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
307 global $fetch_last_error;
308 global $fetch_last_error_code;
309 global $fetch_last_content_type;
310 global $fetch_curl_used;
312 $url = str_replace(' ', '%20', $url);
314 if (!defined('NO_CURL') && function_exists('curl_init')) {
316 $fetch_curl_used = true;
318 if (ini_get("safe_mode") ||
ini_get("open_basedir")) {
319 $ch = curl_init(geturl($url));
321 $ch = curl_init($url);
324 if ($timestamp && !$post_query) {
325 curl_setopt($ch, CURLOPT_HTTPHEADER
,
326 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
329 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : FILE_FETCH_CONNECT_TIMEOUT
);
330 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : FILE_FETCH_TIMEOUT
);
331 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode") && !ini_get("open_basedir"));
332 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
333 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
334 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
335 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
336 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
337 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
338 curl_setopt($ch, CURLOPT_ENCODING
, "");
339 curl_setopt($ch, CURLOPT_REFERER
, $url);
342 curl_setopt($ch, CURLOPT_POST
, true);
343 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
347 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
349 $contents = @curl_exec
($ch);
351 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
352 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
353 $contents = @curl_exec
($ch);
356 if ($contents === false) {
357 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
362 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
363 $fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
365 $fetch_last_error_code = $http_code;
367 if ($http_code != 200 ||
$type && strpos($fetch_last_content_type, "$type") === false) {
368 if (curl_errno($ch) != 0) {
369 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
371 $fetch_last_error = "HTTP Code: $http_code";
382 $fetch_curl_used = false;
384 if ($login && $pass){
385 $url_parts = array();
387 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
389 $pass = urlencode($pass);
391 if ($url_parts[1] && $url_parts[2]) {
392 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
396 if (!$post_query && $timestamp) {
397 $context = stream_context_create(array(
400 'header' => "If-Modified-Since: ".gmdate("D, d M Y H:i:s \\G\\M\\T\r\n", $timestamp)
406 $old_error = error_get_last();
408 $data = @file_get_contents
($url, false, $context);
410 $fetch_last_content_type = false; // reset if no type was sent from server
411 if (isset($http_response_header) && is_array($http_response_header)) {
412 foreach ($http_response_header as $h) {
413 if (substr(strtolower($h), 0, 13) == 'content-type:') {
414 $fetch_last_content_type = substr($h, 14);
415 // don't abort here b/c there might be more than one
416 // e.g. if we were being redirected -- last one is the right one
419 if (substr(strtolower($h), 0, 7) == 'http/1.') {
420 $fetch_last_error_code = (int) substr($h, 9, 3);
426 $error = error_get_last();
428 if ($error['message'] != $old_error['message']) {
429 $fetch_last_error = $error["message"];
431 $fetch_last_error = "HTTP Code: $fetch_last_error_code";
440 * Try to determine the favicon URL for a feed.
441 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
442 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
444 * @param string $url A feed or page URL
446 * @return mixed The favicon URL, or false if none was found.
448 function get_favicon_url($url) {
450 $favicon_url = false;
452 if ($html = @fetch_file_contents
($url)) {
454 libxml_use_internal_errors(true);
456 $doc = new DOMDocument();
457 $doc->loadHTML($html);
458 $xpath = new DOMXPath($doc);
460 $base = $xpath->query('/html/head/base');
461 foreach ($base as $b) {
462 $url = $b->getAttribute("href");
466 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
467 if (count($entries) > 0) {
468 foreach ($entries as $entry) {
469 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
476 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
479 } // function get_favicon_url
481 function check_feed_favicon($site_url, $feed) {
482 # print "FAVICON [$site_url]: $favicon_url\n";
484 $icon_file = ICONS_DIR
. "/$feed.ico";
486 if (!file_exists($icon_file)) {
487 $favicon_url = get_favicon_url($site_url);
490 // Limiting to "image" type misses those served with text/plain
491 $contents = fetch_file_contents($favicon_url); // , "image");
494 // Crude image type matching.
495 // Patterns gleaned from the file(1) source code.
496 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
497 // 0 string \000\000\001\000 MS Windows icon resource
498 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
500 elseif (preg_match('/^GIF8/', $contents)) {
501 // 0 string GIF8 GIF image data
502 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
504 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
505 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
506 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
508 elseif (preg_match('/^\xff\xd8/', $contents)) {
509 // 0 beshort 0xffd8 JPEG image data
510 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
513 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
519 $fp = @fopen
($icon_file, "w");
522 fwrite($fp, $contents);
524 chmod($icon_file, 0644);
532 function print_select($id, $default, $values, $attributes = "") {
533 print "<select name=\"$id\" id=\"$id\" $attributes>";
534 foreach ($values as $v) {
536 $sel = "selected=\"1\"";
542 print "<option value=\"$v\" $sel>$v</option>";
547 function print_select_hash($id, $default, $values, $attributes = "") {
548 print "<select name=\"$id\" id='$id' $attributes>";
549 foreach (array_keys($values) as $v) {
551 $sel = 'selected="selected"';
557 print "<option $sel value=\"$v\">".$values[$v]."</option>";
563 function print_radio($id, $default, $true_is, $values, $attributes = "") {
564 foreach ($values as $v) {
571 if ($v == $true_is) {
572 $sel .= " value=\"1\"";
574 $sel .= " value=\"0\"";
577 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
578 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
583 function initialize_user_prefs($uid, $profile = false) {
585 $uid = db_escape_string($uid);
589 $profile_qpart = "AND profile IS NULL";
591 $profile_qpart = "AND profile = '$profile'";
594 if (get_schema_version() < 63) $profile_qpart = "";
598 $result = db_query("SELECT pref_name,def_value FROM ttrss_prefs");
600 $u_result = db_query("SELECT pref_name
601 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
603 $active_prefs = array();
605 while ($line = db_fetch_assoc($u_result)) {
606 array_push($active_prefs, $line["pref_name"]);
609 while ($line = db_fetch_assoc($result)) {
610 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
611 // print "adding " . $line["pref_name"] . "<br>";
613 $line["def_value"] = db_escape_string($line["def_value"]);
614 $line["pref_name"] = db_escape_string($line["pref_name"]);
616 if (get_schema_version() < 63) {
617 db_query("INSERT INTO ttrss_user_prefs
618 (owner_uid,pref_name,value) VALUES
619 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
622 db_query("INSERT INTO ttrss_user_prefs
623 (owner_uid,pref_name,value, profile) VALUES
624 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
634 function get_ssl_certificate_id() {
635 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
636 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
637 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
638 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
639 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
644 function authenticate_user($login, $password, $check_only = false) {
646 if (!SINGLE_USER_MODE
) {
649 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_AUTH_USER
) as $plugin) {
651 $user_id = (int) $plugin->authenticate($login, $password);
654 $_SESSION["auth_module"] = strtolower(get_class($plugin));
659 if ($user_id && !$check_only) {
662 $_SESSION["uid"] = $user_id;
663 $_SESSION["version"] = VERSION_STATIC
;
665 $result = db_query("SELECT login,access_level,pwd_hash FROM ttrss_users
666 WHERE id = '$user_id'");
668 $_SESSION["name"] = db_fetch_result($result, 0, "login");
669 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
670 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
672 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
675 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
676 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
677 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
679 $_SESSION["last_version_check"] = time();
681 initialize_user_prefs($_SESSION["uid"]);
690 $_SESSION["uid"] = 1;
691 $_SESSION["name"] = "admin";
692 $_SESSION["access_level"] = 10;
694 $_SESSION["hide_hello"] = true;
695 $_SESSION["hide_logout"] = true;
697 $_SESSION["auth_module"] = false;
699 if (!$_SESSION["csrf_token"]) {
700 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
703 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
705 initialize_user_prefs($_SESSION["uid"]);
711 function make_password($length = 8) {
714 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
718 while ($i < $length) {
719 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
721 if (!strstr($password, $char)) {
729 // this is called after user is created to initialize default feeds, labels
732 // user preferences are checked on every login, not here
734 function initialize_user($uid) {
736 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
737 values ('$uid', 'Tiny Tiny RSS: New Releases',
738 'http://tt-rss.org/releases.rss')");
740 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
741 values ('$uid', 'Tiny Tiny RSS: Forum',
742 'http://tt-rss.org/forum/rss.php')");
745 function logout_user() {
747 if (isset($_COOKIE[session_name()])) {
748 setcookie(session_name(), '', time()-42000, '/');
752 function validate_csrf($csrf_token) {
753 return $csrf_token == $_SESSION['csrf_token'];
756 function load_user_plugins($owner_uid) {
758 $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
760 PluginHost
::getInstance()->load($plugins, PluginHost
::KIND_USER
, $owner_uid);
762 if (get_schema_version() > 100) {
763 PluginHost
::getInstance()->load_data();
768 function login_sequence() {
769 if (SINGLE_USER_MODE
) {
771 authenticate_user("admin", null);
772 load_user_plugins($_SESSION["uid"]);
774 if (!validate_session()) $_SESSION["uid"] = false;
776 if (!$_SESSION["uid"]) {
778 if (AUTH_AUTO_LOGIN
&& authenticate_user(null, null)) {
779 $_SESSION["ref_schema_version"] = get_schema_version(true);
781 authenticate_user(null, null, true);
784 if (!$_SESSION["uid"]) {
786 setcookie(session_name(), '', time()-42000, '/');
793 /* bump login timestamp */
794 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
796 $_SESSION["last_login_update"] = time();
799 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
800 setcookie("ttrss_lang", $_SESSION["language"],
801 time() + SESSION_COOKIE_LIFETIME
);
804 if ($_SESSION["uid"]) {
805 load_user_plugins($_SESSION["uid"]);
809 db_query("DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
810 $_SESSION["uid"] . " AND
811 (SELECT COUNT(id) FROM ttrss_feeds WHERE
812 ttrss_feeds.id = feed_id) = 0");
814 db_query("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
815 $_SESSION["uid"] . " AND
816 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
817 ttrss_feed_categories.id = feed_id) = 0");
824 function truncate_string($str, $max_len, $suffix = '…') {
825 if (mb_strlen($str, "utf-8") > $max_len - 3) {
826 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
832 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
835 $source_tz = new DateTimeZone($source_tz);
836 } catch (Exception
$e) {
837 $source_tz = new DateTimeZone('UTC');
841 $dest_tz = new DateTimeZone($dest_tz);
842 } catch (Exception
$e) {
843 $dest_tz = new DateTimeZone('UTC');
846 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
847 return $dt->format('U') +
$dest_tz->getOffset($dt);
850 function make_local_datetime($timestamp, $long, $owner_uid = false,
851 $no_smart_dt = false) {
853 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
854 if (!$timestamp) $timestamp = '1970-01-01 0:00';
859 if (!$utc_tz) $utc_tz = new DateTimeZone('UTC');
861 $timestamp = substr($timestamp, 0, 19);
863 # We store date in UTC internally
864 $dt = new DateTime($timestamp, $utc_tz);
866 $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
868 if ($user_tz_string != 'Automatic') {
871 if (!$user_tz) $user_tz = new DateTimeZone($user_tz_string);
872 } catch (Exception
$e) {
876 $tz_offset = $user_tz->getOffset($dt);
878 $tz_offset = (int) -$_SESSION["clientTzOffset"];
881 $user_timestamp = $dt->format('U') +
$tz_offset;
884 return smart_date_time($user_timestamp,
885 $tz_offset, $owner_uid);
888 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
890 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
892 return date($format, $user_timestamp);
896 function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false) {
897 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
899 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
900 return date("G:i", $timestamp);
901 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
902 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
903 return date($format, $timestamp);
905 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
906 return date($format, $timestamp);
910 function sql_bool_to_bool($s) {
911 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
918 function bool_to_sql_bool($s) {
926 // Session caching removed due to causing wrong redirects to upgrade
927 // script when get_schema_version() is called on an obsolete session
928 // created on a previous schema version.
929 function get_schema_version($nocache = false) {
930 global $schema_version;
932 if (!$schema_version && !$nocache) {
933 $result = db_query("SELECT schema_version FROM ttrss_version");
934 $version = db_fetch_result($result, 0, "schema_version");
935 $schema_version = $version;
938 return $schema_version;
942 function sanity_check() {
943 require_once 'errors.php';
946 $schema_version = get_schema_version(true);
948 if ($schema_version != SCHEMA_VERSION
) {
952 if (DB_TYPE
== "mysql") {
953 $result = db_query("SELECT true", false);
954 if (db_num_rows($result) != 1) {
959 if (db_escape_string("testTEST") != "testTEST") {
963 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
966 function file_is_locked($filename) {
967 if (function_exists('flock')) {
968 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
970 if (flock($fp, LOCK_EX | LOCK_NB
)) {
981 return true; // consider the file always locked and skip the test
984 function make_lockfile($filename) {
985 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
987 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
988 if (function_exists('posix_getpid')) {
989 fwrite($fp, posix_getpid() . "\n");
997 function make_stampfile($filename) {
998 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
1000 if (flock($fp, LOCK_EX | LOCK_NB
)) {
1001 fwrite($fp, time() . "\n");
1002 flock($fp, LOCK_UN
);
1010 function sql_random_function() {
1011 if (DB_TYPE
== "mysql") {
1018 function catchup_feed($feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
1020 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
1022 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
1024 // Todo: all this interval stuff needs some generic generator function
1026 $date_qpart = "false";
1030 if (DB_TYPE
== "pgsql") {
1031 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
1033 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
1037 if (DB_TYPE
== "pgsql") {
1038 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
1040 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
1044 if (DB_TYPE
== "pgsql") {
1045 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
1047 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
1051 $date_qpart = "true";
1054 if (is_numeric($feed)) {
1060 $children = getChildCategories($feed, $owner_uid);
1061 array_push($children, $feed);
1063 $children = join(",", $children);
1065 $cat_qpart = "cat_id IN ($children)";
1067 $cat_qpart = "cat_id IS NULL";
1070 db_query("UPDATE ttrss_user_entries
1071 SET unread = false, last_read = NOW() WHERE ref_id IN
1073 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1074 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1075 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1077 } else if ($feed == -2) {
1079 db_query("UPDATE ttrss_user_entries
1080 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1081 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1082 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1085 } else if ($feed > 0) {
1087 db_query("UPDATE ttrss_user_entries
1088 SET unread = false, last_read = NOW() WHERE ref_id IN
1090 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1091 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1093 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1096 db_query("UPDATE ttrss_user_entries
1097 SET unread = false, last_read = NOW() WHERE ref_id IN
1099 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1100 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1104 db_query("UPDATE ttrss_user_entries
1105 SET unread = false, last_read = NOW() WHERE ref_id IN
1107 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1108 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1113 $intl = get_pref("FRESH_ARTICLE_MAX_AGE");
1115 if (DB_TYPE
== "pgsql") {
1116 $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
1118 $match_part = "date_entered > DATE_SUB(NOW(),
1119 INTERVAL $intl HOUR) ";
1122 db_query("UPDATE ttrss_user_entries
1123 SET unread = false, last_read = NOW() WHERE ref_id IN
1125 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1126 AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
1130 db_query("UPDATE ttrss_user_entries
1131 SET unread = false, last_read = NOW() WHERE ref_id IN
1133 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1134 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1137 } else if ($feed < LABEL_BASE_INDEX
) { // label
1139 $label_id = feed_to_label_id($feed);
1141 db_query("UPDATE ttrss_user_entries
1142 SET unread = false, last_read = NOW() WHERE ref_id IN
1144 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1145 AND label_id = '$label_id' AND ref_id = article_id
1146 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1150 ccache_update($feed, $owner_uid, $cat_view);
1153 db_query("UPDATE ttrss_user_entries
1154 SET unread = false, last_read = NOW() WHERE ref_id IN
1156 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1157 AND post_int_id = int_id AND tag_name = '$feed'
1158 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1163 function getAllCounters() {
1164 $data = getGlobalCounters();
1166 $data = array_merge($data, getVirtCounters());
1167 $data = array_merge($data, getLabelCounters());
1168 $data = array_merge($data, getFeedCounters($active_feed));
1169 $data = array_merge($data, getCategoryCounters());
1174 function getCategoryTitle($cat_id) {
1176 if ($cat_id == -1) {
1177 return __("Special");
1178 } else if ($cat_id == -2) {
1179 return __("Labels");
1182 $result = db_query("SELECT title FROM ttrss_feed_categories WHERE
1185 if (db_num_rows($result) == 1) {
1186 return db_fetch_result($result, 0, "title");
1188 return __("Uncategorized");
1194 function getCategoryCounters() {
1197 /* Labels category */
1199 $cv = array("id" => -2, "kind" => "cat",
1200 "counter" => getCategoryUnread(-2));
1202 array_push($ret_arr, $cv);
1204 $result = db_query("SELECT id AS cat_id, value AS unread,
1205 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1206 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1207 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1208 WHERE ttrss_cat_counters_cache.feed_id = id AND
1209 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1210 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1212 while ($line = db_fetch_assoc($result)) {
1213 $line["cat_id"] = (int) $line["cat_id"];
1215 if ($line["num_children"] > 0) {
1216 $child_counter = getCategoryChildrenUnread($line["cat_id"], $_SESSION["uid"]);
1221 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1222 "counter" => $line["unread"] +
$child_counter);
1224 array_push($ret_arr, $cv);
1227 /* Special case: NULL category doesn't actually exist in the DB */
1229 $cv = array("id" => 0, "kind" => "cat",
1230 "counter" => (int) ccache_find(0, $_SESSION["uid"], true));
1232 array_push($ret_arr, $cv);
1237 // only accepts real cats (>= 0)
1238 function getCategoryChildrenUnread($cat, $owner_uid = false) {
1239 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1241 $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1242 AND owner_uid = $owner_uid");
1246 while ($line = db_fetch_assoc($result)) {
1247 $unread +
= getCategoryUnread($line["id"], $owner_uid);
1248 $unread +
= getCategoryChildrenUnread($line["id"], $owner_uid);
1254 function getCategoryUnread($cat, $owner_uid = false) {
1256 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1261 $cat_query = "cat_id = '$cat'";
1263 $cat_query = "cat_id IS NULL";
1266 $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query
1267 AND owner_uid = " . $owner_uid);
1269 $cat_feeds = array();
1270 while ($line = db_fetch_assoc($result)) {
1271 array_push($cat_feeds, "feed_id = " . $line["id"]);
1274 if (count($cat_feeds) == 0) return 0;
1276 $match_part = implode(" OR ", $cat_feeds);
1278 $result = db_query("SELECT COUNT(int_id) AS unread
1279 FROM ttrss_user_entries
1280 WHERE unread = true AND ($match_part)
1281 AND owner_uid = " . $owner_uid);
1285 # this needs to be rewritten
1286 while ($line = db_fetch_assoc($result)) {
1287 $unread +
= $line["unread"];
1291 } else if ($cat == -1) {
1292 return getFeedUnread(-1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1293 } else if ($cat == -2) {
1295 $result = db_query("
1296 SELECT COUNT(unread) AS unread FROM
1297 ttrss_user_entries, ttrss_user_labels2
1298 WHERE article_id = ref_id AND unread = true
1299 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1301 $unread = db_fetch_result($result, 0, "unread");
1308 function getFeedUnread($feed, $is_cat = false) {
1309 return getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
1312 function getLabelUnread($label_id, $owner_uid = false) {
1313 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1315 $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1316 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1318 if (db_num_rows($result) != 0) {
1319 return db_fetch_result($result, 0, "unread");
1325 function getFeedArticles($feed, $is_cat = false, $unread_only = false,
1326 $owner_uid = false) {
1328 $n_feed = (int) $feed;
1329 $need_entries = false;
1331 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1334 $unread_qpart = "unread = true";
1336 $unread_qpart = "true";
1340 return getCategoryUnread($n_feed, $owner_uid);
1341 } else if ($n_feed == -6) {
1343 } else if ($feed != "0" && $n_feed == 0) {
1345 $feed = db_escape_string($feed);
1347 $result = db_query("SELECT SUM((SELECT COUNT(int_id)
1348 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1349 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1350 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1351 return db_fetch_result($result, 0, "count");
1353 } else if ($n_feed == -1) {
1354 $match_part = "marked = true";
1355 } else if ($n_feed == -2) {
1356 $match_part = "published = true";
1357 } else if ($n_feed == -3) {
1358 $match_part = "unread = true AND score >= 0";
1360 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1362 if (DB_TYPE
== "pgsql") {
1363 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1365 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1368 $need_entries = true;
1370 } else if ($n_feed == -4) {
1371 $match_part = "true";
1372 } else if ($n_feed >= 0) {
1375 $match_part = "feed_id = '$n_feed'";
1377 $match_part = "feed_id IS NULL";
1380 } else if ($feed < LABEL_BASE_INDEX
) {
1382 $label_id = feed_to_label_id($feed);
1384 return getLabelUnread($label_id, $owner_uid);
1390 if ($need_entries) {
1391 $from_qpart = "ttrss_user_entries,ttrss_entries";
1392 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1394 $from_qpart = "ttrss_user_entries";
1397 $query = "SELECT count(int_id) AS unread
1398 FROM $from_qpart WHERE
1399 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1401 //echo "[$feed/$query]\n";
1403 $result = db_query($query);
1407 $result = db_query("SELECT COUNT(post_int_id) AS unread
1408 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1409 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1410 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1413 $unread = db_fetch_result($result, 0, "unread");
1418 function getGlobalUnread($user_id = false) {
1421 $user_id = $_SESSION["uid"];
1424 $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1425 WHERE owner_uid = '$user_id' AND feed_id > 0");
1427 $c_id = db_fetch_result($result, 0, "c_id");
1432 function getGlobalCounters($global_unread = -1) {
1435 if ($global_unread == -1) {
1436 $global_unread = getGlobalUnread();
1439 $cv = array("id" => "global-unread",
1440 "counter" => (int) $global_unread);
1442 array_push($ret_arr, $cv);
1444 $result = db_query("SELECT COUNT(id) AS fn FROM
1445 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1447 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1449 $cv = array("id" => "subscribed-feeds",
1450 "counter" => (int) $subscribed_feeds);
1452 array_push($ret_arr, $cv);
1457 function getVirtCounters() {
1461 for ($i = 0; $i >= -4; $i--) {
1463 $count = getFeedUnread($i);
1465 $cv = array("id" => $i,
1466 "counter" => (int) $count);
1468 // if (get_pref('EXTENDED_FEEDLIST'))
1469 // $cv["xmsg"] = getFeedArticles($i)." ".__("total");
1471 array_push($ret_arr, $cv);
1474 $feeds = PluginHost
::getInstance()->get_feeds(-1);
1476 if (is_array($feeds)) {
1477 foreach ($feeds as $feed) {
1478 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1479 "counter" => $feed['sender']->get_unread($feed['id']));
1480 array_push($ret_arr, $cv);
1487 function getLabelCounters($descriptions = false) {
1491 $owner_uid = $_SESSION["uid"];
1493 $result = db_query("SELECT id,caption,COUNT(unread) AS unread
1494 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1495 (ttrss_labels2.id = label_id)
1496 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true
1497 AND ttrss_user_entries.owner_uid = $owner_uid)
1498 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1499 ttrss_labels2.caption");
1501 while ($line = db_fetch_assoc($result)) {
1503 $id = label_to_feed_id($line["id"]);
1505 $label_name = $line["caption"];
1506 $count = $line["unread"];
1508 $cv = array("id" => $id,
1509 "counter" => (int) $count);
1512 $cv["description"] = $label_name;
1514 // if (get_pref('EXTENDED_FEEDLIST'))
1515 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1517 array_push($ret_arr, $cv);
1523 function getFeedCounters($active_feed = false) {
1527 $query = "SELECT ttrss_feeds.id,
1529 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1530 last_error, value AS count
1531 FROM ttrss_feeds, ttrss_counters_cache
1532 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1533 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1534 AND ttrss_counters_cache.feed_id = id";
1536 $result = db_query($query);
1537 $fctrs_modified = false;
1539 while ($line = db_fetch_assoc($result)) {
1542 $count = $line["count"];
1543 $last_error = htmlspecialchars($line["last_error"]);
1545 $last_updated = make_local_datetime($line['last_updated'], false);
1547 $has_img = feed_has_icon($id);
1549 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1552 $cv = array("id" => $id,
1553 "updated" => $last_updated,
1554 "counter" => (int) $count,
1555 "has_img" => (int) $has_img);
1558 $cv["error"] = $last_error;
1560 // if (get_pref('EXTENDED_FEEDLIST'))
1561 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1563 if ($active_feed && $id == $active_feed)
1564 $cv["title"] = truncate_string($line["title"], 30);
1566 array_push($ret_arr, $cv);
1573 function get_pgsql_version() {
1574 $result = db_query("SELECT version() AS version");
1575 $version = explode(" ", db_fetch_result($result, 0, "version"));
1580 * @return array (code => Status code, message => error message if available)
1582 * 0 - OK, Feed already exists
1583 * 1 - OK, Feed added
1585 * 3 - URL content is HTML, no feeds available
1586 * 4 - URL content is HTML which contains multiple feeds.
1587 * Here you should call extractfeedurls in rpc-backend
1588 * to get all possible feeds.
1589 * 5 - Couldn't download the URL content.
1590 * 6 - Content is an invalid XML.
1592 function subscribe_to_feed($url, $cat_id = 0,
1593 $auth_login = '', $auth_pass = '') {
1595 global $fetch_last_error;
1597 require_once "include/rssfuncs.php";
1599 $url = fix_url($url);
1601 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1603 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1606 return array("code" => 5, "message" => $fetch_last_error);
1609 if (is_html($contents)) {
1610 $feedUrls = get_feeds_from_html($url, $contents);
1612 if (count($feedUrls) == 0) {
1613 return array("code" => 3);
1614 } else if (count($feedUrls) > 1) {
1615 return array("code" => 4, "feeds" => $feedUrls);
1617 //use feed url as new URL
1618 $url = key($feedUrls);
1621 /* libxml_use_internal_errors(true);
1622 $doc = new DOMDocument();
1623 $doc->loadXML($contents);
1624 $error = libxml_get_last_error();
1625 libxml_clear_errors();
1628 $error_message = format_libxml_error($error);
1630 return array("code" => 6, "message" => $error_message);
1633 if ($cat_id == "0" ||
!$cat_id) {
1634 $cat_qpart = "NULL";
1636 $cat_qpart = "'$cat_id'";
1640 "SELECT id FROM ttrss_feeds
1641 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1643 if (strlen(FEED_CRYPT_KEY
) > 0) {
1644 require_once "crypt.php";
1645 $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
1646 $auth_pass_encrypted = 'true';
1648 $auth_pass_encrypted = 'false';
1651 $auth_pass = db_escape_string($auth_pass);
1653 if (db_num_rows($result) == 0) {
1655 "INSERT INTO ttrss_feeds
1656 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
1657 VALUES ('".$_SESSION["uid"]."', '$url',
1658 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, $auth_pass_encrypted)");
1661 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1662 AND owner_uid = " . $_SESSION["uid"]);
1664 $feed_id = db_fetch_result($result, 0, "id");
1667 update_rss_feed($feed_id, true);
1670 return array("code" => 1);
1672 return array("code" => 0);
1676 function print_feed_select($id, $default_id = "",
1677 $attributes = "", $include_all_feeds = true,
1678 $root_id = false, $nest_level = 0) {
1681 print "<select id=\"$id\" name=\"$id\" $attributes>";
1682 if ($include_all_feeds) {
1683 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1684 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1688 if (get_pref('ENABLE_FEED_CATS')) {
1691 $parent_qpart = "parent_cat = '$root_id'";
1693 $parent_qpart = "parent_cat IS NULL";
1695 $result = db_query("SELECT id,title,
1696 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1697 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1698 FROM ttrss_feed_categories
1699 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1701 while ($line = db_fetch_assoc($result)) {
1703 for ($i = 0; $i < $nest_level; $i++
)
1704 $line["title"] = " - " . $line["title"];
1706 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1708 printf("<option $is_selected value='CAT:%d'>%s</option>",
1709 $line["id"], htmlspecialchars($line["title"]));
1711 if ($line["num_children"] > 0)
1712 print_feed_select($id, $default_id, $attributes,
1713 $include_all_feeds, $line["id"], $nest_level+
1);
1715 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1716 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1718 while ($fline = db_fetch_assoc($feed_result)) {
1719 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1721 $fline["title"] = " + " . $fline["title"];
1723 for ($i = 0; $i < $nest_level; $i++
)
1724 $fline["title"] = " - " . $fline["title"];
1726 printf("<option $is_selected value='%d'>%s</option>",
1727 $fline["id"], htmlspecialchars($fline["title"]));
1732 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1734 printf("<option $is_selected value='CAT:0'>%s</option>",
1735 __("Uncategorized"));
1737 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1738 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1740 while ($fline = db_fetch_assoc($feed_result)) {
1741 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1743 $fline["title"] = " + " . $fline["title"];
1745 for ($i = 0; $i < $nest_level; $i++
)
1746 $fline["title"] = " - " . $fline["title"];
1748 printf("<option $is_selected value='%d'>%s</option>",
1749 $fline["id"], htmlspecialchars($fline["title"]));
1754 $result = db_query("SELECT id,title FROM ttrss_feeds
1755 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1757 while ($line = db_fetch_assoc($result)) {
1759 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1761 printf("<option $is_selected value='%d'>%s</option>",
1762 $line["id"], htmlspecialchars($line["title"]));
1771 function print_feed_cat_select($id, $default_id,
1772 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1775 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1779 $parent_qpart = "parent_cat = '$root_id'";
1781 $parent_qpart = "parent_cat IS NULL";
1783 $result = db_query("SELECT id,title,
1784 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1785 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1786 FROM ttrss_feed_categories
1787 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1789 while ($line = db_fetch_assoc($result)) {
1790 if ($line["id"] == $default_id) {
1791 $is_selected = "selected=\"1\"";
1796 for ($i = 0; $i < $nest_level; $i++
)
1797 $line["title"] = " - " . $line["title"];
1800 printf("<option $is_selected value='%d'>%s</option>",
1801 $line["id"], htmlspecialchars($line["title"]));
1803 if ($line["num_children"] > 0)
1804 print_feed_cat_select($id, $default_id, $attributes,
1805 $include_all_cats, $line["id"], $nest_level+
1);
1809 if ($include_all_cats) {
1810 if (db_num_rows($result) > 0) {
1811 print "<option disabled=\"1\">--------</option>";
1814 if ($default_id == 0) {
1815 $is_selected = "selected=\"1\"";
1820 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1826 function checkbox_to_sql_bool($val) {
1827 return ($val == "on") ?
"true" : "false";
1830 function getFeedCatTitle($id) {
1832 return __("Special");
1833 } else if ($id < LABEL_BASE_INDEX
) {
1834 return __("Labels");
1835 } else if ($id > 0) {
1836 $result = db_query("SELECT ttrss_feed_categories.title
1837 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1838 cat_id = ttrss_feed_categories.id");
1839 if (db_num_rows($result) == 1) {
1840 return db_fetch_result($result, 0, "title");
1842 return __("Uncategorized");
1845 return "getFeedCatTitle($id) failed";
1850 function getFeedIcon($id) {
1853 return "images/archive.png";
1856 return "images/mark_set.svg";
1859 return "images/pub_set.svg";
1862 return "images/fresh.png";
1865 return "images/tag.png";
1868 return "images/recently_read.png";
1871 if ($id < LABEL_BASE_INDEX
) {
1872 return "images/label.png";
1874 if (file_exists(ICONS_DIR
. "/$id.ico"))
1875 return ICONS_URL
. "/$id.ico";
1883 function getFeedTitle($id, $cat = false) {
1885 return getCategoryTitle($id);
1886 } else if ($id == -1) {
1887 return __("Starred articles");
1888 } else if ($id == -2) {
1889 return __("Published articles");
1890 } else if ($id == -3) {
1891 return __("Fresh articles");
1892 } else if ($id == -4) {
1893 return __("All articles");
1894 } else if ($id === 0 ||
$id === "0") {
1895 return __("Archived articles");
1896 } else if ($id == -6) {
1897 return __("Recently read");
1898 } else if ($id < LABEL_BASE_INDEX
) {
1899 $label_id = feed_to_label_id($id);
1900 $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1901 if (db_num_rows($result) == 1) {
1902 return db_fetch_result($result, 0, "caption");
1904 return "Unknown label ($label_id)";
1907 } else if (is_numeric($id) && $id > 0) {
1908 $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'");
1909 if (db_num_rows($result) == 1) {
1910 return db_fetch_result($result, 0, "title");
1912 return "Unknown feed ($id)";
1919 function make_init_params() {
1922 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1923 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1924 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
1925 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1927 $params[strtolower($param)] = (int) get_pref($param);
1930 $params["icons_url"] = ICONS_URL
;
1931 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1932 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
1933 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
1934 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
1935 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1936 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1938 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1939 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1941 $max_feed_id = db_fetch_result($result, 0, "mid");
1942 $num_feeds = db_fetch_result($result, 0, "nf");
1944 $params["max_feed_id"] = (int) $max_feed_id;
1945 $params["num_feeds"] = (int) $num_feeds;
1947 $params["collapsed_feedlist"] = (int) get_pref("_COLLAPSED_FEEDLIST");
1948 $params["hotkeys"] = get_hotkeys_map();
1950 $params["csrf_token"] = $_SESSION["csrf_token"];
1951 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1953 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1958 function get_hotkeys_info() {
1960 __("Navigation") => array(
1961 "next_feed" => __("Open next feed"),
1962 "prev_feed" => __("Open previous feed"),
1963 "next_article" => __("Open next article"),
1964 "prev_article" => __("Open previous article"),
1965 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1966 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1967 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
1968 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
1969 "search_dialog" => __("Show search dialog")),
1970 __("Article") => array(
1971 "toggle_mark" => __("Toggle starred"),
1972 "toggle_publ" => __("Toggle published"),
1973 "toggle_unread" => __("Toggle unread"),
1974 "edit_tags" => __("Edit tags"),
1975 "dismiss_selected" => __("Dismiss selected"),
1976 "dismiss_read" => __("Dismiss read"),
1977 "open_in_new_window" => __("Open in new window"),
1978 "catchup_below" => __("Mark below as read"),
1979 "catchup_above" => __("Mark above as read"),
1980 "article_scroll_down" => __("Scroll down"),
1981 "article_scroll_up" => __("Scroll up"),
1982 "select_article_cursor" => __("Select article under cursor"),
1983 "email_article" => __("Email article"),
1984 "close_article" => __("Close/collapse article"),
1985 "toggle_expand" => __("Toggle article expansion (combined mode)"),
1986 "toggle_widescreen" => __("Toggle widescreen mode"),
1987 "toggle_embed_original" => __("Toggle embed original")),
1988 __("Article selection") => array(
1989 "select_all" => __("Select all articles"),
1990 "select_unread" => __("Select unread"),
1991 "select_marked" => __("Select starred"),
1992 "select_published" => __("Select published"),
1993 "select_invert" => __("Invert selection"),
1994 "select_none" => __("Deselect everything")),
1995 __("Feed") => array(
1996 "feed_refresh" => __("Refresh current feed"),
1997 "feed_unhide_read" => __("Un/hide read feeds"),
1998 "feed_subscribe" => __("Subscribe to feed"),
1999 "feed_edit" => __("Edit feed"),
2000 "feed_catchup" => __("Mark as read"),
2001 "feed_reverse" => __("Reverse headlines"),
2002 "feed_debug_update" => __("Debug feed update"),
2003 "catchup_all" => __("Mark all feeds as read"),
2004 "cat_toggle_collapse" => __("Un/collapse current category"),
2005 "toggle_combined_mode" => __("Toggle combined mode"),
2006 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
2007 __("Go to") => array(
2008 "goto_all" => __("All articles"),
2009 "goto_fresh" => __("Fresh"),
2010 "goto_marked" => __("Starred"),
2011 "goto_published" => __("Published"),
2012 "goto_tagcloud" => __("Tag cloud"),
2013 "goto_prefs" => __("Preferences")),
2014 __("Other") => array(
2015 "create_label" => __("Create label"),
2016 "create_filter" => __("Create filter"),
2017 "collapse_sidebar" => __("Un/collapse sidebar"),
2018 "help_dialog" => __("Show help dialog"))
2021 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_INFO
) as $plugin) {
2022 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
2028 function get_hotkeys_map() {
2030 // "navigation" => array(
2033 "n" => "next_article",
2034 "p" => "prev_article",
2035 "(38)|up" => "prev_article",
2036 "(40)|down" => "next_article",
2037 // "^(38)|Ctrl-up" => "prev_article_noscroll",
2038 // "^(40)|Ctrl-down" => "next_article_noscroll",
2039 "(191)|/" => "search_dialog",
2040 // "article" => array(
2041 "s" => "toggle_mark",
2042 "*s" => "toggle_publ",
2043 "u" => "toggle_unread",
2044 "*t" => "edit_tags",
2045 "*d" => "dismiss_selected",
2046 "*x" => "dismiss_read",
2047 "o" => "open_in_new_window",
2048 "c p" => "catchup_below",
2049 "c n" => "catchup_above",
2050 "*n" => "article_scroll_down",
2051 "*p" => "article_scroll_up",
2052 "*(38)|Shift+up" => "article_scroll_up",
2053 "*(40)|Shift+down" => "article_scroll_down",
2054 "a *w" => "toggle_widescreen",
2055 "a e" => "toggle_embed_original",
2056 "e" => "email_article",
2057 "a q" => "close_article",
2058 // "article_selection" => array(
2059 "a a" => "select_all",
2060 "a u" => "select_unread",
2061 "a *u" => "select_marked",
2062 "a p" => "select_published",
2063 "a i" => "select_invert",
2064 "a n" => "select_none",
2066 "f r" => "feed_refresh",
2067 "f a" => "feed_unhide_read",
2068 "f s" => "feed_subscribe",
2069 "f e" => "feed_edit",
2070 "f q" => "feed_catchup",
2071 "f x" => "feed_reverse",
2072 "f *d" => "feed_debug_update",
2073 "f *c" => "toggle_combined_mode",
2074 "f c" => "toggle_cdm_expanded",
2075 "*q" => "catchup_all",
2076 "x" => "cat_toggle_collapse",
2078 "g a" => "goto_all",
2079 "g f" => "goto_fresh",
2080 "g s" => "goto_marked",
2081 "g p" => "goto_published",
2082 "g t" => "goto_tagcloud",
2083 "g *p" => "goto_prefs",
2084 // "other" => array(
2085 "(9)|Tab" => "select_article_cursor", // tab
2086 "c l" => "create_label",
2087 "c f" => "create_filter",
2088 "c s" => "collapse_sidebar",
2089 "^(191)|Ctrl+/" => "help_dialog",
2092 if (get_pref('COMBINED_DISPLAY_MODE')) {
2093 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2094 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2097 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_MAP
) as $plugin) {
2098 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2101 $prefixes = array();
2103 foreach (array_keys($hotkeys) as $hotkey) {
2104 $pair = explode(" ", $hotkey, 2);
2106 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2107 array_push($prefixes, $pair[0]);
2111 return array($prefixes, $hotkeys);
2114 function make_runtime_info() {
2117 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2118 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2120 $max_feed_id = db_fetch_result($result, 0, "mid");
2121 $num_feeds = db_fetch_result($result, 0, "nf");
2123 $data["max_feed_id"] = (int) $max_feed_id;
2124 $data["num_feeds"] = (int) $num_feeds;
2126 $data['last_article_id'] = getLastArticleId();
2127 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
2129 $data['dep_ts'] = calculate_dep_timestamp();
2130 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2132 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2134 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2136 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2138 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2141 $stamp_delta = time() - $stamp;
2143 if ($stamp_delta > 1800) {
2147 $_SESSION["daemon_stamp_check"] = time();
2150 $data['daemon_stamp_ok'] = $stamp_check;
2152 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2154 $data['daemon_stamp'] = $stamp_fmt;
2159 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2160 $new_version_details = @check_for_update
();
2162 $data['new_version_available'] = (int) ($new_version_details != false);
2164 $_SESSION["last_version_check"] = time();
2165 $_SESSION["version_data"] = $new_version_details;
2171 function search_to_sql($search) {
2173 $search_query_part = "";
2175 $keywords = explode(" ", $search);
2176 $query_keywords = array();
2178 foreach ($keywords as $k) {
2179 if (strpos($k, "-") === 0) {
2186 $commandpair = explode(":", mb_strtolower($k), 2);
2188 switch ($commandpair[0]) {
2190 if ($commandpair[1]) {
2191 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
2192 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2194 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2195 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2199 if ($commandpair[1]) {
2200 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
2201 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2203 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2204 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2208 if ($commandpair[1]) {
2209 if ($commandpair[1] == "true")
2210 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2211 else if ($commandpair[1] == "false")
2212 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2214 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
2215 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2217 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2218 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2223 if ($commandpair[1]) {
2224 if ($commandpair[1] == "true")
2225 array_push($query_keywords, "($not (marked = true))");
2227 array_push($query_keywords, "($not (marked = false))");
2229 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2230 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2234 if ($commandpair[1]) {
2235 if ($commandpair[1] == "true")
2236 array_push($query_keywords, "($not (published = true))");
2238 array_push($query_keywords, "($not (published = false))");
2241 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2242 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2246 if (strpos($k, "@") === 0) {
2248 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
2249 $orig_ts = strtotime(substr($k, 1));
2250 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2252 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2254 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2256 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2257 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2262 $search_query_part = implode("AND", $query_keywords);
2264 return $search_query_part;
2267 function getParentCategories($cat, $owner_uid) {
2270 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
2271 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2273 while ($line = db_fetch_assoc($result)) {
2274 array_push($rv, $line["parent_cat"]);
2275 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
2281 function getChildCategories($cat, $owner_uid) {
2284 $result = db_query("SELECT id FROM ttrss_feed_categories
2285 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2287 while ($line = db_fetch_assoc($result)) {
2288 array_push($rv, $line["id"]);
2289 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
2295 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) {
2297 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2299 $ext_tables_part = "";
2303 if (SPHINX_ENABLED
) {
2304 $ids = join(",", @sphinx_search
($search, 0, 500));
2307 $search_query_part = "ref_id IN ($ids) AND ";
2309 $search_query_part = "ref_id = -1 AND ";
2312 $search_query_part = search_to_sql($search);
2313 $search_query_part .= " AND ";
2317 $search_query_part = "";
2322 if (DB_TYPE
== "pgsql") {
2323 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2325 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2328 $override_order = "updated DESC";
2330 $filter_query_part = filter_to_sql($filter, $owner_uid);
2332 // Try to check if SQL regexp implementation chokes on a valid regexp
2333 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
2334 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2335 WHERE $filter_query_part LIMIT 1", false);
2338 $test = db_fetch_result($result, 0, "true_val");
2341 $filter_query_part = "false AND";
2343 $filter_query_part .= " AND";
2346 $filter_query_part = "false AND";
2350 $filter_query_part = "";
2354 $since_id_part = "ttrss_entries.id > $since_id AND ";
2356 $since_id_part = "";
2359 $view_query_part = "";
2361 if ($view_mode == "adaptive") {
2363 $view_query_part = " ";
2364 } else if ($feed != -1) {
2366 $unread = getFeedUnread($feed, $cat_view);
2368 if ($cat_view && $feed > 0 && $include_children)
2369 $unread +
= getCategoryChildrenUnread($feed);
2372 $view_query_part = " unread = true AND ";
2377 if ($view_mode == "marked") {
2378 $view_query_part = " marked = true AND ";
2381 if ($view_mode == "has_note") {
2382 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2385 if ($view_mode == "published") {
2386 $view_query_part = " published = true AND ";
2389 if ($view_mode == "unread" && $feed != -6) {
2390 $view_query_part = " unread = true AND ";
2394 $limit_query_part = "LIMIT " . $limit;
2397 $allow_archived = false;
2399 $vfeed_query_part = "";
2401 // override query strategy and enable feed display when searching globally
2402 if ($search && $search_mode == "all_feeds") {
2403 $query_strategy_part = "true";
2404 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2406 } else if (!is_numeric($feed)) {
2407 $query_strategy_part = "true";
2408 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2409 id = feed_id) as feed_title,";
2410 } else if ($search && $search_mode == "this_cat") {
2411 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2414 if ($include_children) {
2415 $subcats = getChildCategories($feed, $owner_uid);
2416 array_push($subcats, $feed);
2417 $cats_qpart = join(",", $subcats);
2419 $cats_qpart = $feed;
2422 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2425 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2428 } else if ($feed > 0) {
2433 if ($include_children) {
2435 $subcats = getChildCategories($feed, $owner_uid);
2437 array_push($subcats, $feed);
2438 $query_strategy_part = "cat_id IN (".
2439 implode(",", $subcats).")";
2442 $query_strategy_part = "cat_id = '$feed'";
2446 $query_strategy_part = "cat_id IS NULL";
2449 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2452 $query_strategy_part = "feed_id = '$feed'";
2454 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2455 $query_strategy_part = "feed_id IS NULL";
2456 $allow_archived = true;
2457 } else if ($feed == 0 && $cat_view) { // uncategorized
2458 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2459 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2460 } else if ($feed == -1) { // starred virtual feed
2461 $query_strategy_part = "marked = true";
2462 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2463 $allow_archived = true;
2465 if (!$override_order) {
2466 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2469 } else if ($feed == -2) { // published virtual feed OR labels category
2472 $query_strategy_part = "published = true";
2473 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2474 $allow_archived = true;
2476 if (!$override_order) {
2477 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2481 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2483 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2485 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2486 ttrss_user_labels2.article_id = ref_id";
2489 } else if ($feed == -6) { // recently read
2490 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2491 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2492 $allow_archived = true;
2494 if (!$override_order) $override_order = "last_read DESC";
2495 } else if ($feed == -3) { // fresh virtual feed
2496 $query_strategy_part = "unread = true AND score >= 0";
2498 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
2500 if (DB_TYPE
== "pgsql") {
2501 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2503 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2506 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2507 } else if ($feed == -4) { // all articles virtual feed
2508 $allow_archived = true;
2509 $query_strategy_part = "true";
2510 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2511 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2512 $label_id = feed_to_label_id($feed);
2514 $query_strategy_part = "label_id = '$label_id' AND
2515 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2516 ttrss_user_labels2.article_id = ref_id";
2518 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2519 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2520 $allow_archived = true;
2523 $query_strategy_part = "true";
2526 $order_by = "score DESC, date_entered DESC, updated DESC";
2528 if ($view_mode == "unread_first") {
2529 $order_by = "unread DESC, $order_by";
2532 if ($override_order) {
2533 $order_by = $override_order;
2539 $feed_title = T_sprintf("Search results: %s", $search);
2542 $feed_title = getCategoryTitle($feed);
2544 if (is_numeric($feed) && $feed > 0) {
2545 $result = db_query("SELECT title,site_url,last_error,last_updated
2546 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2548 $feed_title = db_fetch_result($result, 0, "title");
2549 $feed_site_url = db_fetch_result($result, 0, "site_url");
2550 $last_error = db_fetch_result($result, 0, "last_error");
2551 $last_updated = db_fetch_result($result, 0, "last_updated");
2553 $feed_title = getFeedTitle($feed);
2558 $content_query_part = "content as content_preview, cached_content, ";
2560 if (is_numeric($feed)) {
2563 $feed_kind = "Feeds";
2565 $feed_kind = "Labels";
2568 if ($limit_query_part) {
2569 $offset_query_part = "OFFSET $offset";
2572 // proper override_order applied above
2573 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
2574 if (!$override_order) {
2575 $order_by = "ttrss_feeds.title, $order_by";
2577 $order_by = "ttrss_feeds.title, $override_order";
2581 if (!$allow_archived) {
2582 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2583 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2586 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2587 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2590 if ($vfeed_query_part)
2591 $vfeed_query_part .= "favicon_avg_color,";
2593 $query = "SELECT DISTINCT
2596 ttrss_entries.id,ttrss_entries.title,
2600 always_display_enclosures,
2607 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2608 last_marked, last_published,
2616 ttrss_user_entries.ref_id = ttrss_entries.id AND
2617 ttrss_user_entries.owner_uid = '$owner_uid' AND
2622 $query_strategy_part ORDER BY $order_by
2623 $limit_query_part $offset_query_part";
2625 if ($_REQUEST["debug"]) print $query;
2627 $result = db_query($query);
2632 $select_qpart = "SELECT DISTINCT " .
2636 "ttrss_entries.id as id," .
2649 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2650 "last_marked, last_published, " .
2653 $content_query_part .
2656 $feed_kind = "Tags";
2657 $all_tags = explode(",", $feed);
2658 if ($search_mode == 'any') {
2659 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2660 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2661 $where_qpart = " WHERE " .
2662 "ref_id = ttrss_entries.id AND " .
2663 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2664 "post_int_id = int_id AND $tag_sql AND " .
2666 $search_query_part .
2667 $query_strategy_part . " ORDER BY $order_by " .
2672 $sub_selects = array();
2673 $sub_ands = array();
2674 foreach ($all_tags as $term) {
2675 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");
2682 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2687 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2688 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2689 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2690 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2692 // error_log("TAG SQL: " . $tag_sql);
2693 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2695 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2696 $result = db_query($select_qpart . $from_qpart . $where_qpart);
2699 return array($result, $feed_title, $feed_site_url, $last_error, $last_updated);
2703 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false) {
2704 if (!$owner) $owner = $_SESSION["uid"];
2706 $res = trim($str); if (!$res) return '';
2708 if (strpos($res, "href=") === false)
2709 $res = rewrite_urls($res);
2711 $charset_hack = '<head>
2712 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2715 $res = trim($res); if (!$res) return '';
2717 libxml_use_internal_errors(true);
2719 $doc = new DOMDocument();
2720 $doc->loadHTML($charset_hack . $res);
2721 $xpath = new DOMXPath($doc);
2723 $entries = $xpath->query('(//a[@href]|//img[@src])');
2725 foreach ($entries as $entry) {
2729 if ($entry->hasAttribute('href'))
2730 $entry->setAttribute('href',
2731 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2733 if ($entry->hasAttribute('src')) {
2734 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2736 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2738 if (file_exists($cached_filename)) {
2739 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2742 $entry->setAttribute('src', $src);
2745 if ($entry->nodeName
== 'img') {
2746 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
2747 $force_remove_images ||
$_SESSION["bw_limit"]) {
2749 $p = $doc->createElement('p');
2751 $a = $doc->createElement('a');
2752 $a->setAttribute('href', $entry->getAttribute('src'));
2754 $a->appendChild(new DOMText($entry->getAttribute('src')));
2755 $a->setAttribute('target', '_blank');
2757 $p->appendChild($a);
2759 $entry->parentNode
->replaceChild($p, $entry);
2764 if (strtolower($entry->nodeName
) == "a") {
2765 $entry->setAttribute("target", "_blank");
2769 $entries = $xpath->query('//iframe');
2770 foreach ($entries as $entry) {
2771 $entry->setAttribute('sandbox', 'allow-scripts');
2775 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
2776 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
2777 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
2778 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
2779 'dt', 'em', 'footer', 'figure', 'figcaption',
2780 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
2781 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
2782 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
2783 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2784 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
2785 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2787 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2789 $disallowed_attributes = array('id', 'style', 'class');
2791 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_SANITIZE
) as $plugin) {
2792 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2793 if (is_array($retval)) {
2795 $allowed_elements = $retval[1];
2796 $disallowed_attributes = $retval[2];
2802 $doc->removeChild($doc->firstChild
); //remove doctype
2803 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2804 $res = $doc->saveHTML();
2808 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2809 $xpath = new DOMXPath($doc);
2810 $entries = $xpath->query('//*');
2812 foreach ($entries as $entry) {
2813 if (!in_array($entry->nodeName
, $allowed_elements)) {
2814 $entry->parentNode
->removeChild($entry);
2817 if ($entry->hasAttributes()) {
2818 $attrs_to_remove = array();
2820 foreach ($entry->attributes
as $attr) {
2822 if (strpos($attr->nodeName
, 'on') === 0) {
2823 array_push($attrs_to_remove, $attr);
2826 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2827 array_push($attrs_to_remove, $attr);
2831 foreach ($attrs_to_remove as $attr) {
2832 $entry->removeAttributeNode($attr);
2840 function check_for_update() {
2841 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2842 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2843 "&iid=" . sha1(SELF_URL_PATH
);
2845 $version_data = @fetch_file_contents
($version_url);
2847 if ($version_data) {
2848 $version_data = json_decode($version_data, true);
2849 if ($version_data && $version_data['version']) {
2851 if (version_compare(VERSION
, $version_data['version']) == -1) {
2852 return $version_data;
2860 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
2862 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2863 if (count($ids) == 0) return;
2867 foreach ($ids as $id) {
2868 array_push($tmp_ids, "ref_id = '$id'");
2871 $ids_qpart = join(" OR ", $tmp_ids);
2874 db_query("UPDATE ttrss_user_entries SET
2875 unread = false,last_read = NOW()
2876 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2877 } else if ($cmode == 1) {
2878 db_query("UPDATE ttrss_user_entries SET
2880 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2882 db_query("UPDATE ttrss_user_entries SET
2883 unread = NOT unread,last_read = NOW()
2884 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2889 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
2890 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2892 while ($line = db_fetch_assoc($result)) {
2893 ccache_update($line["feed_id"], $owner_uid);
2897 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
2899 $a_id = db_escape_string($id);
2901 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2903 $query = "SELECT DISTINCT tag_name,
2904 owner_uid as owner FROM
2905 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2906 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2908 $obj_id = md5("TAGS:$owner_uid:$id");
2911 /* check cache first */
2913 if ($tag_cache === false) {
2914 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
2915 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2917 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2921 $tags = explode(",", $tag_cache);
2924 /* do it the hard way */
2926 $tmp_result = db_query($query);
2928 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2929 array_push($tags, $tmp_line["tag_name"]);
2932 /* update the cache */
2934 $tags_str = db_escape_string(join(",", $tags));
2936 db_query("UPDATE ttrss_user_entries
2937 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2938 AND owner_uid = $owner_uid");
2944 function trim_array($array) {
2946 array_walk($tmp, 'trim');
2950 function tag_is_valid($tag) {
2951 if ($tag == '') return false;
2952 if (preg_match("/^[0-9]*$/", $tag)) return false;
2953 if (mb_strlen($tag) > 250) return false;
2955 if (function_exists('iconv')) {
2956 $tag = iconv("utf-8", "utf-8", $tag);
2959 if (!$tag) return false;
2964 function render_login_form() {
2965 header('Cache-Control: public');
2967 require_once "login_form.php";
2971 function format_warning($msg, $id = "") {
2973 return "<div class=\"warning\" id=\"$id\">
2974 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2977 function format_notice($msg, $id = "") {
2979 return "<div class=\"notice\" id=\"$id\">
2980 <span><img src=\"images/sign_info.svg\"></span><span>$msg</span></div>";
2983 function format_error($msg, $id = "") {
2985 return "<div class=\"error\" id=\"$id\">
2986 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2989 function print_notice($msg) {
2990 return print format_notice($msg);
2993 function print_warning($msg) {
2994 return print format_warning($msg);
2997 function print_error($msg) {
2998 return print format_error($msg);
3002 function T_sprintf() {
3003 $args = func_get_args();
3004 return vsprintf(__(array_shift($args)), $args);
3007 function format_inline_player($url, $ctype) {
3011 $url = htmlspecialchars($url);
3013 if (strpos($ctype, "audio/") === 0) {
3015 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
3016 $_SESSION["hasMp3"])) {
3018 $entry .= "<audio controls>
3019 <source type=\"$ctype\" src=\"$url\"></source>
3024 $entry .= "<object type=\"application/x-shockwave-flash\"
3025 data=\"lib/button/musicplayer.swf?song_url=$url\"
3026 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
3027 <param name=\"movie\"
3028 value=\"lib/button/musicplayer.swf?song_url=$url\" />
3032 if ($entry) $entry .= " <a target=\"_blank\"
3033 href=\"$url\">" . basename($url) . "</a>";
3041 /* $filename = substr($url, strrpos($url, "/")+1);
3043 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3044 $filename . " (" . $ctype . ")" . "</a>"; */
3048 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
3049 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3055 /* we can figure out feed_id from article id anyway, why do we
3056 * pass feed_id here? let's ignore the argument :(*/
3058 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3059 WHERE ref_id = '$id'");
3061 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
3063 $rv['feed_id'] = $feed_id;
3065 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3067 if ($mark_as_read) {
3068 $result = db_query("UPDATE ttrss_user_entries
3069 SET unread = false,last_read = NOW()
3070 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
3072 ccache_update($feed_id, $owner_uid);
3075 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,
3076 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
3077 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
3078 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
3079 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
3086 FROM ttrss_entries,ttrss_user_entries
3087 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3091 $line = db_fetch_assoc($result);
3093 $tag_cache = $line["tag_cache"];
3095 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
3096 unset($line["tag_cache"]);
3098 $line["content"] = sanitize($line["content"], false, $owner_uid, $line["site_url"]);
3100 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_RENDER_ARTICLE
) as $p) {
3101 $line = $p->hook_render_article($line);
3104 $num_comments = $line["num_comments"];
3105 $entry_comments = "";
3107 if ($num_comments > 0) {
3108 if ($line["comments"]) {
3109 $comments_url = htmlspecialchars($line["comments"]);
3111 $comments_url = htmlspecialchars($line["link"]);
3113 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3115 if ($line["comments"] && $line["link"] != $line["comments"]) {
3116 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3121 header("Content-Type: text/html");
3122 $rv['content'] .= "<html><head>
3123 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3124 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3125 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3126 </head><body id=\"ttrssZoom\">";
3129 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3131 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3133 $entry_author = $line["author"];
3135 if ($entry_author) {
3136 $entry_author = __(" - ") . $entry_author;
3139 $parsed_updated = make_local_datetime($line["updated"], true,
3142 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3144 if ($line["link"]) {
3145 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3146 title=\"".htmlspecialchars($line['title'])."\"
3148 htmlspecialchars($line["link"]) . "\">" .
3149 $line["title"] . "</a>" .
3150 "<span class='author'>$entry_author</span></div>";
3152 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3155 $tags_str = format_tags_string($line["tags"], $id);
3156 $tags_str_full = join(", ", $line["tags"]);
3158 if (!$tags_str_full) $tags_str_full = __("no tags");
3160 if (!$entry_comments) $entry_comments = " "; # placeholder
3162 $rv['content'] .= "<div class='postTags' style='float : right'>
3163 <img src='images/tag.png'
3164 class='tagsPic' alt='Tags' title='Tags'> ";
3167 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3168 <a title=\"".__('Edit tags for this article')."\"
3169 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3171 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3172 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3173 position=\"below\">$tags_str_full</div>";
3175 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_BUTTON
) as $p) {
3176 $rv['content'] .= $p->hook_article_button($line);
3180 $tags_str = strip_tags($tags_str);
3181 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3183 $rv['content'] .= "</div>";
3184 $rv['content'] .= "<div clear='both'>";
3186 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_LEFT_BUTTON
) as $p) {
3187 $rv['content'] .= $p->hook_article_left_button($line);
3190 $rv['content'] .= "$entry_comments</div>";
3192 if ($line["orig_feed_id"]) {
3194 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
3195 WHERE id = ".$line["orig_feed_id"]);
3197 if (db_num_rows($tmp_result) != 0) {
3199 $rv['content'] .= "<div clear='both'>";
3200 $rv['content'] .= __("Originally from:");
3202 $rv['content'] .= " ";
3204 $tmp_line = db_fetch_assoc($tmp_result);
3206 $rv['content'] .= "<a target='_blank'
3207 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3208 $tmp_line['title'] . "</a>";
3210 $rv['content'] .= " ";
3212 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3213 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3215 $rv['content'] .= "</div>";
3219 $rv['content'] .= "</div>";
3221 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3222 if ($line['note']) {
3223 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3225 $rv['content'] .= "</div>";
3227 $rv['content'] .= "<div class=\"postContent\">";
3229 $rv['content'] .= $line["content"];
3230 $rv['content'] .= format_article_enclosures($id,
3231 sql_bool_to_bool($line["always_display_enclosures"]),
3233 sql_bool_to_bool($line["hide_images"]));
3235 $rv['content'] .= "</div>";
3237 $rv['content'] .= "</div>";
3243 <div class='footer'>
3244 <button onclick=\"return window.close()\">".
3245 __("Close this window")."</button></div>";
3246 $rv['content'] .= "</body></html>";
3253 function print_checkpoint($n, $s) {
3254 $ts = microtime(true);
3255 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3259 function sanitize_tag($tag) {
3262 $tag = mb_strtolower($tag, 'utf-8');
3264 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3266 // $tag = str_replace('"', "", $tag);
3267 // $tag = str_replace("+", " ", $tag);
3268 $tag = str_replace("technorati tag: ", "", $tag);
3273 function get_self_url_prefix() {
3274 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3275 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3277 return SELF_URL_PATH
;
3282 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3284 * @return string The Mozilla Firefox feed adding URL.
3286 function add_feed_url() {
3287 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3289 $url_path = get_self_url_prefix() .
3290 "/public.php?op=subscribe&feed_url=%s";
3292 } // function add_feed_url
3294 function encrypt_password($pass, $salt = '', $mode2 = false) {
3295 if ($salt && $mode2) {
3296 return "MODE2:" . hash('sha256', $salt . $pass);
3298 return "SHA1X:" . sha1("$salt:$pass");
3300 return "SHA1:" . sha1($pass);
3302 } // function encrypt_password
3304 function load_filters($feed_id, $owner_uid, $action_id = false) {
3307 $cat_id = (int)getFeedCategory($feed_id);
3309 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
3310 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3312 $check_cats = join(",", array_merge(
3313 getParentCategories($cat_id, $owner_uid),
3316 while ($line = db_fetch_assoc($result)) {
3317 $filter_id = $line["id"];
3319 $result2 = db_query("SELECT
3320 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3321 FROM ttrss_filters2_rules AS r,
3322 ttrss_filter_types AS t
3324 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3325 (feed_id IS NULL OR feed_id = '$feed_id') AND
3326 filter_type = t.id AND filter_id = '$filter_id'");
3331 while ($rule_line = db_fetch_assoc($result2)) {
3332 # print_r($rule_line);
3335 $rule["reg_exp"] = $rule_line["reg_exp"];
3336 $rule["type"] = $rule_line["type_name"];
3337 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3339 array_push($rules, $rule);
3342 $result2 = db_query("SELECT a.action_param,t.name AS type_name
3343 FROM ttrss_filters2_actions AS a,
3344 ttrss_filter_actions AS t
3346 action_id = t.id AND filter_id = '$filter_id'");
3348 while ($action_line = db_fetch_assoc($result2)) {
3349 # print_r($action_line);
3352 $action["type"] = $action_line["type_name"];
3353 $action["param"] = $action_line["action_param"];
3355 array_push($actions, $action);
3360 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3361 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3362 $filter["rules"] = $rules;
3363 $filter["actions"] = $actions;
3365 if (count($rules) > 0 && count($actions) > 0) {
3366 array_push($filters, $filter);
3373 function get_score_pic($score) {
3375 return "score_high.png";
3376 } else if ($score > 0) {
3377 return "score_half_high.png";
3378 } else if ($score < -100) {
3379 return "score_low.png";
3380 } else if ($score < 0) {
3381 return "score_half_low.png";
3383 return "score_neutral.png";
3387 function feed_has_icon($id) {
3388 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3391 function init_plugins() {
3392 PluginHost
::getInstance()->load(PLUGINS
, PluginHost
::KIND_ALL
);
3397 function format_tags_string($tags, $id) {
3400 $tags_nolinks_str = "";
3406 $formatted_tags = array();
3408 foreach ($tags as $tag) {
3410 $tag_escaped = str_replace("'", "\\'", $tag);
3412 if (mb_strlen($tag) > 30) {
3413 $tag = truncate_string($tag, 30);
3416 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3418 array_push($formatted_tags, $tag_str);
3420 $tmp_tags_str = implode(", ", $formatted_tags);
3422 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3427 $tags_str = implode(", ", $formatted_tags);
3429 if ($num_tags < count($tags)) {
3430 $tags_str .= ", …";
3433 if ($num_tags == 0) {
3434 $tags_str = __("no tags");
3441 function format_article_labels($labels, $id) {
3443 if (!is_array($labels)) return '';
3447 foreach ($labels as $l) {
3448 $labels_str .= sprintf("<span class='hlLabelRef'
3449 style='color : %s; background-color : %s'>%s</span>",
3450 $l[2], $l[3], $l[1]);
3457 function format_article_note($id, $note, $allow_edit = true) {
3459 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3460 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3461 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3467 function get_feed_category($feed_cat, $parent_cat_id = false) {
3468 if ($parent_cat_id) {
3469 $parent_qpart = "parent_cat = '$parent_cat_id'";
3470 $parent_insert = "'$parent_cat_id'";
3472 $parent_qpart = "parent_cat IS NULL";
3473 $parent_insert = "NULL";
3477 "SELECT id FROM ttrss_feed_categories
3478 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3480 if (db_num_rows($result) == 0) {
3483 return db_fetch_result($result, 0, "id");
3487 function add_feed_category($feed_cat, $parent_cat_id = false) {
3489 if (!$feed_cat) return false;
3493 if ($parent_cat_id) {
3494 $parent_qpart = "parent_cat = '$parent_cat_id'";
3495 $parent_insert = "'$parent_cat_id'";
3497 $parent_qpart = "parent_cat IS NULL";
3498 $parent_insert = "NULL";
3501 $feed_cat = mb_substr($feed_cat, 0, 250);
3504 "SELECT id FROM ttrss_feed_categories
3505 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3507 if (db_num_rows($result) == 0) {
3510 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3511 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3521 function getArticleFeed($id) {
3522 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3523 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3525 if (db_num_rows($result) != 0) {
3526 return db_fetch_result($result, 0, "feed_id");
3533 * Fixes incomplete URLs by prepending "http://".
3534 * Also replaces feed:// with http://, and
3535 * prepends a trailing slash if the url is a domain name only.
3537 * @param string $url Possibly incomplete URL
3539 * @return string Fixed URL.
3541 function fix_url($url) {
3542 if (strpos($url, '://') === false) {
3543 $url = 'http://' . $url;
3544 } else if (substr($url, 0, 5) == 'feed:') {
3545 $url = 'http:' . substr($url, 5);
3548 //prepend slash if the URL has no slash in it
3549 // "http://www.example" -> "http://www.example/"
3550 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3554 if ($url != "http:///")
3560 function validate_feed_url($url) {
3561 $parts = parse_url($url);
3563 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3567 function get_article_enclosures($id) {
3569 $query = "SELECT * FROM ttrss_enclosures
3570 WHERE post_id = '$id' AND content_url != ''";
3574 $result = db_query($query);
3576 if (db_num_rows($result) > 0) {
3577 while ($line = db_fetch_assoc($result)) {
3578 array_push($rv, $line);
3585 function save_email_address($email) {
3586 // FIXME: implement persistent storage of emails
3588 if (!$_SESSION['stored_emails'])
3589 $_SESSION['stored_emails'] = array();
3591 if (!in_array($email, $_SESSION['stored_emails']))
3592 array_push($_SESSION['stored_emails'], $email);
3596 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
3598 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3600 $sql_is_cat = bool_to_sql_bool($is_cat);
3602 $result = db_query("SELECT access_key FROM ttrss_access_keys
3603 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3604 AND owner_uid = " . $owner_uid);
3606 if (db_num_rows($result) == 1) {
3607 return db_fetch_result($result, 0, "access_key");
3609 $key = db_escape_string(sha1(uniqid(rand(), true)));
3611 $result = db_query("INSERT INTO ttrss_access_keys
3612 (access_key, feed_id, is_cat, owner_uid)
3613 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3620 function get_feeds_from_html($url, $content)
3622 $url = fix_url($url);
3623 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3625 libxml_use_internal_errors(true);
3627 $doc = new DOMDocument();
3628 $doc->loadHTML($content);
3629 $xpath = new DOMXPath($doc);
3630 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3631 $feedUrls = array();
3632 foreach ($entries as $entry) {
3633 if ($entry->hasAttribute('href')) {
3634 $title = $entry->getAttribute('title');
3636 $title = $entry->getAttribute('type');
3638 $feedUrl = rewrite_relative_url(
3639 $baseUrl, $entry->getAttribute('href')
3641 $feedUrls[$feedUrl] = $title;
3647 function is_html($content) {
3648 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3651 function url_is_html($url, $login = false, $pass = false) {
3652 return is_html(fetch_file_contents($url, false, $login, $pass));
3655 function print_label_select($name, $value, $attributes = "") {
3657 $result = db_query("SELECT caption FROM ttrss_labels2
3658 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3660 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3661 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3663 while ($line = db_fetch_assoc($result)) {
3665 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3667 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3668 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3672 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3679 function format_article_enclosures($id, $always_display_enclosures,
3680 $article_content, $hide_images = false) {
3682 $result = get_article_enclosures($id);
3685 if (count($result) > 0) {
3687 $entries_html = array();
3689 $entries_inline = array();
3691 foreach ($result as $line) {
3693 $url = $line["content_url"];
3694 $ctype = $line["content_type"];
3696 if (!$ctype) $ctype = __("unknown type");
3698 $filename = substr($url, strrpos($url, "/")+
1);
3700 $player = format_inline_player($url, $ctype);
3702 if ($player) array_push($entries_inline, $player);
3704 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3705 # $filename . " (" . $ctype . ")" . "</a>";
3707 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3708 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3710 array_push($entries_html, $entry);
3714 $entry["type"] = $ctype;
3715 $entry["filename"] = $filename;
3716 $entry["url"] = $url;
3718 array_push($entries, $entry);
3721 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3722 if ($always_display_enclosures ||
3723 !preg_match("/<img/i", $article_content)) {
3725 foreach ($entries as $entry) {
3727 if (preg_match("/image/", $entry["type"]) ||
3728 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3730 if (!$hide_images) {
3732 alt=\"".htmlspecialchars($entry["filename"])."\"
3733 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3735 $rv .= "<p><a target=\"_blank\"
3736 href=\"".htmlspecialchars($entry["url"])."\"
3737 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3745 if (count($entries_inline) > 0) {
3746 $rv .= "<hr clear='both'/>";
3747 foreach ($entries_inline as $entry) { $rv .= $entry; };
3748 $rv .= "<hr clear='both'/>";
3751 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3752 "<option value=''>" . __('Attachments')."</option>";
3754 foreach ($entries as $entry) {
3755 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3765 function getLastArticleId() {
3766 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3767 WHERE owner_uid = " . $_SESSION["uid"]);
3769 if (db_num_rows($result) == 1) {
3770 return db_fetch_result($result, 0, "id");
3776 function build_url($parts) {
3777 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3781 * Converts a (possibly) relative URL to a absolute one.
3783 * @param string $url Base URL (i.e. from where the document is)
3784 * @param string $rel_url Possibly relative URL in the document
3786 * @return string Absolute URL
3788 function rewrite_relative_url($url, $rel_url) {
3789 if (strpos($rel_url, "magnet:") === 0) {
3791 } else if (strpos($rel_url, "://") !== false) {
3793 } else if (strpos($rel_url, "//") === 0) {
3794 # protocol-relative URL (rare but they exist)
3796 } else if (strpos($rel_url, "/") === 0)
3798 $parts = parse_url($url);
3799 $parts['path'] = $rel_url;
3801 return build_url($parts);
3804 $parts = parse_url($url);
3805 if (!isset($parts['path'])) {
3806 $parts['path'] = '/';
3808 $dir = $parts['path'];
3809 if (substr($dir, -1) !== '/') {
3810 $dir = dirname($parts['path']);
3811 $dir !== '/' && $dir .= '/';
3813 $parts['path'] = $dir . $rel_url;
3815 return build_url($parts);
3819 function sphinx_search($query, $offset = 0, $limit = 30) {
3820 require_once 'lib/sphinxapi.php';
3822 $sphinxClient = new SphinxClient();
3824 $sphinxpair = explode(":", SPHINX_SERVER
, 2);
3826 $sphinxClient->SetServer($sphinxpair[0], $sphinxpair[1]);
3827 $sphinxClient->SetConnectTimeout(1);
3829 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3830 'feed_title' => 20));
3832 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3833 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3834 $sphinxClient->SetLimits($offset, $limit, 1000);
3835 $sphinxClient->SetArrayResult(false);
3836 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3838 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3842 if (is_array($result['matches'])) {
3843 foreach (array_keys($result['matches']) as $int_id) {
3844 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3845 array_push($ids, $ref_id);
3852 function cleanup_tags($days = 14, $limit = 1000) {
3854 if (DB_TYPE
== "pgsql") {
3855 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3856 } else if (DB_TYPE
== "mysql") {
3857 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3862 while ($limit > 0) {
3865 $query = "SELECT ttrss_tags.id AS id
3866 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3867 WHERE post_int_id = int_id AND $interval_query AND
3868 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3870 $result = db_query($query);
3874 while ($line = db_fetch_assoc($result)) {
3875 array_push($ids, $line['id']);
3878 if (count($ids) > 0) {
3879 $ids = join(",", $ids);
3881 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
3882 $tags_deleted +
= db_affected_rows($tmp_result);
3887 $limit -= $limit_part;
3890 return $tags_deleted;
3893 function print_user_stylesheet() {
3894 $value = get_pref('USER_STYLESHEET');
3897 print "<style type=\"text/css\">";
3898 print str_replace("<br/>", "\n", $value);
3904 function rewrite_urls($html) {
3905 libxml_use_internal_errors(true);
3907 $charset_hack = '<head>
3908 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3911 $doc = new DOMDocument();
3912 $doc->loadHTML($charset_hack . $html);
3913 $xpath = new DOMXPath($doc);
3915 $entries = $xpath->query('//*/text()');
3917 foreach ($entries as $entry) {
3918 if (strstr($entry->wholeText
, "://") !== false) {
3919 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3920 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3922 if ($text != $entry->wholeText
) {
3923 $cdoc = new DOMDocument();
3924 $cdoc->loadHTML($charset_hack . $text);
3927 foreach ($cdoc->childNodes
as $cnode) {
3928 $cnode = $doc->importNode($cnode, true);
3931 $entry->parentNode
->insertBefore($cnode);
3935 $entry->parentNode
->removeChild($entry);
3941 $node = $doc->getElementsByTagName('body')->item(0);
3943 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3945 return $doc->saveXML($node);
3950 function filter_to_sql($filter, $owner_uid) {
3953 if (DB_TYPE
== "pgsql")
3956 $reg_qpart = "REGEXP";
3958 foreach ($filter["rules"] AS $rule) {
3959 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3960 $rule['reg_exp']) !== FALSE;
3962 if ($regexp_valid) {
3964 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
3966 switch ($rule["type"]) {
3968 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3969 $rule['reg_exp'] . "')";
3972 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3973 $rule['reg_exp'] . "')";
3976 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3977 $rule['reg_exp'] . "') OR LOWER(" .
3978 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3981 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3982 $rule['reg_exp'] . "')";
3985 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3986 $rule['reg_exp'] . "')";
3989 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3990 $rule['reg_exp'] . "')";
3994 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3996 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3997 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
4000 if (isset($rule["cat_id"])) {
4002 if ($rule["cat_id"] > 0) {
4003 $children = getChildCategories($rule["cat_id"], $owner_uid);
4004 array_push($children, $rule["cat_id"]);
4006 $children = join(",", $children);
4008 $cat_qpart = "cat_id IN ($children)";
4010 $cat_qpart = "cat_id IS NULL";
4013 $qpart .= " AND $cat_qpart";
4016 array_push($query, "($qpart)");
4021 if (count($query) > 0) {
4022 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
4024 $fullquery = "(false)";
4027 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
4032 if (!function_exists('gzdecode')) {
4033 function gzdecode($string) { // no support for 2nd argument
4034 return file_get_contents('compress.zlib://data:who/cares;base64,'.
4035 base64_encode($string));
4039 function get_random_bytes($length) {
4040 if (function_exists('openssl_random_pseudo_bytes')) {
4041 return openssl_random_pseudo_bytes($length);
4045 for ($i = 0; $i < $length; $i++
)
4046 $output .= chr(mt_rand(0, 255));
4052 function read_stdin() {
4053 $fp = fopen("php://stdin", "r");
4056 $line = trim(fgets($fp));
4064 function tmpdirname($path, $prefix) {
4065 // Use PHP's tmpfile function to create a temporary
4066 // directory name. Delete the file and keep the name.
4067 $tempname = tempnam($path,$prefix);
4071 if (!unlink($tempname))
4077 function getFeedCategory($feed) {
4078 $result = db_query("SELECT cat_id FROM ttrss_feeds
4079 WHERE id = '$feed'");
4081 if (db_num_rows($result) > 0) {
4082 return db_fetch_result($result, 0, "cat_id");
4089 function implements_interface($class, $interface) {
4090 return in_array($interface, class_implements($class));
4093 function geturl($url){
4095 if (!function_exists('curl_init'))
4096 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
);
4098 $curl = curl_init();
4099 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4100 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4101 $header[] = "Cache-Control: max-age=0";
4102 $header[] = "Connection: keep-alive";
4103 $header[] = "Keep-Alive: 300";
4104 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4105 $header[] = "Accept-Language: en-us,en;q=0.5";
4106 $header[] = "Pragma: ";
4108 curl_setopt($curl, CURLOPT_URL
, $url);
4109 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4110 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4111 curl_setopt($curl, CURLOPT_HEADER
, true);
4112 curl_setopt($curl, CURLOPT_REFERER
, $url);
4113 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4114 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4115 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4116 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4117 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4119 $html = curl_exec($curl);
4121 $status = curl_getinfo($curl);
4124 if($status['http_code']!=200){
4125 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4126 list($header) = explode("\r\n\r\n", $html, 2);
4128 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4129 $url = trim(str_replace($matches[1],"",$matches[0]));
4130 $url_parsed = parse_url($url);
4131 return (isset($url_parsed))?
geturl($url, $referer):'';
4134 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4135 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4136 # $handle = @fopen('./curl.error.log', 'a');
4137 # fwrite($handle, $line);
4143 function get_minified_js($files) {
4144 require_once 'lib/jshrink/Minifier.php';
4148 foreach ($files as $js) {
4149 if (!isset($_GET['debug'])) {
4150 $cached_file = CACHE_DIR
. "/js/$js.js";
4152 if (file_exists($cached_file) &&
4153 is_readable($cached_file) &&
4154 filemtime($cached_file) >= filemtime("js/$js.js")) {
4156 $rv .= file_get_contents($cached_file);
4159 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4160 file_put_contents($cached_file, $minified);
4164 $rv .= file_get_contents("js/$js.js");
4171 function stylesheet_tag($filename) {
4172 $timestamp = filemtime($filename);
4174 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4177 function javascript_tag($filename) {
4180 if (!(strpos($filename, "?") === FALSE)) {
4181 $query = substr($filename, strpos($filename, "?")+
1);
4182 $filename = substr($filename, 0, strpos($filename, "?"));
4185 $timestamp = filemtime($filename);
4187 if ($query) $timestamp .= "&$query";
4189 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4192 function calculate_dep_timestamp() {
4193 $files = array_merge(glob("js/*.js"), glob("*.css"));
4197 foreach ($files as $file) {
4198 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4204 function T_js_decl($s1, $s2) {
4206 $s1 = preg_replace("/\n/", "", $s1);
4207 $s2 = preg_replace("/\n/", "", $s2);
4209 $s1 = preg_replace("/\"/", "\\\"", $s1);
4210 $s2 = preg_replace("/\"/", "\\\"", $s2);
4212 return "T_messages[\"$s1\"] = \"$s2\";\n";
4216 function init_js_translations() {
4218 print 'var T_messages = new Object();
4221 if (T_messages[msg]) {
4222 return T_messages[msg];
4228 function ngettext(msg1, msg2, n) {
4229 return (parseInt(n) > 1) ? msg2 : msg1;
4232 $l10n = _get_reader();
4234 for ($i = 0; $i < $l10n->total
; $i++
) {
4235 $orig = $l10n->get_original_string($i);
4236 $translation = __($orig);
4238 print T_js_decl($orig, $translation);
4242 function label_to_feed_id($label) {
4243 return LABEL_BASE_INDEX
- 1 - abs($label);
4246 function feed_to_label_id($feed) {
4247 return LABEL_BASE_INDEX
- 1 +
abs($feed);
4250 function format_libxml_error($error) {
4251 return T_sprintf("LibXML error %s at line %d (column %d): %s",
4252 $error->code
, $error->line
, $error->column
,