2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 118);
5 define('LABEL_BASE_INDEX', -1024);
6 define('PLUGIN_FEED_BASE_INDEX', -128);
8 $fetch_last_error = false;
9 $fetch_last_error_code = false;
10 $fetch_last_content_type = false;
11 $fetch_curl_used = false;
13 mb_internal_encoding("UTF-8");
14 date_default_timezone_set('UTC');
15 if (defined('E_DEPRECATED')) {
16 error_reporting(E_ALL
& ~E_NOTICE
& ~E_DEPRECATED
);
18 error_reporting(E_ALL
& ~E_NOTICE
);
21 require_once 'config.php';
24 * Define a constant if not already defined
26 * @param string $name The constant name.
27 * @param mixed $value The constant value.
29 * @return boolean True if defined successfully or not.
31 function define_default($name, $value) {
32 defined($name) or define($name, $value);
35 ///// Some defaults that you can override in config.php //////
37 define_default('FEED_FETCH_TIMEOUT', 45);
38 // How may seconds to wait for response when requesting feed from a site
39 define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
40 // How may seconds to wait for response when requesting feed from a
41 // site when that feed wasn't cached before
42 define_default('FILE_FETCH_TIMEOUT', 45);
43 // Default timeout when fetching files from remote sites
44 define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
45 // How many seconds to wait for initial response from website when
46 // fetching files from remote sites
48 if (DB_TYPE
== "pgsql") {
49 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
51 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
55 * Return available translations names.
58 * @return array A array of available translations.
60 function get_translations() {
62 "auto" => "Detect automatically",
68 "fr_FR" => "Français",
69 "hu_HU" => "Magyar (Hungarian)",
70 "it_IT" => "Italiano",
71 "ja_JP" => "日本語 (Japanese)",
72 "lv_LV" => "Latviešu",
73 "nb_NO" => "Norwegian bokmål",
77 "pt_BR" => "Portuguese/Brazil",
78 "zh_CN" => "Simplified Chinese",
85 require_once "lib/accept-to-gettext.php";
86 require_once "lib/gettext/gettext.inc";
89 function startup_gettext() {
91 # Get locale from Accept-Language header
92 $lang = al2gt(array_keys(get_translations()), "text/html");
94 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
95 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
98 if ($_SESSION["language"] && $_SESSION["language"] != "auto") {
99 $lang = $_SESSION["language"];
103 if (defined('LC_MESSAGES')) {
104 _setlocale(LC_MESSAGES
, $lang);
105 } else if (defined('LC_ALL')) {
106 _setlocale(LC_ALL
, $lang);
109 _bindtextdomain("messages", "locale");
111 _textdomain("messages");
112 _bind_textdomain_codeset("messages", "UTF-8");
118 require_once 'db-prefs.php';
119 require_once 'version.php';
120 require_once 'ccache.php';
121 require_once 'labels.php';
123 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
124 ini_set('user_agent', SELF_USER_AGENT
);
126 require_once 'lib/pubsubhubbub/publisher.php';
129 $utc_tz = new DateTimeZone('UTC');
130 $schema_version = false;
132 global $_debug_enabled;
133 $_debug_enabled = true;
136 * Print a timestamped debug message.
138 * @param string $msg The debug message.
141 function _debug($msg) {
142 global $_debug_enabled;
144 if (!$_debug_enabled) return;
146 $ts = strftime("%H:%M:%S", time());
147 if (function_exists('posix_getpid')) {
148 $ts = "$ts/" . posix_getpid();
151 if (!(defined('QUIET') && QUIET
)) {
152 print "[$ts] $msg\n";
155 if (defined('LOGFILE')) {
156 $fp = fopen(LOGFILE
, 'a+');
159 fputs($fp, "[$ts] $msg\n");
166 function _debug_enable($enabled) {
167 global $_debug_enabled;
169 $old = $_debug_enabled;
170 $_debug_enabled = $enabled;
176 * Purge a feed old posts.
178 * @param mixed $link A database connection.
179 * @param mixed $feed_id The id of the purged feed.
180 * @param mixed $purge_interval Olderness of purged posts.
181 * @param boolean $debug Set to True to enable the debug. False by default.
185 function purge_feed($feed_id, $purge_interval, $debug = false) {
187 if (!$purge_interval) $purge_interval = feed_purge_interval($feed_id);
192 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
196 if (db_num_rows($result) == 1) {
197 $owner_uid = db_fetch_result($result, 0, "owner_uid");
200 if ($purge_interval == -1 ||
!$purge_interval) {
202 ccache_update($feed_id, $owner_uid);
207 if (!$owner_uid) return;
209 if (FORCE_ARTICLE_PURGE
== 0) {
210 $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
213 $purge_unread = true;
214 $purge_interval = FORCE_ARTICLE_PURGE
;
217 if (!$purge_unread) $query_limit = " unread = false AND ";
219 if (DB_TYPE
== "pgsql") {
220 $pg_version = get_pgsql_version();
222 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
224 $result = db_query("DELETE FROM ttrss_user_entries WHERE
225 ttrss_entries.id = ref_id AND
227 feed_id = '$feed_id' AND
229 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
233 $result = db_query("DELETE FROM ttrss_user_entries
235 WHERE ttrss_entries.id = ref_id AND
237 feed_id = '$feed_id' AND
239 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
244 /* $result = db_query("DELETE FROM ttrss_user_entries WHERE
245 marked = false AND feed_id = '$feed_id' AND
246 (SELECT date_updated FROM ttrss_entries WHERE
247 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
249 $result = db_query("DELETE FROM ttrss_user_entries
250 USING ttrss_user_entries, ttrss_entries
251 WHERE ttrss_entries.id = ref_id AND
253 feed_id = '$feed_id' AND
255 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
258 $rows = db_affected_rows($result);
260 ccache_update($feed_id, $owner_uid);
263 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
267 } // function purge_feed
269 function feed_purge_interval($feed_id) {
271 $result = db_query("SELECT purge_interval, owner_uid FROM ttrss_feeds
272 WHERE id = '$feed_id'");
274 if (db_num_rows($result) == 1) {
275 $purge_interval = db_fetch_result($result, 0, "purge_interval");
276 $owner_uid = db_fetch_result($result, 0, "owner_uid");
278 if ($purge_interval == 0) $purge_interval = get_pref(
279 'PURGE_OLD_DAYS', $owner_uid);
281 return $purge_interval;
288 function purge_orphans($do_output = false) {
290 // purge orphaned posts in main content table
291 $result = db_query("DELETE FROM ttrss_entries WHERE
292 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
295 $rows = db_affected_rows($result);
296 _debug("Purged $rows orphaned posts.");
300 function get_feed_update_interval($feed_id) {
301 $result = db_query("SELECT owner_uid, update_interval FROM
302 ttrss_feeds WHERE id = '$feed_id'");
304 if (db_num_rows($result) == 1) {
305 $update_interval = db_fetch_result($result, 0, "update_interval");
306 $owner_uid = db_fetch_result($result, 0, "owner_uid");
308 if ($update_interval != 0) {
309 return $update_interval;
311 return get_pref('DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
319 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
321 global $fetch_last_error;
322 global $fetch_last_error_code;
323 global $fetch_last_content_type;
324 global $fetch_curl_used;
326 $url = str_replace(' ', '%20', $url);
328 if (!defined('NO_CURL') && function_exists('curl_init')) {
330 $fetch_curl_used = true;
332 if (ini_get("safe_mode") ||
ini_get("open_basedir")) {
333 $ch = curl_init(geturl($url));
335 $ch = curl_init($url);
339 curl_setopt($ch, CURLOPT_HTTPHEADER
,
340 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
343 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : FILE_FETCH_CONNECT_TIMEOUT
);
344 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : FILE_FETCH_TIMEOUT
);
345 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode") && !ini_get("open_basedir"));
346 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
347 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
348 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
349 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
350 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
351 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
352 curl_setopt($ch, CURLOPT_ENCODING
, "");
353 curl_setopt($ch, CURLOPT_REFERER
, $url);
356 curl_setopt($ch, CURLOPT_POST
, true);
357 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
361 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
363 $contents = @curl_exec
($ch);
365 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
366 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
367 $contents = @curl_exec
($ch);
370 if ($contents === false) {
371 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
376 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
377 $fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
379 $fetch_last_error_code = $http_code;
381 if ($http_code != 200 ||
$type && strpos($fetch_last_content_type, "$type") === false) {
382 if (curl_errno($ch) != 0) {
383 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
385 $fetch_last_error = "HTTP Code: $http_code";
396 $fetch_curl_used = false;
398 if ($login && $pass){
399 $url_parts = array();
401 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
403 $pass = urlencode($pass);
405 if ($url_parts[1] && $url_parts[2]) {
406 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
410 $data = @file_get_contents
($url);
412 $fetch_last_content_type = false; // reset if no type was sent from server
413 if (is_array($http_response_header)) {
414 foreach ($http_response_header as $h) {
415 if (substr(strtolower($h), 0, 13) == 'content-type:') {
416 $fetch_last_content_type = substr($h, 14);
417 // don't abort here b/c there might be more than one
418 // e.g. if we were being redirected -- last one is the right one
423 if (!$data && function_exists('error_get_last')) {
424 $error = error_get_last();
425 $fetch_last_error = $error["message"];
433 * Try to determine the favicon URL for a feed.
434 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
435 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
437 * @param string $url A feed or page URL
439 * @return mixed The favicon URL, or false if none was found.
441 function get_favicon_url($url) {
443 $favicon_url = false;
445 if ($html = @fetch_file_contents
($url)) {
447 libxml_use_internal_errors(true);
449 $doc = new DOMDocument();
450 $doc->loadHTML($html);
451 $xpath = new DOMXPath($doc);
453 $base = $xpath->query('/html/head/base');
454 foreach ($base as $b) {
455 $url = $b->getAttribute("href");
459 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
460 if (count($entries) > 0) {
461 foreach ($entries as $entry) {
462 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
469 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
472 } // function get_favicon_url
474 function check_feed_favicon($site_url, $feed) {
475 # print "FAVICON [$site_url]: $favicon_url\n";
477 $icon_file = ICONS_DIR
. "/$feed.ico";
479 if (!file_exists($icon_file)) {
480 $favicon_url = get_favicon_url($site_url);
483 // Limiting to "image" type misses those served with text/plain
484 $contents = fetch_file_contents($favicon_url); // , "image");
487 // Crude image type matching.
488 // Patterns gleaned from the file(1) source code.
489 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
490 // 0 string \000\000\001\000 MS Windows icon resource
491 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
493 elseif (preg_match('/^GIF8/', $contents)) {
494 // 0 string GIF8 GIF image data
495 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
497 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
498 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
499 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
501 elseif (preg_match('/^\xff\xd8/', $contents)) {
502 // 0 beshort 0xffd8 JPEG image data
503 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
506 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
512 $fp = @fopen
($icon_file, "w");
515 fwrite($fp, $contents);
517 chmod($icon_file, 0644);
525 function print_select($id, $default, $values, $attributes = "") {
526 print "<select name=\"$id\" id=\"$id\" $attributes>";
527 foreach ($values as $v) {
529 $sel = "selected=\"1\"";
535 print "<option value=\"$v\" $sel>$v</option>";
540 function print_select_hash($id, $default, $values, $attributes = "") {
541 print "<select name=\"$id\" id='$id' $attributes>";
542 foreach (array_keys($values) as $v) {
544 $sel = 'selected="selected"';
550 print "<option $sel value=\"$v\">".$values[$v]."</option>";
556 function print_radio($id, $default, $true_is, $values, $attributes = "") {
557 foreach ($values as $v) {
564 if ($v == $true_is) {
565 $sel .= " value=\"1\"";
567 $sel .= " value=\"0\"";
570 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
571 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
576 function initialize_user_prefs($uid, $profile = false) {
578 $uid = db_escape_string($uid);
582 $profile_qpart = "AND profile IS NULL";
584 $profile_qpart = "AND profile = '$profile'";
587 if (get_schema_version() < 63) $profile_qpart = "";
591 $result = db_query("SELECT pref_name,def_value FROM ttrss_prefs");
593 $u_result = db_query("SELECT pref_name
594 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
596 $active_prefs = array();
598 while ($line = db_fetch_assoc($u_result)) {
599 array_push($active_prefs, $line["pref_name"]);
602 while ($line = db_fetch_assoc($result)) {
603 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
604 // print "adding " . $line["pref_name"] . "<br>";
606 $line["def_value"] = db_escape_string($line["def_value"]);
607 $line["pref_name"] = db_escape_string($line["pref_name"]);
609 if (get_schema_version() < 63) {
610 db_query("INSERT INTO ttrss_user_prefs
611 (owner_uid,pref_name,value) VALUES
612 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
615 db_query("INSERT INTO ttrss_user_prefs
616 (owner_uid,pref_name,value, profile) VALUES
617 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
627 function get_ssl_certificate_id() {
628 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
629 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
630 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
631 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
632 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
637 function authenticate_user($login, $password, $check_only = false) {
639 if (!SINGLE_USER_MODE
) {
642 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_AUTH_USER
) as $plugin) {
644 $user_id = (int) $plugin->authenticate($login, $password);
647 $_SESSION["auth_module"] = strtolower(get_class($plugin));
652 if ($user_id && !$check_only) {
655 $_SESSION["uid"] = $user_id;
656 $_SESSION["version"] = VERSION
;
658 $result = db_query("SELECT login,access_level,pwd_hash FROM ttrss_users
659 WHERE id = '$user_id'");
661 $_SESSION["name"] = db_fetch_result($result, 0, "login");
662 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
663 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
665 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
668 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
669 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
670 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
672 $_SESSION["last_version_check"] = time();
674 initialize_user_prefs($_SESSION["uid"]);
683 $_SESSION["uid"] = 1;
684 $_SESSION["name"] = "admin";
685 $_SESSION["access_level"] = 10;
687 $_SESSION["hide_hello"] = true;
688 $_SESSION["hide_logout"] = true;
690 $_SESSION["auth_module"] = false;
692 if (!$_SESSION["csrf_token"]) {
693 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
696 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
698 initialize_user_prefs($_SESSION["uid"]);
704 function make_password($length = 8) {
707 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
711 while ($i < $length) {
712 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
714 if (!strstr($password, $char)) {
722 // this is called after user is created to initialize default feeds, labels
725 // user preferences are checked on every login, not here
727 function initialize_user($uid) {
729 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
730 values ('$uid', 'Tiny Tiny RSS: New Releases',
731 'http://tt-rss.org/releases.rss')");
733 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
734 values ('$uid', 'Tiny Tiny RSS: Forum',
735 'http://tt-rss.org/forum/rss.php')");
738 function logout_user() {
740 if (isset($_COOKIE[session_name()])) {
741 setcookie(session_name(), '', time()-42000, '/');
745 function validate_csrf($csrf_token) {
746 return $csrf_token == $_SESSION['csrf_token'];
749 function load_user_plugins($owner_uid) {
751 $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
753 PluginHost
::getInstance()->load($plugins, PluginHost
::KIND_USER
, $owner_uid);
755 if (get_schema_version() > 100) {
756 PluginHost
::getInstance()->load_data();
761 function login_sequence() {
762 if (SINGLE_USER_MODE
) {
764 authenticate_user("admin", null);
765 load_user_plugins($_SESSION["uid"]);
767 if (!validate_session()) $_SESSION["uid"] = false;
769 if (!$_SESSION["uid"]) {
771 if (AUTH_AUTO_LOGIN
&& authenticate_user(null, null)) {
772 $_SESSION["ref_schema_version"] = get_schema_version(true);
774 authenticate_user(null, null, true);
777 if (!$_SESSION["uid"]) {
779 setcookie(session_name(), '', time()-42000, '/');
786 /* bump login timestamp */
787 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
789 $_SESSION["last_login_update"] = time();
792 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
793 setcookie("ttrss_lang", $_SESSION["language"],
794 time() + SESSION_COOKIE_LIFETIME
);
797 if ($_SESSION["uid"]) {
798 load_user_plugins($_SESSION["uid"]);
802 db_query("DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
803 $_SESSION["uid"] . " AND
804 (SELECT COUNT(id) FROM ttrss_feeds WHERE
805 ttrss_feeds.id = feed_id) = 0");
807 db_query("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
808 $_SESSION["uid"] . " AND
809 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
810 ttrss_feed_categories.id = feed_id) = 0");
817 function truncate_string($str, $max_len, $suffix = '…') {
818 if (mb_strlen($str, "utf-8") > $max_len - 3) {
819 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
825 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
828 $source_tz = new DateTimeZone($source_tz);
829 } catch (Exception
$e) {
830 $source_tz = new DateTimeZone('UTC');
834 $dest_tz = new DateTimeZone($dest_tz);
835 } catch (Exception
$e) {
836 $dest_tz = new DateTimeZone('UTC');
839 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
840 return $dt->format('U') +
$dest_tz->getOffset($dt);
843 function make_local_datetime($timestamp, $long, $owner_uid = false,
844 $no_smart_dt = false) {
846 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
847 if (!$timestamp) $timestamp = '1970-01-01 0:00';
852 # We store date in UTC internally
853 $dt = new DateTime($timestamp, $utc_tz);
855 if ($tz_offset == -1) {
857 $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
860 $user_tz = new DateTimeZone($user_tz_string);
861 } catch (Exception
$e) {
865 $tz_offset = $user_tz->getOffset($dt);
868 $user_timestamp = $dt->format('U') +
$tz_offset;
871 return smart_date_time($user_timestamp,
872 $tz_offset, $owner_uid);
875 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
877 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
879 return date($format, $user_timestamp);
883 function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false) {
884 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
886 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
887 return date("G:i", $timestamp);
888 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
889 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
890 return date($format, $timestamp);
892 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
893 return date($format, $timestamp);
897 function sql_bool_to_bool($s) {
898 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
905 function bool_to_sql_bool($s) {
913 // Session caching removed due to causing wrong redirects to upgrade
914 // script when get_schema_version() is called on an obsolete session
915 // created on a previous schema version.
916 function get_schema_version($nocache = false) {
917 global $schema_version;
919 if (!$schema_version) {
920 $result = db_query("SELECT schema_version FROM ttrss_version");
921 $version = db_fetch_result($result, 0, "schema_version");
922 $schema_version = $version;
925 return $schema_version;
929 function sanity_check() {
930 require_once 'errors.php';
933 $schema_version = get_schema_version(true);
935 if ($schema_version != SCHEMA_VERSION
) {
939 if (DB_TYPE
== "mysql") {
940 $result = db_query("SELECT true", false);
941 if (db_num_rows($result) != 1) {
946 if (db_escape_string("testTEST") != "testTEST") {
950 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
953 function file_is_locked($filename) {
954 if (function_exists('flock')) {
955 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
957 if (flock($fp, LOCK_EX | LOCK_NB
)) {
968 return true; // consider the file always locked and skip the test
971 function make_lockfile($filename) {
972 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
974 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
975 if (function_exists('posix_getpid')) {
976 fwrite($fp, posix_getpid() . "\n");
984 function make_stampfile($filename) {
985 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
987 if (flock($fp, LOCK_EX | LOCK_NB
)) {
988 fwrite($fp, time() . "\n");
997 function sql_random_function() {
998 if (DB_TYPE
== "mysql") {
1005 function catchup_feed($feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
1007 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
1009 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
1011 // Todo: all this interval stuff needs some generic generator function
1013 $date_qpart = "false";
1017 if (DB_TYPE
== "pgsql") {
1018 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
1020 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
1024 if (DB_TYPE
== "pgsql") {
1025 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
1027 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
1031 if (DB_TYPE
== "pgsql") {
1032 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
1034 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
1038 $date_qpart = "true";
1041 if (is_numeric($feed)) {
1047 $children = getChildCategories($feed, $owner_uid);
1048 array_push($children, $feed);
1050 $children = join(",", $children);
1052 $cat_qpart = "cat_id IN ($children)";
1054 $cat_qpart = "cat_id IS NULL";
1057 db_query("UPDATE ttrss_user_entries
1058 SET unread = false, last_read = NOW() WHERE ref_id IN
1060 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1061 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1062 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1064 } else if ($feed == -2) {
1066 db_query("UPDATE ttrss_user_entries
1067 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1068 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1069 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1072 } else if ($feed > 0) {
1074 db_query("UPDATE ttrss_user_entries
1075 SET unread = false, last_read = NOW() WHERE ref_id IN
1077 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1078 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1080 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1083 db_query("UPDATE ttrss_user_entries
1084 SET unread = false, last_read = NOW() WHERE ref_id IN
1086 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1087 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1091 db_query("UPDATE ttrss_user_entries
1092 SET unread = false, last_read = NOW() WHERE ref_id IN
1094 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1095 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1100 $intl = get_pref("FRESH_ARTICLE_MAX_AGE");
1102 if (DB_TYPE
== "pgsql") {
1103 $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
1105 $match_part = "date_entered > DATE_SUB(NOW(),
1106 INTERVAL $intl HOUR) ";
1109 db_query("UPDATE ttrss_user_entries
1110 SET unread = false, last_read = NOW() WHERE ref_id IN
1112 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1113 AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
1117 db_query("UPDATE ttrss_user_entries
1118 SET unread = false, last_read = NOW() WHERE ref_id IN
1120 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1121 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1124 } else if ($feed < LABEL_BASE_INDEX
) { // label
1126 $label_id = feed_to_label_id($feed);
1128 db_query("UPDATE ttrss_user_entries
1129 SET unread = false, last_read = NOW() WHERE ref_id IN
1131 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1132 AND label_id = '$label_id' AND ref_id = article_id
1133 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1137 ccache_update($feed, $owner_uid, $cat_view);
1140 db_query("UPDATE ttrss_user_entries
1141 SET unread = false, last_read = NOW() WHERE ref_id IN
1143 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1144 AND post_int_id = int_id AND tag_name = '$feed'
1145 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1150 function getAllCounters() {
1151 $data = getGlobalCounters();
1153 $data = array_merge($data, getVirtCounters());
1154 $data = array_merge($data, getLabelCounters());
1155 $data = array_merge($data, getFeedCounters($active_feed));
1156 $data = array_merge($data, getCategoryCounters());
1161 function getCategoryTitle($cat_id) {
1163 if ($cat_id == -1) {
1164 return __("Special");
1165 } else if ($cat_id == -2) {
1166 return __("Labels");
1169 $result = db_query("SELECT title FROM ttrss_feed_categories WHERE
1172 if (db_num_rows($result) == 1) {
1173 return db_fetch_result($result, 0, "title");
1175 return __("Uncategorized");
1181 function getCategoryCounters() {
1184 /* Labels category */
1186 $cv = array("id" => -2, "kind" => "cat",
1187 "counter" => getCategoryUnread(-2));
1189 array_push($ret_arr, $cv);
1191 $result = db_query("SELECT id AS cat_id, value AS unread,
1192 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1193 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1194 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1195 WHERE ttrss_cat_counters_cache.feed_id = id AND
1196 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1197 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1199 while ($line = db_fetch_assoc($result)) {
1200 $line["cat_id"] = (int) $line["cat_id"];
1202 if ($line["num_children"] > 0) {
1203 $child_counter = getCategoryChildrenUnread($line["cat_id"], $_SESSION["uid"]);
1208 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1209 "counter" => $line["unread"] +
$child_counter);
1211 array_push($ret_arr, $cv);
1214 /* Special case: NULL category doesn't actually exist in the DB */
1216 $cv = array("id" => 0, "kind" => "cat",
1217 "counter" => (int) ccache_find(0, $_SESSION["uid"], true));
1219 array_push($ret_arr, $cv);
1224 // only accepts real cats (>= 0)
1225 function getCategoryChildrenUnread($cat, $owner_uid = false) {
1226 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1228 $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1229 AND owner_uid = $owner_uid");
1233 while ($line = db_fetch_assoc($result)) {
1234 $unread +
= getCategoryUnread($line["id"], $owner_uid);
1235 $unread +
= getCategoryChildrenUnread($line["id"], $owner_uid);
1241 function getCategoryUnread($cat, $owner_uid = false) {
1243 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1248 $cat_query = "cat_id = '$cat'";
1250 $cat_query = "cat_id IS NULL";
1253 $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query
1254 AND owner_uid = " . $owner_uid);
1256 $cat_feeds = array();
1257 while ($line = db_fetch_assoc($result)) {
1258 array_push($cat_feeds, "feed_id = " . $line["id"]);
1261 if (count($cat_feeds) == 0) return 0;
1263 $match_part = implode(" OR ", $cat_feeds);
1265 $result = db_query("SELECT COUNT(int_id) AS unread
1266 FROM ttrss_user_entries
1267 WHERE unread = true AND ($match_part)
1268 AND owner_uid = " . $owner_uid);
1272 # this needs to be rewritten
1273 while ($line = db_fetch_assoc($result)) {
1274 $unread +
= $line["unread"];
1278 } else if ($cat == -1) {
1279 return getFeedUnread(-1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1280 } else if ($cat == -2) {
1282 $result = db_query("
1283 SELECT COUNT(unread) AS unread FROM
1284 ttrss_user_entries, ttrss_user_labels2
1285 WHERE article_id = ref_id AND unread = true
1286 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1288 $unread = db_fetch_result($result, 0, "unread");
1295 function getFeedUnread($feed, $is_cat = false) {
1296 return getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
1299 function getLabelUnread($label_id, $owner_uid = false) {
1300 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1302 $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1303 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1305 if (db_num_rows($result) != 0) {
1306 return db_fetch_result($result, 0, "unread");
1312 function getFeedArticles($feed, $is_cat = false, $unread_only = false,
1313 $owner_uid = false) {
1315 $n_feed = (int) $feed;
1316 $need_entries = false;
1318 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1321 $unread_qpart = "unread = true";
1323 $unread_qpart = "true";
1327 return getCategoryUnread($n_feed, $owner_uid);
1328 } else if ($n_feed == -6) {
1330 } else if ($feed != "0" && $n_feed == 0) {
1332 $feed = db_escape_string($feed);
1334 $result = db_query("SELECT SUM((SELECT COUNT(int_id)
1335 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1336 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1337 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1338 return db_fetch_result($result, 0, "count");
1340 } else if ($n_feed == -1) {
1341 $match_part = "marked = true";
1342 } else if ($n_feed == -2) {
1343 $match_part = "published = true";
1344 } else if ($n_feed == -3) {
1345 $match_part = "unread = true AND score >= 0";
1347 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1349 if (DB_TYPE
== "pgsql") {
1350 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1352 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1355 $need_entries = true;
1357 } else if ($n_feed == -4) {
1358 $match_part = "true";
1359 } else if ($n_feed >= 0) {
1362 $match_part = "feed_id = '$n_feed'";
1364 $match_part = "feed_id IS NULL";
1367 } else if ($feed < LABEL_BASE_INDEX
) {
1369 $label_id = feed_to_label_id($feed);
1371 return getLabelUnread($label_id, $owner_uid);
1377 if ($need_entries) {
1378 $from_qpart = "ttrss_user_entries,ttrss_entries";
1379 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1381 $from_qpart = "ttrss_user_entries";
1384 $query = "SELECT count(int_id) AS unread
1385 FROM $from_qpart WHERE
1386 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1388 //echo "[$feed/$query]\n";
1390 $result = db_query($query);
1394 $result = db_query("SELECT COUNT(post_int_id) AS unread
1395 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1396 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1397 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1400 $unread = db_fetch_result($result, 0, "unread");
1405 function getGlobalUnread($user_id = false) {
1408 $user_id = $_SESSION["uid"];
1411 $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1412 WHERE owner_uid = '$user_id' AND feed_id > 0");
1414 $c_id = db_fetch_result($result, 0, "c_id");
1419 function getGlobalCounters($global_unread = -1) {
1422 if ($global_unread == -1) {
1423 $global_unread = getGlobalUnread();
1426 $cv = array("id" => "global-unread",
1427 "counter" => (int) $global_unread);
1429 array_push($ret_arr, $cv);
1431 $result = db_query("SELECT COUNT(id) AS fn FROM
1432 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1434 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1436 $cv = array("id" => "subscribed-feeds",
1437 "counter" => (int) $subscribed_feeds);
1439 array_push($ret_arr, $cv);
1444 function getVirtCounters() {
1448 for ($i = 0; $i >= -4; $i--) {
1450 $count = getFeedUnread($i);
1452 $cv = array("id" => $i,
1453 "counter" => (int) $count);
1455 // if (get_pref('EXTENDED_FEEDLIST'))
1456 // $cv["xmsg"] = getFeedArticles($i)." ".__("total");
1458 array_push($ret_arr, $cv);
1461 $feeds = PluginHost
::getInstance()->get_feeds(-1);
1463 if (is_array($feeds)) {
1464 foreach ($feeds as $feed) {
1465 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1466 "counter" => $feed['sender']->get_unread($feed['id']));
1467 array_push($ret_arr, $cv);
1474 function getLabelCounters($descriptions = false) {
1478 $owner_uid = $_SESSION["uid"];
1480 $result = db_query("SELECT id,caption,COUNT(unread) AS unread
1481 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1482 (ttrss_labels2.id = label_id)
1483 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true
1484 AND ttrss_user_entries.owner_uid = $owner_uid)
1485 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1486 ttrss_labels2.caption");
1488 while ($line = db_fetch_assoc($result)) {
1490 $id = label_to_feed_id($line["id"]);
1492 $label_name = $line["caption"];
1493 $count = $line["unread"];
1495 $cv = array("id" => $id,
1496 "counter" => (int) $count);
1499 $cv["description"] = $label_name;
1501 // if (get_pref('EXTENDED_FEEDLIST'))
1502 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1504 array_push($ret_arr, $cv);
1510 function getFeedCounters($active_feed = false) {
1514 $query = "SELECT ttrss_feeds.id,
1516 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1517 last_error, value AS count
1518 FROM ttrss_feeds, ttrss_counters_cache
1519 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1520 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1521 AND ttrss_counters_cache.feed_id = id";
1523 $result = db_query($query);
1524 $fctrs_modified = false;
1526 while ($line = db_fetch_assoc($result)) {
1529 $count = $line["count"];
1530 $last_error = htmlspecialchars($line["last_error"]);
1532 $last_updated = make_local_datetime($line['last_updated'], false);
1534 $has_img = feed_has_icon($id);
1536 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1539 $cv = array("id" => $id,
1540 "updated" => $last_updated,
1541 "counter" => (int) $count,
1542 "has_img" => (int) $has_img);
1545 $cv["error"] = $last_error;
1547 // if (get_pref('EXTENDED_FEEDLIST'))
1548 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1550 if ($active_feed && $id == $active_feed)
1551 $cv["title"] = truncate_string($line["title"], 30);
1553 array_push($ret_arr, $cv);
1560 function get_pgsql_version() {
1561 $result = db_query("SELECT version() AS version");
1562 $version = explode(" ", db_fetch_result($result, 0, "version"));
1567 * @return array (code => Status code, message => error message if available)
1569 * 0 - OK, Feed already exists
1570 * 1 - OK, Feed added
1572 * 3 - URL content is HTML, no feeds available
1573 * 4 - URL content is HTML which contains multiple feeds.
1574 * Here you should call extractfeedurls in rpc-backend
1575 * to get all possible feeds.
1576 * 5 - Couldn't download the URL content.
1577 * 6 - Content is an invalid XML.
1579 function subscribe_to_feed($url, $cat_id = 0,
1580 $auth_login = '', $auth_pass = '') {
1582 global $fetch_last_error;
1584 require_once "include/rssfuncs.php";
1586 $url = fix_url($url);
1588 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1590 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1593 return array("code" => 5, "message" => $fetch_last_error);
1596 if (is_html($contents)) {
1597 $feedUrls = get_feeds_from_html($url, $contents);
1599 if (count($feedUrls) == 0) {
1600 return array("code" => 3);
1601 } else if (count($feedUrls) > 1) {
1602 return array("code" => 4, "feeds" => $feedUrls);
1604 //use feed url as new URL
1605 $url = key($feedUrls);
1608 libxml_use_internal_errors(true);
1609 $doc = new DOMDocument();
1610 $doc->loadXML(html_entity_decode($contents));
1611 $error = libxml_get_last_error();
1612 libxml_clear_errors();
1615 $error_message = format_libxml_error($error);
1617 return array("code" => 6, "message" => $error_message);
1620 if ($cat_id == "0" ||
!$cat_id) {
1621 $cat_qpart = "NULL";
1623 $cat_qpart = "'$cat_id'";
1627 "SELECT id FROM ttrss_feeds
1628 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1630 if (strlen(FEED_CRYPT_KEY
) > 0) {
1631 require_once "crypt.php";
1632 $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
1633 $auth_pass_encrypted = 'true';
1635 $auth_pass_encrypted = 'false';
1638 $auth_pass = db_escape_string($auth_pass);
1640 if (db_num_rows($result) == 0) {
1642 "INSERT INTO ttrss_feeds
1643 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
1644 VALUES ('".$_SESSION["uid"]."', '$url',
1645 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, $auth_pass_encrypted)");
1648 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1649 AND owner_uid = " . $_SESSION["uid"]);
1651 $feed_id = db_fetch_result($result, 0, "id");
1654 update_rss_feed($feed_id, true);
1657 return array("code" => 1);
1659 return array("code" => 0);
1663 function print_feed_select($id, $default_id = "",
1664 $attributes = "", $include_all_feeds = true,
1665 $root_id = false, $nest_level = 0) {
1668 print "<select id=\"$id\" name=\"$id\" $attributes>";
1669 if ($include_all_feeds) {
1670 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1671 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1675 if (get_pref('ENABLE_FEED_CATS')) {
1678 $parent_qpart = "parent_cat = '$root_id'";
1680 $parent_qpart = "parent_cat IS NULL";
1682 $result = db_query("SELECT id,title,
1683 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1684 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1685 FROM ttrss_feed_categories
1686 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1688 while ($line = db_fetch_assoc($result)) {
1690 for ($i = 0; $i < $nest_level; $i++
)
1691 $line["title"] = " - " . $line["title"];
1693 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1695 printf("<option $is_selected value='CAT:%d'>%s</option>",
1696 $line["id"], htmlspecialchars($line["title"]));
1698 if ($line["num_children"] > 0)
1699 print_feed_select($id, $default_id, $attributes,
1700 $include_all_feeds, $line["id"], $nest_level+
1);
1702 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1703 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1705 while ($fline = db_fetch_assoc($feed_result)) {
1706 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1708 $fline["title"] = " + " . $fline["title"];
1710 for ($i = 0; $i < $nest_level; $i++
)
1711 $fline["title"] = " - " . $fline["title"];
1713 printf("<option $is_selected value='%d'>%s</option>",
1714 $fline["id"], htmlspecialchars($fline["title"]));
1719 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1721 printf("<option $is_selected value='CAT:0'>%s</option>",
1722 __("Uncategorized"));
1724 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1725 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1727 while ($fline = db_fetch_assoc($feed_result)) {
1728 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1730 $fline["title"] = " + " . $fline["title"];
1732 for ($i = 0; $i < $nest_level; $i++
)
1733 $fline["title"] = " - " . $fline["title"];
1735 printf("<option $is_selected value='%d'>%s</option>",
1736 $fline["id"], htmlspecialchars($fline["title"]));
1741 $result = db_query("SELECT id,title FROM ttrss_feeds
1742 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1744 while ($line = db_fetch_assoc($result)) {
1746 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1748 printf("<option $is_selected value='%d'>%s</option>",
1749 $line["id"], htmlspecialchars($line["title"]));
1758 function print_feed_cat_select($id, $default_id,
1759 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1762 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1766 $parent_qpart = "parent_cat = '$root_id'";
1768 $parent_qpart = "parent_cat IS NULL";
1770 $result = db_query("SELECT id,title,
1771 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1772 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1773 FROM ttrss_feed_categories
1774 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1776 while ($line = db_fetch_assoc($result)) {
1777 if ($line["id"] == $default_id) {
1778 $is_selected = "selected=\"1\"";
1783 for ($i = 0; $i < $nest_level; $i++
)
1784 $line["title"] = " - " . $line["title"];
1787 printf("<option $is_selected value='%d'>%s</option>",
1788 $line["id"], htmlspecialchars($line["title"]));
1790 if ($line["num_children"] > 0)
1791 print_feed_cat_select($id, $default_id, $attributes,
1792 $include_all_cats, $line["id"], $nest_level+
1);
1796 if ($include_all_cats) {
1797 if (db_num_rows($result) > 0) {
1798 print "<option disabled=\"1\">--------</option>";
1801 if ($default_id == 0) {
1802 $is_selected = "selected=\"1\"";
1807 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1813 function checkbox_to_sql_bool($val) {
1814 return ($val == "on") ?
"true" : "false";
1817 function getFeedCatTitle($id) {
1819 return __("Special");
1820 } else if ($id < LABEL_BASE_INDEX
) {
1821 return __("Labels");
1822 } else if ($id > 0) {
1823 $result = db_query("SELECT ttrss_feed_categories.title
1824 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1825 cat_id = ttrss_feed_categories.id");
1826 if (db_num_rows($result) == 1) {
1827 return db_fetch_result($result, 0, "title");
1829 return __("Uncategorized");
1832 return "getFeedCatTitle($id) failed";
1837 function getFeedIcon($id) {
1840 return "images/archive.png";
1843 return "images/mark_set.svg";
1846 return "images/pub_set.svg";
1849 return "images/fresh.png";
1852 return "images/tag.png";
1855 return "images/recently_read.png";
1858 if ($id < LABEL_BASE_INDEX
) {
1859 return "images/label.png";
1861 if (file_exists(ICONS_DIR
. "/$id.ico"))
1862 return ICONS_URL
. "/$id.ico";
1870 function getFeedTitle($id, $cat = false) {
1872 return getCategoryTitle($id);
1873 } else if ($id == -1) {
1874 return __("Starred articles");
1875 } else if ($id == -2) {
1876 return __("Published articles");
1877 } else if ($id == -3) {
1878 return __("Fresh articles");
1879 } else if ($id == -4) {
1880 return __("All articles");
1881 } else if ($id === 0 ||
$id === "0") {
1882 return __("Archived articles");
1883 } else if ($id == -6) {
1884 return __("Recently read");
1885 } else if ($id < LABEL_BASE_INDEX
) {
1886 $label_id = feed_to_label_id($id);
1887 $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1888 if (db_num_rows($result) == 1) {
1889 return db_fetch_result($result, 0, "caption");
1891 return "Unknown label ($label_id)";
1894 } else if (is_numeric($id) && $id > 0) {
1895 $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'");
1896 if (db_num_rows($result) == 1) {
1897 return db_fetch_result($result, 0, "title");
1899 return "Unknown feed ($id)";
1906 function make_init_params() {
1909 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1910 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1911 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
1912 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1914 $params[strtolower($param)] = (int) get_pref($param);
1917 $params["icons_url"] = ICONS_URL
;
1918 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1919 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
1920 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
1921 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
1922 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1923 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1925 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1926 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1928 $max_feed_id = db_fetch_result($result, 0, "mid");
1929 $num_feeds = db_fetch_result($result, 0, "nf");
1931 $params["max_feed_id"] = (int) $max_feed_id;
1932 $params["num_feeds"] = (int) $num_feeds;
1934 $params["collapsed_feedlist"] = (int) get_pref("_COLLAPSED_FEEDLIST");
1935 $params["hotkeys"] = get_hotkeys_map();
1937 $params["csrf_token"] = $_SESSION["csrf_token"];
1938 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1940 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1945 function get_hotkeys_info() {
1947 __("Navigation") => array(
1948 "next_feed" => __("Open next feed"),
1949 "prev_feed" => __("Open previous feed"),
1950 "next_article" => __("Open next article"),
1951 "prev_article" => __("Open previous article"),
1952 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1953 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1954 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
1955 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
1956 "search_dialog" => __("Show search dialog")),
1957 __("Article") => array(
1958 "toggle_mark" => __("Toggle starred"),
1959 "toggle_publ" => __("Toggle published"),
1960 "toggle_unread" => __("Toggle unread"),
1961 "edit_tags" => __("Edit tags"),
1962 "dismiss_selected" => __("Dismiss selected"),
1963 "dismiss_read" => __("Dismiss read"),
1964 "open_in_new_window" => __("Open in new window"),
1965 "catchup_below" => __("Mark below as read"),
1966 "catchup_above" => __("Mark above as read"),
1967 "article_scroll_down" => __("Scroll down"),
1968 "article_scroll_up" => __("Scroll up"),
1969 "select_article_cursor" => __("Select article under cursor"),
1970 "email_article" => __("Email article"),
1971 "close_article" => __("Close/collapse article"),
1972 "toggle_expand" => __("Toggle article expansion (combined mode)"),
1973 "toggle_widescreen" => __("Toggle widescreen mode"),
1974 "toggle_embed_original" => __("Toggle embed original")),
1975 __("Article selection") => array(
1976 "select_all" => __("Select all articles"),
1977 "select_unread" => __("Select unread"),
1978 "select_marked" => __("Select starred"),
1979 "select_published" => __("Select published"),
1980 "select_invert" => __("Invert selection"),
1981 "select_none" => __("Deselect everything")),
1982 __("Feed") => array(
1983 "feed_refresh" => __("Refresh current feed"),
1984 "feed_unhide_read" => __("Un/hide read feeds"),
1985 "feed_subscribe" => __("Subscribe to feed"),
1986 "feed_edit" => __("Edit feed"),
1987 "feed_catchup" => __("Mark as read"),
1988 "feed_reverse" => __("Reverse headlines"),
1989 "feed_debug_update" => __("Debug feed update"),
1990 "catchup_all" => __("Mark all feeds as read"),
1991 "cat_toggle_collapse" => __("Un/collapse current category"),
1992 "toggle_combined_mode" => __("Toggle combined mode"),
1993 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1994 __("Go to") => array(
1995 "goto_all" => __("All articles"),
1996 "goto_fresh" => __("Fresh"),
1997 "goto_marked" => __("Starred"),
1998 "goto_published" => __("Published"),
1999 "goto_tagcloud" => __("Tag cloud"),
2000 "goto_prefs" => __("Preferences")),
2001 __("Other") => array(
2002 "create_label" => __("Create label"),
2003 "create_filter" => __("Create filter"),
2004 "collapse_sidebar" => __("Un/collapse sidebar"),
2005 "help_dialog" => __("Show help dialog"))
2008 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_INFO
) as $plugin) {
2009 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
2015 function get_hotkeys_map() {
2017 // "navigation" => array(
2020 "n" => "next_article",
2021 "p" => "prev_article",
2022 "(38)|up" => "prev_article",
2023 "(40)|down" => "next_article",
2024 // "^(38)|Ctrl-up" => "prev_article_noscroll",
2025 // "^(40)|Ctrl-down" => "next_article_noscroll",
2026 "(191)|/" => "search_dialog",
2027 // "article" => array(
2028 "s" => "toggle_mark",
2029 "*s" => "toggle_publ",
2030 "u" => "toggle_unread",
2031 "*t" => "edit_tags",
2032 "*d" => "dismiss_selected",
2033 "*x" => "dismiss_read",
2034 "o" => "open_in_new_window",
2035 "c p" => "catchup_below",
2036 "c n" => "catchup_above",
2037 "*n" => "article_scroll_down",
2038 "*p" => "article_scroll_up",
2039 "*(38)|Shift+up" => "article_scroll_up",
2040 "*(40)|Shift+down" => "article_scroll_down",
2041 "a *w" => "toggle_widescreen",
2042 "a e" => "toggle_embed_original",
2043 "e" => "email_article",
2044 "a q" => "close_article",
2045 // "article_selection" => array(
2046 "a a" => "select_all",
2047 "a u" => "select_unread",
2048 "a *u" => "select_marked",
2049 "a p" => "select_published",
2050 "a i" => "select_invert",
2051 "a n" => "select_none",
2053 "f r" => "feed_refresh",
2054 "f a" => "feed_unhide_read",
2055 "f s" => "feed_subscribe",
2056 "f e" => "feed_edit",
2057 "f q" => "feed_catchup",
2058 "f x" => "feed_reverse",
2059 "f *d" => "feed_debug_update",
2060 "f *c" => "toggle_combined_mode",
2061 "f c" => "toggle_cdm_expanded",
2062 "*q" => "catchup_all",
2063 "x" => "cat_toggle_collapse",
2065 "g a" => "goto_all",
2066 "g f" => "goto_fresh",
2067 "g s" => "goto_marked",
2068 "g p" => "goto_published",
2069 "g t" => "goto_tagcloud",
2070 "g *p" => "goto_prefs",
2071 // "other" => array(
2072 "(9)|Tab" => "select_article_cursor", // tab
2073 "c l" => "create_label",
2074 "c f" => "create_filter",
2075 "c s" => "collapse_sidebar",
2076 "^(191)|Ctrl+/" => "help_dialog",
2079 if (get_pref('COMBINED_DISPLAY_MODE')) {
2080 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2081 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2084 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_MAP
) as $plugin) {
2085 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2088 $prefixes = array();
2090 foreach (array_keys($hotkeys) as $hotkey) {
2091 $pair = explode(" ", $hotkey, 2);
2093 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2094 array_push($prefixes, $pair[0]);
2098 return array($prefixes, $hotkeys);
2101 function make_runtime_info() {
2104 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2105 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2107 $max_feed_id = db_fetch_result($result, 0, "mid");
2108 $num_feeds = db_fetch_result($result, 0, "nf");
2110 $data["max_feed_id"] = (int) $max_feed_id;
2111 $data["num_feeds"] = (int) $num_feeds;
2113 $data['last_article_id'] = getLastArticleId();
2114 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
2116 $data['dep_ts'] = calculate_dep_timestamp();
2117 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2119 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2121 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2123 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2125 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2128 $stamp_delta = time() - $stamp;
2130 if ($stamp_delta > 1800) {
2134 $_SESSION["daemon_stamp_check"] = time();
2137 $data['daemon_stamp_ok'] = $stamp_check;
2139 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2141 $data['daemon_stamp'] = $stamp_fmt;
2146 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2147 $new_version_details = @check_for_update
();
2149 $data['new_version_available'] = (int) ($new_version_details != false);
2151 $_SESSION["last_version_check"] = time();
2152 $_SESSION["version_data"] = $new_version_details;
2158 function search_to_sql($search) {
2160 $search_query_part = "";
2162 $keywords = explode(" ", $search);
2163 $query_keywords = array();
2165 foreach ($keywords as $k) {
2166 if (strpos($k, "-") === 0) {
2173 $commandpair = explode(":", mb_strtolower($k), 2);
2175 switch ($commandpair[0]) {
2177 if ($commandpair[1]) {
2178 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
2179 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2181 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2182 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2186 if ($commandpair[1]) {
2187 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
2188 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2190 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2191 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2195 if ($commandpair[1]) {
2196 if ($commandpair[1] == "true")
2197 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2198 else if ($commandpair[1] == "false")
2199 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2201 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
2202 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2204 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2205 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2210 if ($commandpair[1]) {
2211 if ($commandpair[1] == "true")
2212 array_push($query_keywords, "($not (marked = true))");
2214 array_push($query_keywords, "($not (marked = false))");
2216 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2217 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2221 if ($commandpair[1]) {
2222 if ($commandpair[1] == "true")
2223 array_push($query_keywords, "($not (published = true))");
2225 array_push($query_keywords, "($not (published = false))");
2228 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2229 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2233 if (strpos($k, "@") === 0) {
2235 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
2236 $orig_ts = strtotime(substr($k, 1));
2237 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2239 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2241 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2243 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2244 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2249 $search_query_part = implode("AND", $query_keywords);
2251 return $search_query_part;
2254 function getParentCategories($cat, $owner_uid) {
2257 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
2258 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2260 while ($line = db_fetch_assoc($result)) {
2261 array_push($rv, $line["parent_cat"]);
2262 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
2268 function getChildCategories($cat, $owner_uid) {
2271 $result = db_query("SELECT id FROM ttrss_feed_categories
2272 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2274 while ($line = db_fetch_assoc($result)) {
2275 array_push($rv, $line["id"]);
2276 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
2282 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) {
2284 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2286 $ext_tables_part = "";
2290 if (SPHINX_ENABLED
) {
2291 $ids = join(",", @sphinx_search
($search, 0, 500));
2294 $search_query_part = "ref_id IN ($ids) AND ";
2296 $search_query_part = "ref_id = -1 AND ";
2299 $search_query_part = search_to_sql($search);
2300 $search_query_part .= " AND ";
2304 $search_query_part = "";
2309 if (DB_TYPE
== "pgsql") {
2310 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2312 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2315 $override_order = "updated DESC";
2317 $filter_query_part = filter_to_sql($filter, $owner_uid);
2319 // Try to check if SQL regexp implementation chokes on a valid regexp
2320 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
2321 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2322 WHERE $filter_query_part LIMIT 1", false);
2325 $test = db_fetch_result($result, 0, "true_val");
2328 $filter_query_part = "false AND";
2330 $filter_query_part .= " AND";
2333 $filter_query_part = "false AND";
2337 $filter_query_part = "";
2341 $since_id_part = "ttrss_entries.id > $since_id AND ";
2343 $since_id_part = "";
2346 $view_query_part = "";
2348 if ($view_mode == "adaptive") {
2350 $view_query_part = " ";
2351 } else if ($feed != -1) {
2353 $unread = getFeedUnread($feed, $cat_view);
2355 if ($cat_view && $feed > 0 && $include_children)
2356 $unread +
= getCategoryChildrenUnread($feed);
2359 $view_query_part = " unread = true AND ";
2364 if ($view_mode == "marked") {
2365 $view_query_part = " marked = true AND ";
2368 if ($view_mode == "has_note") {
2369 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2372 if ($view_mode == "published") {
2373 $view_query_part = " published = true AND ";
2376 if ($view_mode == "unread" && $feed != -6) {
2377 $view_query_part = " unread = true AND ";
2381 $limit_query_part = "LIMIT " . $limit;
2384 $allow_archived = false;
2386 $vfeed_query_part = "";
2388 // override query strategy and enable feed display when searching globally
2389 if ($search && $search_mode == "all_feeds") {
2390 $query_strategy_part = "true";
2391 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2393 } else if (!is_numeric($feed)) {
2394 $query_strategy_part = "true";
2395 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2396 id = feed_id) as feed_title,";
2397 } else if ($search && $search_mode == "this_cat") {
2398 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2401 if ($include_children) {
2402 $subcats = getChildCategories($feed, $owner_uid);
2403 array_push($subcats, $feed);
2404 $cats_qpart = join(",", $subcats);
2406 $cats_qpart = $feed;
2409 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2412 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2415 } else if ($feed > 0) {
2420 if ($include_children) {
2422 $subcats = getChildCategories($feed, $owner_uid);
2424 array_push($subcats, $feed);
2425 $query_strategy_part = "cat_id IN (".
2426 implode(",", $subcats).")";
2429 $query_strategy_part = "cat_id = '$feed'";
2433 $query_strategy_part = "cat_id IS NULL";
2436 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2439 $query_strategy_part = "feed_id = '$feed'";
2441 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2442 $query_strategy_part = "feed_id IS NULL";
2443 $allow_archived = true;
2444 } else if ($feed == 0 && $cat_view) { // uncategorized
2445 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2446 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2447 } else if ($feed == -1) { // starred virtual feed
2448 $query_strategy_part = "marked = true";
2449 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2450 $allow_archived = true;
2452 if (!$override_order) {
2453 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2456 } else if ($feed == -2) { // published virtual feed OR labels category
2459 $query_strategy_part = "published = true";
2460 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2461 $allow_archived = true;
2463 if (!$override_order) {
2464 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2468 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2470 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2472 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2473 ttrss_user_labels2.article_id = ref_id";
2476 } else if ($feed == -6) { // recently read
2477 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2478 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2479 $allow_archived = true;
2481 if (!$override_order) $override_order = "last_read DESC";
2482 } else if ($feed == -3) { // fresh virtual feed
2483 $query_strategy_part = "unread = true AND score >= 0";
2485 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
2487 if (DB_TYPE
== "pgsql") {
2488 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2490 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2493 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2494 } else if ($feed == -4) { // all articles virtual feed
2495 $allow_archived = true;
2496 $query_strategy_part = "true";
2497 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2498 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2499 $label_id = feed_to_label_id($feed);
2501 $query_strategy_part = "label_id = '$label_id' AND
2502 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2503 ttrss_user_labels2.article_id = ref_id";
2505 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2506 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2507 $allow_archived = true;
2510 $query_strategy_part = "true";
2513 $order_by = "score DESC, date_entered DESC, updated DESC";
2515 if ($view_mode == "unread_first") {
2516 $order_by = "unread DESC, $order_by";
2519 if ($override_order) {
2520 $order_by = $override_order;
2526 $feed_title = T_sprintf("Search results: %s", $search);
2529 $feed_title = getCategoryTitle($feed);
2531 if (is_numeric($feed) && $feed > 0) {
2532 $result = db_query("SELECT title,site_url,last_error
2533 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2535 $feed_title = db_fetch_result($result, 0, "title");
2536 $feed_site_url = db_fetch_result($result, 0, "site_url");
2537 $last_error = db_fetch_result($result, 0, "last_error");
2539 $feed_title = getFeedTitle($feed);
2544 $content_query_part = "content as content_preview, cached_content, ";
2546 if (is_numeric($feed)) {
2549 $feed_kind = "Feeds";
2551 $feed_kind = "Labels";
2554 if ($limit_query_part) {
2555 $offset_query_part = "OFFSET $offset";
2558 // proper override_order applied above
2559 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
2560 if (!$override_order) {
2561 $order_by = "ttrss_feeds.title, $order_by";
2563 $order_by = "ttrss_feeds.title, $override_order";
2567 if (!$allow_archived) {
2568 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2569 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2572 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2573 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2576 if ($vfeed_query_part)
2577 $vfeed_query_part .= "favicon_avg_color,";
2579 $query = "SELECT DISTINCT
2582 ttrss_entries.id,ttrss_entries.title,
2586 always_display_enclosures,
2593 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2594 last_marked, last_published,
2602 ttrss_user_entries.ref_id = ttrss_entries.id AND
2603 ttrss_user_entries.owner_uid = '$owner_uid' AND
2608 $query_strategy_part ORDER BY $order_by
2609 $limit_query_part $offset_query_part";
2611 if ($_REQUEST["debug"]) print $query;
2613 $result = db_query($query);
2618 $select_qpart = "SELECT DISTINCT " .
2622 "ttrss_entries.id as id," .
2635 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2636 "last_marked, last_published, " .
2639 $content_query_part .
2642 $feed_kind = "Tags";
2643 $all_tags = explode(",", $feed);
2644 if ($search_mode == 'any') {
2645 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2646 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2647 $where_qpart = " WHERE " .
2648 "ref_id = ttrss_entries.id AND " .
2649 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2650 "post_int_id = int_id AND $tag_sql AND " .
2652 $search_query_part .
2653 $query_strategy_part . " ORDER BY $order_by " .
2658 $sub_selects = array();
2659 $sub_ands = array();
2660 foreach ($all_tags as $term) {
2661 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");
2668 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2673 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2674 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2675 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2676 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2678 // error_log("TAG SQL: " . $tag_sql);
2679 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2681 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2682 $result = db_query($select_qpart . $from_qpart . $where_qpart);
2685 return array($result, $feed_title, $feed_site_url, $last_error);
2689 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false) {
2690 if (!$owner) $owner = $_SESSION["uid"];
2692 $res = trim($str); if (!$res) return '';
2694 if (strpos($res, "href=") === false)
2695 $res = rewrite_urls($res);
2697 $charset_hack = '<head>
2698 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2701 $res = trim($res); if (!$res) return '';
2703 libxml_use_internal_errors(true);
2705 $doc = new DOMDocument();
2706 $doc->loadHTML($charset_hack . $res);
2707 $xpath = new DOMXPath($doc);
2709 $entries = $xpath->query('(//a[@href]|//img[@src])');
2711 foreach ($entries as $entry) {
2715 if ($entry->hasAttribute('href'))
2716 $entry->setAttribute('href',
2717 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2719 if ($entry->hasAttribute('src')) {
2720 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2722 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2724 if (file_exists($cached_filename)) {
2725 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2728 $entry->setAttribute('src', $src);
2731 if ($entry->nodeName
== 'img') {
2732 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
2733 $force_remove_images ||
$_SESSION["bw_limit"]) {
2735 $p = $doc->createElement('p');
2737 $a = $doc->createElement('a');
2738 $a->setAttribute('href', $entry->getAttribute('src'));
2740 $a->appendChild(new DOMText($entry->getAttribute('src')));
2741 $a->setAttribute('target', '_blank');
2743 $p->appendChild($a);
2745 $entry->parentNode
->replaceChild($p, $entry);
2750 if (strtolower($entry->nodeName
) == "a") {
2751 $entry->setAttribute("target", "_blank");
2755 $entries = $xpath->query('//iframe');
2756 foreach ($entries as $entry) {
2757 $entry->setAttribute('sandbox', 'allow-scripts');
2761 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
2762 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
2763 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
2764 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
2765 'dt', 'em', 'footer', 'figure', 'figcaption',
2766 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
2767 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
2768 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
2769 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2770 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
2771 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2773 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2775 $disallowed_attributes = array('id', 'style', 'class');
2777 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_SANITIZE
) as $plugin) {
2778 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2779 if (is_array($retval)) {
2781 $allowed_elements = $retval[1];
2782 $disallowed_attributes = $retval[2];
2788 $doc->removeChild($doc->firstChild
); //remove doctype
2789 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2790 $res = $doc->saveHTML();
2794 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2795 $entries = $doc->getElementsByTagName("*");
2797 foreach ($entries as $entry) {
2798 if (!in_array($entry->nodeName
, $allowed_elements)) {
2799 $entry->parentNode
->removeChild($entry);
2802 if ($entry->hasAttributes()) {
2803 $attrs_to_remove = array();
2805 foreach ($entry->attributes
as $attr) {
2807 if (strpos($attr->nodeName
, 'on') === 0) {
2808 array_push($attrs_to_remove, $attr);
2811 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2812 array_push($attrs_to_remove, $attr);
2816 foreach ($attrs_to_remove as $attr) {
2817 $entry->removeAttributeNode($attr);
2825 function check_for_update() {
2826 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2827 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2828 "&iid=" . sha1(SELF_URL_PATH
);
2830 $version_data = @fetch_file_contents
($version_url);
2832 if ($version_data) {
2833 $version_data = json_decode($version_data, true);
2834 if ($version_data && $version_data['version']) {
2836 if (version_compare(VERSION
, $version_data['version']) == -1) {
2837 return $version_data;
2845 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
2847 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2848 if (count($ids) == 0) return;
2852 foreach ($ids as $id) {
2853 array_push($tmp_ids, "ref_id = '$id'");
2856 $ids_qpart = join(" OR ", $tmp_ids);
2859 db_query("UPDATE ttrss_user_entries SET
2860 unread = false,last_read = NOW()
2861 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2862 } else if ($cmode == 1) {
2863 db_query("UPDATE ttrss_user_entries SET
2865 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2867 db_query("UPDATE ttrss_user_entries SET
2868 unread = NOT unread,last_read = NOW()
2869 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2874 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
2875 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2877 while ($line = db_fetch_assoc($result)) {
2878 ccache_update($line["feed_id"], $owner_uid);
2882 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
2884 $a_id = db_escape_string($id);
2886 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2888 $query = "SELECT DISTINCT tag_name,
2889 owner_uid as owner FROM
2890 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2891 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2893 $obj_id = md5("TAGS:$owner_uid:$id");
2896 /* check cache first */
2898 if ($tag_cache === false) {
2899 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
2900 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2902 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2906 $tags = explode(",", $tag_cache);
2909 /* do it the hard way */
2911 $tmp_result = db_query($query);
2913 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2914 array_push($tags, $tmp_line["tag_name"]);
2917 /* update the cache */
2919 $tags_str = db_escape_string(join(",", $tags));
2921 db_query("UPDATE ttrss_user_entries
2922 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2923 AND owner_uid = $owner_uid");
2929 function trim_array($array) {
2931 array_walk($tmp, 'trim');
2935 function tag_is_valid($tag) {
2936 if ($tag == '') return false;
2937 if (preg_match("/^[0-9]*$/", $tag)) return false;
2938 if (mb_strlen($tag) > 250) return false;
2940 if (function_exists('iconv')) {
2941 $tag = iconv("utf-8", "utf-8", $tag);
2944 if (!$tag) return false;
2949 function render_login_form() {
2950 header('Cache-Control: public');
2952 require_once "login_form.php";
2956 function format_warning($msg, $id = "") {
2958 return "<div class=\"warning\" id=\"$id\">
2959 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2962 function format_notice($msg, $id = "") {
2964 return "<div class=\"notice\" id=\"$id\">
2965 <span><img src=\"images/sign_info.svg\"></span><span>$msg</span></div>";
2968 function format_error($msg, $id = "") {
2970 return "<div class=\"error\" id=\"$id\">
2971 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2974 function print_notice($msg) {
2975 return print format_notice($msg);
2978 function print_warning($msg) {
2979 return print format_warning($msg);
2982 function print_error($msg) {
2983 return print format_error($msg);
2987 function T_sprintf() {
2988 $args = func_get_args();
2989 return vsprintf(__(array_shift($args)), $args);
2992 function format_inline_player($url, $ctype) {
2996 $url = htmlspecialchars($url);
2998 if (strpos($ctype, "audio/") === 0) {
3000 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
3001 $_SESSION["hasMp3"])) {
3003 $entry .= "<audio controls>
3004 <source type=\"$ctype\" src=\"$url\"></source>
3009 $entry .= "<object type=\"application/x-shockwave-flash\"
3010 data=\"lib/button/musicplayer.swf?song_url=$url\"
3011 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
3012 <param name=\"movie\"
3013 value=\"lib/button/musicplayer.swf?song_url=$url\" />
3017 if ($entry) $entry .= " <a target=\"_blank\"
3018 href=\"$url\">" . basename($url) . "</a>";
3026 /* $filename = substr($url, strrpos($url, "/")+1);
3028 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3029 $filename . " (" . $ctype . ")" . "</a>"; */
3033 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
3034 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3040 /* we can figure out feed_id from article id anyway, why do we
3041 * pass feed_id here? let's ignore the argument :(*/
3043 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3044 WHERE ref_id = '$id'");
3046 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
3048 $rv['feed_id'] = $feed_id;
3050 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3052 if ($mark_as_read) {
3053 $result = db_query("UPDATE ttrss_user_entries
3054 SET unread = false,last_read = NOW()
3055 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
3057 ccache_update($feed_id, $owner_uid);
3060 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,
3061 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
3062 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
3063 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
3064 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
3071 FROM ttrss_entries,ttrss_user_entries
3072 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3076 $line = db_fetch_assoc($result);
3078 $tag_cache = $line["tag_cache"];
3080 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
3081 unset($line["tag_cache"]);
3083 $line["content"] = sanitize($line["content"], false, $owner_uid, $line["site_url"]);
3085 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_RENDER_ARTICLE
) as $p) {
3086 $line = $p->hook_render_article($line);
3089 $num_comments = $line["num_comments"];
3090 $entry_comments = "";
3092 if ($num_comments > 0) {
3093 if ($line["comments"]) {
3094 $comments_url = htmlspecialchars($line["comments"]);
3096 $comments_url = htmlspecialchars($line["link"]);
3098 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3100 if ($line["comments"] && $line["link"] != $line["comments"]) {
3101 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3106 header("Content-Type: text/html");
3107 $rv['content'] .= "<html><head>
3108 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3109 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3110 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3111 </head><body id=\"ttrssZoom\">";
3114 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3116 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3118 $entry_author = $line["author"];
3120 if ($entry_author) {
3121 $entry_author = __(" - ") . $entry_author;
3124 $parsed_updated = make_local_datetime($line["updated"], true,
3127 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3129 if ($line["link"]) {
3130 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3131 title=\"".htmlspecialchars($line['title'])."\"
3133 htmlspecialchars($line["link"]) . "\">" .
3134 $line["title"] . "</a>" .
3135 "<span class='author'>$entry_author</span></div>";
3137 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3140 $tags_str = format_tags_string($line["tags"], $id);
3141 $tags_str_full = join(", ", $line["tags"]);
3143 if (!$tags_str_full) $tags_str_full = __("no tags");
3145 if (!$entry_comments) $entry_comments = " "; # placeholder
3147 $rv['content'] .= "<div class='postTags' style='float : right'>
3148 <img src='images/tag.png'
3149 class='tagsPic' alt='Tags' title='Tags'> ";
3152 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3153 <a title=\"".__('Edit tags for this article')."\"
3154 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3156 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3157 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3158 position=\"below\">$tags_str_full</div>";
3160 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_BUTTON
) as $p) {
3161 $rv['content'] .= $p->hook_article_button($line);
3165 $tags_str = strip_tags($tags_str);
3166 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3168 $rv['content'] .= "</div>";
3169 $rv['content'] .= "<div clear='both'>";
3171 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_LEFT_BUTTON
) as $p) {
3172 $rv['content'] .= $p->hook_article_left_button($line);
3175 $rv['content'] .= "$entry_comments</div>";
3177 if ($line["orig_feed_id"]) {
3179 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
3180 WHERE id = ".$line["orig_feed_id"]);
3182 if (db_num_rows($tmp_result) != 0) {
3184 $rv['content'] .= "<div clear='both'>";
3185 $rv['content'] .= __("Originally from:");
3187 $rv['content'] .= " ";
3189 $tmp_line = db_fetch_assoc($tmp_result);
3191 $rv['content'] .= "<a target='_blank'
3192 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3193 $tmp_line['title'] . "</a>";
3195 $rv['content'] .= " ";
3197 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3198 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3200 $rv['content'] .= "</div>";
3204 $rv['content'] .= "</div>";
3206 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3207 if ($line['note']) {
3208 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3210 $rv['content'] .= "</div>";
3212 $rv['content'] .= "<div class=\"postContent\">";
3214 $rv['content'] .= $line["content"];
3215 $rv['content'] .= format_article_enclosures($id,
3216 sql_bool_to_bool($line["always_display_enclosures"]),
3218 sql_bool_to_bool($line["hide_images"]));
3220 $rv['content'] .= "</div>";
3222 $rv['content'] .= "</div>";
3228 <div class='footer'>
3229 <button onclick=\"return window.close()\">".
3230 __("Close this window")."</button></div>";
3231 $rv['content'] .= "</body></html>";
3238 function print_checkpoint($n, $s) {
3239 $ts = microtime(true);
3240 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3244 function sanitize_tag($tag) {
3247 $tag = mb_strtolower($tag, 'utf-8');
3249 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3251 // $tag = str_replace('"', "", $tag);
3252 // $tag = str_replace("+", " ", $tag);
3253 $tag = str_replace("technorati tag: ", "", $tag);
3258 function get_self_url_prefix() {
3259 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3260 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3262 return SELF_URL_PATH
;
3267 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3269 * @return string The Mozilla Firefox feed adding URL.
3271 function add_feed_url() {
3272 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3274 $url_path = get_self_url_prefix() .
3275 "/public.php?op=subscribe&feed_url=%s";
3277 } // function add_feed_url
3279 function encrypt_password($pass, $salt = '', $mode2 = false) {
3280 if ($salt && $mode2) {
3281 return "MODE2:" . hash('sha256', $salt . $pass);
3283 return "SHA1X:" . sha1("$salt:$pass");
3285 return "SHA1:" . sha1($pass);
3287 } // function encrypt_password
3289 function load_filters($feed_id, $owner_uid, $action_id = false) {
3292 $cat_id = (int)getFeedCategory($feed_id);
3294 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
3295 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3297 $check_cats = join(",", array_merge(
3298 getParentCategories($cat_id, $owner_uid),
3301 while ($line = db_fetch_assoc($result)) {
3302 $filter_id = $line["id"];
3304 $result2 = db_query("SELECT
3305 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3306 FROM ttrss_filters2_rules AS r,
3307 ttrss_filter_types AS t
3309 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3310 (feed_id IS NULL OR feed_id = '$feed_id') AND
3311 filter_type = t.id AND filter_id = '$filter_id'");
3316 while ($rule_line = db_fetch_assoc($result2)) {
3317 # print_r($rule_line);
3320 $rule["reg_exp"] = $rule_line["reg_exp"];
3321 $rule["type"] = $rule_line["type_name"];
3322 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3324 array_push($rules, $rule);
3327 $result2 = db_query("SELECT a.action_param,t.name AS type_name
3328 FROM ttrss_filters2_actions AS a,
3329 ttrss_filter_actions AS t
3331 action_id = t.id AND filter_id = '$filter_id'");
3333 while ($action_line = db_fetch_assoc($result2)) {
3334 # print_r($action_line);
3337 $action["type"] = $action_line["type_name"];
3338 $action["param"] = $action_line["action_param"];
3340 array_push($actions, $action);
3345 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3346 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3347 $filter["rules"] = $rules;
3348 $filter["actions"] = $actions;
3350 if (count($rules) > 0 && count($actions) > 0) {
3351 array_push($filters, $filter);
3358 function get_score_pic($score) {
3360 return "score_high.png";
3361 } else if ($score > 0) {
3362 return "score_half_high.png";
3363 } else if ($score < -100) {
3364 return "score_low.png";
3365 } else if ($score < 0) {
3366 return "score_half_low.png";
3368 return "score_neutral.png";
3372 function feed_has_icon($id) {
3373 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3376 function init_plugins() {
3377 PluginHost
::getInstance()->load(PLUGINS
, PluginHost
::KIND_ALL
);
3382 function format_tags_string($tags, $id) {
3385 $tags_nolinks_str = "";
3391 $formatted_tags = array();
3393 foreach ($tags as $tag) {
3395 $tag_escaped = str_replace("'", "\\'", $tag);
3397 if (mb_strlen($tag) > 30) {
3398 $tag = truncate_string($tag, 30);
3401 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3403 array_push($formatted_tags, $tag_str);
3405 $tmp_tags_str = implode(", ", $formatted_tags);
3407 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3412 $tags_str = implode(", ", $formatted_tags);
3414 if ($num_tags < count($tags)) {
3415 $tags_str .= ", …";
3418 if ($num_tags == 0) {
3419 $tags_str = __("no tags");
3426 function format_article_labels($labels, $id) {
3428 if (is_array($labels)) return '';
3432 foreach ($labels as $l) {
3433 $labels_str .= sprintf("<span class='hlLabelRef'
3434 style='color : %s; background-color : %s'>%s</span>",
3435 $l[2], $l[3], $l[1]);
3442 function format_article_note($id, $note, $allow_edit = true) {
3444 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3445 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3446 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3452 function get_feed_category($feed_cat, $parent_cat_id = false) {
3453 if ($parent_cat_id) {
3454 $parent_qpart = "parent_cat = '$parent_cat_id'";
3455 $parent_insert = "'$parent_cat_id'";
3457 $parent_qpart = "parent_cat IS NULL";
3458 $parent_insert = "NULL";
3462 "SELECT id FROM ttrss_feed_categories
3463 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3465 if (db_num_rows($result) == 0) {
3468 return db_fetch_result($result, 0, "id");
3472 function add_feed_category($feed_cat, $parent_cat_id = false) {
3474 if (!$feed_cat) return false;
3478 if ($parent_cat_id) {
3479 $parent_qpart = "parent_cat = '$parent_cat_id'";
3480 $parent_insert = "'$parent_cat_id'";
3482 $parent_qpart = "parent_cat IS NULL";
3483 $parent_insert = "NULL";
3486 $feed_cat = mb_substr($feed_cat, 0, 250);
3489 "SELECT id FROM ttrss_feed_categories
3490 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3492 if (db_num_rows($result) == 0) {
3495 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3496 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3506 function getArticleFeed($id) {
3507 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3508 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3510 if (db_num_rows($result) != 0) {
3511 return db_fetch_result($result, 0, "feed_id");
3518 * Fixes incomplete URLs by prepending "http://".
3519 * Also replaces feed:// with http://, and
3520 * prepends a trailing slash if the url is a domain name only.
3522 * @param string $url Possibly incomplete URL
3524 * @return string Fixed URL.
3526 function fix_url($url) {
3527 if (strpos($url, '://') === false) {
3528 $url = 'http://' . $url;
3529 } else if (substr($url, 0, 5) == 'feed:') {
3530 $url = 'http:' . substr($url, 5);
3533 //prepend slash if the URL has no slash in it
3534 // "http://www.example" -> "http://www.example/"
3535 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3539 if ($url != "http:///")
3545 function validate_feed_url($url) {
3546 $parts = parse_url($url);
3548 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3552 function get_article_enclosures($id) {
3554 $query = "SELECT * FROM ttrss_enclosures
3555 WHERE post_id = '$id' AND content_url != ''";
3559 $result = db_query($query);
3561 if (db_num_rows($result) > 0) {
3562 while ($line = db_fetch_assoc($result)) {
3563 array_push($rv, $line);
3570 function save_email_address($email) {
3571 // FIXME: implement persistent storage of emails
3573 if (!$_SESSION['stored_emails'])
3574 $_SESSION['stored_emails'] = array();
3576 if (!in_array($email, $_SESSION['stored_emails']))
3577 array_push($_SESSION['stored_emails'], $email);
3581 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
3583 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3585 $sql_is_cat = bool_to_sql_bool($is_cat);
3587 $result = db_query("SELECT access_key FROM ttrss_access_keys
3588 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3589 AND owner_uid = " . $owner_uid);
3591 if (db_num_rows($result) == 1) {
3592 return db_fetch_result($result, 0, "access_key");
3594 $key = db_escape_string(sha1(uniqid(rand(), true)));
3596 $result = db_query("INSERT INTO ttrss_access_keys
3597 (access_key, feed_id, is_cat, owner_uid)
3598 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3605 function get_feeds_from_html($url, $content)
3607 $url = fix_url($url);
3608 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3610 libxml_use_internal_errors(true);
3612 $doc = new DOMDocument();
3613 $doc->loadHTML($content);
3614 $xpath = new DOMXPath($doc);
3615 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3616 $feedUrls = array();
3617 foreach ($entries as $entry) {
3618 if ($entry->hasAttribute('href')) {
3619 $title = $entry->getAttribute('title');
3621 $title = $entry->getAttribute('type');
3623 $feedUrl = rewrite_relative_url(
3624 $baseUrl, $entry->getAttribute('href')
3626 $feedUrls[$feedUrl] = $title;
3632 function is_html($content) {
3633 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3636 function url_is_html($url, $login = false, $pass = false) {
3637 return is_html(fetch_file_contents($url, false, $login, $pass));
3640 function print_label_select($name, $value, $attributes = "") {
3642 $result = db_query("SELECT caption FROM ttrss_labels2
3643 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3645 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3646 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3648 while ($line = db_fetch_assoc($result)) {
3650 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3652 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3653 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3657 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3664 function format_article_enclosures($id, $always_display_enclosures,
3665 $article_content, $hide_images = false) {
3667 $result = get_article_enclosures($id);
3670 if (count($result) > 0) {
3672 $entries_html = array();
3674 $entries_inline = array();
3676 foreach ($result as $line) {
3678 $url = $line["content_url"];
3679 $ctype = $line["content_type"];
3681 if (!$ctype) $ctype = __("unknown type");
3683 $filename = substr($url, strrpos($url, "/")+
1);
3685 $player = format_inline_player($url, $ctype);
3687 if ($player) array_push($entries_inline, $player);
3689 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3690 # $filename . " (" . $ctype . ")" . "</a>";
3692 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3693 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3695 array_push($entries_html, $entry);
3699 $entry["type"] = $ctype;
3700 $entry["filename"] = $filename;
3701 $entry["url"] = $url;
3703 array_push($entries, $entry);
3706 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3707 if ($always_display_enclosures ||
3708 !preg_match("/<img/i", $article_content)) {
3710 foreach ($entries as $entry) {
3712 if (preg_match("/image/", $entry["type"]) ||
3713 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3715 if (!$hide_images) {
3717 alt=\"".htmlspecialchars($entry["filename"])."\"
3718 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3720 $rv .= "<p><a target=\"_blank\"
3721 href=\"".htmlspecialchars($entry["url"])."\"
3722 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3730 if (count($entries_inline) > 0) {
3731 $rv .= "<hr clear='both'/>";
3732 foreach ($entries_inline as $entry) { $rv .= $entry; };
3733 $rv .= "<hr clear='both'/>";
3736 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3737 "<option value=''>" . __('Attachments')."</option>";
3739 foreach ($entries as $entry) {
3740 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3750 function getLastArticleId() {
3751 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3752 WHERE owner_uid = " . $_SESSION["uid"]);
3754 if (db_num_rows($result) == 1) {
3755 return db_fetch_result($result, 0, "id");
3761 function build_url($parts) {
3762 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3766 * Converts a (possibly) relative URL to a absolute one.
3768 * @param string $url Base URL (i.e. from where the document is)
3769 * @param string $rel_url Possibly relative URL in the document
3771 * @return string Absolute URL
3773 function rewrite_relative_url($url, $rel_url) {
3774 if (strpos($rel_url, "magnet:") === 0) {
3776 } else if (strpos($rel_url, "://") !== false) {
3778 } else if (strpos($rel_url, "//") === 0) {
3779 # protocol-relative URL (rare but they exist)
3781 } else if (strpos($rel_url, "/") === 0)
3783 $parts = parse_url($url);
3784 $parts['path'] = $rel_url;
3786 return build_url($parts);
3789 $parts = parse_url($url);
3790 if (!isset($parts['path'])) {
3791 $parts['path'] = '/';
3793 $dir = $parts['path'];
3794 if (substr($dir, -1) !== '/') {
3795 $dir = dirname($parts['path']);
3796 $dir !== '/' && $dir .= '/';
3798 $parts['path'] = $dir . $rel_url;
3800 return build_url($parts);
3804 function sphinx_search($query, $offset = 0, $limit = 30) {
3805 require_once 'lib/sphinxapi.php';
3807 $sphinxClient = new SphinxClient();
3809 $sphinxpair = explode(":", SPHINX_SERVER
, 2);
3811 $sphinxClient->SetServer($sphinxpair[0], $sphinxpair[1]);
3812 $sphinxClient->SetConnectTimeout(1);
3814 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3815 'feed_title' => 20));
3817 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3818 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3819 $sphinxClient->SetLimits($offset, $limit, 1000);
3820 $sphinxClient->SetArrayResult(false);
3821 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3823 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3827 if (is_array($result['matches'])) {
3828 foreach (array_keys($result['matches']) as $int_id) {
3829 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3830 array_push($ids, $ref_id);
3837 function cleanup_tags($days = 14, $limit = 1000) {
3839 if (DB_TYPE
== "pgsql") {
3840 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3841 } else if (DB_TYPE
== "mysql") {
3842 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3847 while ($limit > 0) {
3850 $query = "SELECT ttrss_tags.id AS id
3851 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3852 WHERE post_int_id = int_id AND $interval_query AND
3853 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3855 $result = db_query($query);
3859 while ($line = db_fetch_assoc($result)) {
3860 array_push($ids, $line['id']);
3863 if (count($ids) > 0) {
3864 $ids = join(",", $ids);
3866 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
3867 $tags_deleted +
= db_affected_rows($tmp_result);
3872 $limit -= $limit_part;
3875 return $tags_deleted;
3878 function print_user_stylesheet() {
3879 $value = get_pref('USER_STYLESHEET');
3882 print "<style type=\"text/css\">";
3883 print str_replace("<br/>", "\n", $value);
3889 function rewrite_urls($html) {
3890 libxml_use_internal_errors(true);
3892 $charset_hack = '<head>
3893 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3896 $doc = new DOMDocument();
3897 $doc->loadHTML($charset_hack . $html);
3898 $xpath = new DOMXPath($doc);
3900 $entries = $xpath->query('//*/text()');
3902 foreach ($entries as $entry) {
3903 if (strstr($entry->wholeText
, "://") !== false) {
3904 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3905 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3907 if ($text != $entry->wholeText
) {
3908 $cdoc = new DOMDocument();
3909 $cdoc->loadHTML($charset_hack . $text);
3912 foreach ($cdoc->childNodes
as $cnode) {
3913 $cnode = $doc->importNode($cnode, true);
3916 $entry->parentNode
->insertBefore($cnode);
3920 $entry->parentNode
->removeChild($entry);
3926 $node = $doc->getElementsByTagName('body')->item(0);
3928 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3930 return $doc->saveXML($node);
3935 function filter_to_sql($filter, $owner_uid) {
3938 if (DB_TYPE
== "pgsql")
3941 $reg_qpart = "REGEXP";
3943 foreach ($filter["rules"] AS $rule) {
3944 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3945 $rule['reg_exp']) !== FALSE;
3947 if ($regexp_valid) {
3949 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
3951 switch ($rule["type"]) {
3953 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3954 $rule['reg_exp'] . "')";
3957 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3958 $rule['reg_exp'] . "')";
3961 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3962 $rule['reg_exp'] . "') OR LOWER(" .
3963 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3966 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3967 $rule['reg_exp'] . "')";
3970 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3971 $rule['reg_exp'] . "')";
3974 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3975 $rule['reg_exp'] . "')";
3979 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3981 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3982 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
3985 if (isset($rule["cat_id"])) {
3987 if ($rule["cat_id"] > 0) {
3988 $children = getChildCategories($rule["cat_id"], $owner_uid);
3989 array_push($children, $rule["cat_id"]);
3991 $children = join(",", $children);
3993 $cat_qpart = "cat_id IN ($children)";
3995 $cat_qpart = "cat_id IS NULL";
3998 $qpart .= " AND $cat_qpart";
4001 array_push($query, "($qpart)");
4006 if (count($query) > 0) {
4007 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
4009 $fullquery = "(false)";
4012 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
4017 if (!function_exists('gzdecode')) {
4018 function gzdecode($string) { // no support for 2nd argument
4019 return file_get_contents('compress.zlib://data:who/cares;base64,'.
4020 base64_encode($string));
4024 function get_random_bytes($length) {
4025 if (function_exists('openssl_random_pseudo_bytes')) {
4026 return openssl_random_pseudo_bytes($length);
4030 for ($i = 0; $i < $length; $i++
)
4031 $output .= chr(mt_rand(0, 255));
4037 function read_stdin() {
4038 $fp = fopen("php://stdin", "r");
4041 $line = trim(fgets($fp));
4049 function tmpdirname($path, $prefix) {
4050 // Use PHP's tmpfile function to create a temporary
4051 // directory name. Delete the file and keep the name.
4052 $tempname = tempnam($path,$prefix);
4056 if (!unlink($tempname))
4062 function getFeedCategory($feed) {
4063 $result = db_query("SELECT cat_id FROM ttrss_feeds
4064 WHERE id = '$feed'");
4066 if (db_num_rows($result) > 0) {
4067 return db_fetch_result($result, 0, "cat_id");
4074 function implements_interface($class, $interface) {
4075 return in_array($interface, class_implements($class));
4078 function geturl($url){
4080 if (!function_exists('curl_init'))
4081 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
);
4083 $curl = curl_init();
4084 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4085 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4086 $header[] = "Cache-Control: max-age=0";
4087 $header[] = "Connection: keep-alive";
4088 $header[] = "Keep-Alive: 300";
4089 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4090 $header[] = "Accept-Language: en-us,en;q=0.5";
4091 $header[] = "Pragma: ";
4093 curl_setopt($curl, CURLOPT_URL
, $url);
4094 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4095 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4096 curl_setopt($curl, CURLOPT_HEADER
, true);
4097 curl_setopt($curl, CURLOPT_REFERER
, $url);
4098 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4099 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4100 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4101 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4102 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4104 $html = curl_exec($curl);
4106 $status = curl_getinfo($curl);
4109 if($status['http_code']!=200){
4110 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4111 list($header) = explode("\r\n\r\n", $html, 2);
4113 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4114 $url = trim(str_replace($matches[1],"",$matches[0]));
4115 $url_parsed = parse_url($url);
4116 return (isset($url_parsed))?
geturl($url, $referer):'';
4119 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4120 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4121 # $handle = @fopen('./curl.error.log', 'a');
4122 # fwrite($handle, $line);
4128 function get_minified_js($files) {
4129 require_once 'lib/jshrink/Minifier.php';
4133 foreach ($files as $js) {
4134 if (!isset($_GET['debug'])) {
4135 $cached_file = CACHE_DIR
. "/js/$js.js";
4137 if (file_exists($cached_file) &&
4138 is_readable($cached_file) &&
4139 filemtime($cached_file) >= filemtime("js/$js.js")) {
4141 $rv .= file_get_contents($cached_file);
4144 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4145 file_put_contents($cached_file, $minified);
4149 $rv .= file_get_contents("js/$js.js");
4156 function stylesheet_tag($filename) {
4157 $timestamp = filemtime($filename);
4159 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4162 function javascript_tag($filename) {
4165 if (!(strpos($filename, "?") === FALSE)) {
4166 $query = substr($filename, strpos($filename, "?")+
1);
4167 $filename = substr($filename, 0, strpos($filename, "?"));
4170 $timestamp = filemtime($filename);
4172 if ($query) $timestamp .= "&$query";
4174 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4177 function calculate_dep_timestamp() {
4178 $files = array_merge(glob("js/*.js"), glob("*.css"));
4182 foreach ($files as $file) {
4183 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4189 function T_js_decl($s1, $s2) {
4191 $s1 = preg_replace("/\n/", "", $s1);
4192 $s2 = preg_replace("/\n/", "", $s2);
4194 $s1 = preg_replace("/\"/", "\\\"", $s1);
4195 $s2 = preg_replace("/\"/", "\\\"", $s2);
4197 return "T_messages[\"$s1\"] = \"$s2\";\n";
4201 function init_js_translations() {
4203 print 'var T_messages = new Object();
4206 if (T_messages[msg]) {
4207 return T_messages[msg];
4213 function ngettext(msg1, msg2, n) {
4214 return (parseInt(n) > 1) ? msg2 : msg1;
4217 $l10n = _get_reader();
4219 for ($i = 0; $i < $l10n->total
; $i++
) {
4220 $orig = $l10n->get_original_string($i);
4221 $translation = __($orig);
4223 print T_js_decl($orig, $translation);
4227 function label_to_feed_id($label) {
4228 return LABEL_BASE_INDEX
- 1 - abs($label);
4231 function feed_to_label_id($feed) {
4232 return LABEL_BASE_INDEX
- 1 +
abs($feed);
4235 function format_libxml_error($error) {
4236 return T_sprintf("LibXML error %s at line %d (column %d): %s",
4237 $error->code
, $error->line
, $error->column
,