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';
128 $schema_version = false;
131 * Print a timestamped debug message.
133 * @param string $msg The debug message.
136 function _debug($msg, $show = true) {
138 $ts = strftime("%H:%M:%S", time());
139 if (function_exists('posix_getpid')) {
140 $ts = "$ts/" . posix_getpid();
143 if ($show && !(defined('QUIET') && QUIET
)) {
144 print "[$ts] $msg\n";
147 if (defined('LOGFILE')) {
148 $fp = fopen(LOGFILE
, 'a+');
151 fputs($fp, "[$ts] $msg\n");
159 * Purge a feed old posts.
161 * @param mixed $link A database connection.
162 * @param mixed $feed_id The id of the purged feed.
163 * @param mixed $purge_interval Olderness of purged posts.
164 * @param boolean $debug Set to True to enable the debug. False by default.
168 function purge_feed($feed_id, $purge_interval, $debug = false) {
170 if (!$purge_interval) $purge_interval = feed_purge_interval($feed_id);
175 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
179 if (db_num_rows($result) == 1) {
180 $owner_uid = db_fetch_result($result, 0, "owner_uid");
183 if ($purge_interval == -1 ||
!$purge_interval) {
185 ccache_update($feed_id, $owner_uid);
190 if (!$owner_uid) return;
192 if (FORCE_ARTICLE_PURGE
== 0) {
193 $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
196 $purge_unread = true;
197 $purge_interval = FORCE_ARTICLE_PURGE
;
200 if (!$purge_unread) $query_limit = " unread = false AND ";
202 if (DB_TYPE
== "pgsql") {
203 $pg_version = get_pgsql_version();
205 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
207 $result = db_query("DELETE FROM ttrss_user_entries WHERE
208 ttrss_entries.id = ref_id AND
210 feed_id = '$feed_id' AND
212 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
216 $result = db_query("DELETE FROM ttrss_user_entries
218 WHERE ttrss_entries.id = ref_id AND
220 feed_id = '$feed_id' AND
222 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
227 /* $result = db_query("DELETE FROM ttrss_user_entries WHERE
228 marked = false AND feed_id = '$feed_id' AND
229 (SELECT date_updated FROM ttrss_entries WHERE
230 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
232 $result = db_query("DELETE FROM ttrss_user_entries
233 USING ttrss_user_entries, ttrss_entries
234 WHERE ttrss_entries.id = ref_id AND
236 feed_id = '$feed_id' AND
238 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
241 $rows = db_affected_rows($result);
243 ccache_update($feed_id, $owner_uid);
246 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
250 } // function purge_feed
252 function feed_purge_interval($feed_id) {
254 $result = db_query("SELECT purge_interval, owner_uid FROM ttrss_feeds
255 WHERE id = '$feed_id'");
257 if (db_num_rows($result) == 1) {
258 $purge_interval = db_fetch_result($result, 0, "purge_interval");
259 $owner_uid = db_fetch_result($result, 0, "owner_uid");
261 if ($purge_interval == 0) $purge_interval = get_pref(
262 'PURGE_OLD_DAYS', $owner_uid);
264 return $purge_interval;
271 function purge_orphans($do_output = false) {
273 // purge orphaned posts in main content table
274 $result = db_query("DELETE FROM ttrss_entries WHERE
275 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
278 $rows = db_affected_rows($result);
279 _debug("Purged $rows orphaned posts.");
283 function get_feed_update_interval($feed_id) {
284 $result = db_query("SELECT owner_uid, update_interval FROM
285 ttrss_feeds WHERE id = '$feed_id'");
287 if (db_num_rows($result) == 1) {
288 $update_interval = db_fetch_result($result, 0, "update_interval");
289 $owner_uid = db_fetch_result($result, 0, "owner_uid");
291 if ($update_interval != 0) {
292 return $update_interval;
294 return get_pref('DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
302 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
304 global $fetch_last_error;
305 global $fetch_last_error_code;
306 global $fetch_last_content_type;
307 global $fetch_curl_used;
309 $url = str_replace(' ', '%20', $url);
311 if (!defined('NO_CURL') && function_exists('curl_init')) {
313 $fetch_curl_used = true;
315 if (ini_get("safe_mode") ||
ini_get("open_basedir")) {
316 $ch = curl_init(geturl($url));
318 $ch = curl_init($url);
321 if ($timestamp && !$post_query) {
322 curl_setopt($ch, CURLOPT_HTTPHEADER
,
323 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
326 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : FILE_FETCH_CONNECT_TIMEOUT
);
327 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : FILE_FETCH_TIMEOUT
);
328 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode") && !ini_get("open_basedir"));
329 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
330 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
331 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
332 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
333 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
334 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
335 curl_setopt($ch, CURLOPT_ENCODING
, "");
336 curl_setopt($ch, CURLOPT_REFERER
, $url);
339 curl_setopt($ch, CURLOPT_POST
, true);
340 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
344 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
346 $contents = @curl_exec
($ch);
348 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
349 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
350 $contents = @curl_exec
($ch);
353 if ($contents === false) {
354 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
359 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
360 $fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
362 $fetch_last_error_code = $http_code;
364 if ($http_code != 200 ||
$type && strpos($fetch_last_content_type, "$type") === false) {
365 if (curl_errno($ch) != 0) {
366 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
368 $fetch_last_error = "HTTP Code: $http_code";
379 $fetch_curl_used = false;
381 if ($login && $pass){
382 $url_parts = array();
384 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
386 $pass = urlencode($pass);
388 if ($url_parts[1] && $url_parts[2]) {
389 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
393 if (!$post_query && $timestamp) {
394 $context = stream_context_create(array(
397 'header' => "If-Modified-Since: ".gmdate("D, d M Y H:i:s \\G\\M\\T\r\n", $timestamp)
403 $old_error = error_get_last();
405 $data = @file_get_contents
($url, false, $context);
407 $fetch_last_content_type = false; // reset if no type was sent from server
408 if (isset($http_response_header) && is_array($http_response_header)) {
409 foreach ($http_response_header as $h) {
410 if (substr(strtolower($h), 0, 13) == 'content-type:') {
411 $fetch_last_content_type = substr($h, 14);
412 // don't abort here b/c there might be more than one
413 // e.g. if we were being redirected -- last one is the right one
416 if (substr(strtolower($h), 0, 7) == 'http/1.') {
417 $fetch_last_error_code = (int) substr($h, 9, 3);
423 $error = error_get_last();
425 if ($error['message'] != $old_error['message']) {
426 $fetch_last_error = $error["message"];
428 $fetch_last_error = "HTTP Code: $fetch_last_error_code";
437 * Try to determine the favicon URL for a feed.
438 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
439 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
441 * @param string $url A feed or page URL
443 * @return mixed The favicon URL, or false if none was found.
445 function get_favicon_url($url) {
447 $favicon_url = false;
449 if ($html = @fetch_file_contents
($url)) {
451 libxml_use_internal_errors(true);
453 $doc = new DOMDocument();
454 $doc->loadHTML($html);
455 $xpath = new DOMXPath($doc);
457 $base = $xpath->query('/html/head/base');
458 foreach ($base as $b) {
459 $url = $b->getAttribute("href");
463 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
464 if (count($entries) > 0) {
465 foreach ($entries as $entry) {
466 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
473 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
476 } // function get_favicon_url
478 function check_feed_favicon($site_url, $feed) {
479 # print "FAVICON [$site_url]: $favicon_url\n";
481 $icon_file = ICONS_DIR
. "/$feed.ico";
483 if (!file_exists($icon_file)) {
484 $favicon_url = get_favicon_url($site_url);
487 // Limiting to "image" type misses those served with text/plain
488 $contents = fetch_file_contents($favicon_url); // , "image");
491 // Crude image type matching.
492 // Patterns gleaned from the file(1) source code.
493 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
494 // 0 string \000\000\001\000 MS Windows icon resource
495 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
497 elseif (preg_match('/^GIF8/', $contents)) {
498 // 0 string GIF8 GIF image data
499 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
501 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
502 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
503 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
505 elseif (preg_match('/^\xff\xd8/', $contents)) {
506 // 0 beshort 0xffd8 JPEG image data
507 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
510 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
516 $fp = @fopen
($icon_file, "w");
519 fwrite($fp, $contents);
521 chmod($icon_file, 0644);
529 function print_select($id, $default, $values, $attributes = "") {
530 print "<select name=\"$id\" id=\"$id\" $attributes>";
531 foreach ($values as $v) {
533 $sel = "selected=\"1\"";
539 print "<option value=\"$v\" $sel>$v</option>";
544 function print_select_hash($id, $default, $values, $attributes = "") {
545 print "<select name=\"$id\" id='$id' $attributes>";
546 foreach (array_keys($values) as $v) {
548 $sel = 'selected="selected"';
554 print "<option $sel value=\"$v\">".$values[$v]."</option>";
560 function print_radio($id, $default, $true_is, $values, $attributes = "") {
561 foreach ($values as $v) {
568 if ($v == $true_is) {
569 $sel .= " value=\"1\"";
571 $sel .= " value=\"0\"";
574 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
575 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
580 function initialize_user_prefs($uid, $profile = false) {
582 $uid = db_escape_string($uid);
586 $profile_qpart = "AND profile IS NULL";
588 $profile_qpart = "AND profile = '$profile'";
591 if (get_schema_version() < 63) $profile_qpart = "";
595 $result = db_query("SELECT pref_name,def_value FROM ttrss_prefs");
597 $u_result = db_query("SELECT pref_name
598 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
600 $active_prefs = array();
602 while ($line = db_fetch_assoc($u_result)) {
603 array_push($active_prefs, $line["pref_name"]);
606 while ($line = db_fetch_assoc($result)) {
607 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
608 // print "adding " . $line["pref_name"] . "<br>";
610 $line["def_value"] = db_escape_string($line["def_value"]);
611 $line["pref_name"] = db_escape_string($line["pref_name"]);
613 if (get_schema_version() < 63) {
614 db_query("INSERT INTO ttrss_user_prefs
615 (owner_uid,pref_name,value) VALUES
616 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
619 db_query("INSERT INTO ttrss_user_prefs
620 (owner_uid,pref_name,value, profile) VALUES
621 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
631 function get_ssl_certificate_id() {
632 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
633 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
634 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
635 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
636 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
641 function authenticate_user($login, $password, $check_only = false) {
643 if (!SINGLE_USER_MODE
) {
646 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_AUTH_USER
) as $plugin) {
648 $user_id = (int) $plugin->authenticate($login, $password);
651 $_SESSION["auth_module"] = strtolower(get_class($plugin));
656 if ($user_id && !$check_only) {
659 $_SESSION["uid"] = $user_id;
660 $_SESSION["version"] = VERSION_STATIC
;
662 $result = db_query("SELECT login,access_level,pwd_hash FROM ttrss_users
663 WHERE id = '$user_id'");
665 $_SESSION["name"] = db_fetch_result($result, 0, "login");
666 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
667 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
669 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
672 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
673 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
674 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
676 $_SESSION["last_version_check"] = time();
678 initialize_user_prefs($_SESSION["uid"]);
687 $_SESSION["uid"] = 1;
688 $_SESSION["name"] = "admin";
689 $_SESSION["access_level"] = 10;
691 $_SESSION["hide_hello"] = true;
692 $_SESSION["hide_logout"] = true;
694 $_SESSION["auth_module"] = false;
696 if (!$_SESSION["csrf_token"]) {
697 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
700 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
702 initialize_user_prefs($_SESSION["uid"]);
708 function make_password($length = 8) {
711 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
715 while ($i < $length) {
716 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
718 if (!strstr($password, $char)) {
726 // this is called after user is created to initialize default feeds, labels
729 // user preferences are checked on every login, not here
731 function initialize_user($uid) {
733 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
734 values ('$uid', 'Tiny Tiny RSS: New Releases',
735 'http://tt-rss.org/releases.rss')");
737 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
738 values ('$uid', 'Tiny Tiny RSS: Forum',
739 'http://tt-rss.org/forum/rss.php')");
742 function logout_user() {
744 if (isset($_COOKIE[session_name()])) {
745 setcookie(session_name(), '', time()-42000, '/');
749 function validate_csrf($csrf_token) {
750 return $csrf_token == $_SESSION['csrf_token'];
753 function load_user_plugins($owner_uid) {
755 $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
757 PluginHost
::getInstance()->load($plugins, PluginHost
::KIND_USER
, $owner_uid);
759 if (get_schema_version() > 100) {
760 PluginHost
::getInstance()->load_data();
765 function login_sequence() {
766 if (SINGLE_USER_MODE
) {
768 authenticate_user("admin", null);
769 load_user_plugins($_SESSION["uid"]);
771 if (!validate_session()) $_SESSION["uid"] = false;
773 if (!$_SESSION["uid"]) {
775 if (AUTH_AUTO_LOGIN
&& authenticate_user(null, null)) {
776 $_SESSION["ref_schema_version"] = get_schema_version(true);
778 authenticate_user(null, null, true);
781 if (!$_SESSION["uid"]) {
783 setcookie(session_name(), '', time()-42000, '/');
790 /* bump login timestamp */
791 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
793 $_SESSION["last_login_update"] = time();
796 if ($_SESSION["uid"] && $_SESSION["language"] && SESSION_COOKIE_LIFETIME
> 0) {
797 setcookie("ttrss_lang", $_SESSION["language"],
798 time() + SESSION_COOKIE_LIFETIME
);
801 if ($_SESSION["uid"]) {
802 load_user_plugins($_SESSION["uid"]);
806 db_query("DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
807 $_SESSION["uid"] . " AND
808 (SELECT COUNT(id) FROM ttrss_feeds WHERE
809 ttrss_feeds.id = feed_id) = 0");
811 db_query("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
812 $_SESSION["uid"] . " AND
813 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
814 ttrss_feed_categories.id = feed_id) = 0");
821 function truncate_string($str, $max_len, $suffix = '…') {
822 if (mb_strlen($str, "utf-8") > $max_len - 3) {
823 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
829 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
832 $source_tz = new DateTimeZone($source_tz);
833 } catch (Exception
$e) {
834 $source_tz = new DateTimeZone('UTC');
838 $dest_tz = new DateTimeZone($dest_tz);
839 } catch (Exception
$e) {
840 $dest_tz = new DateTimeZone('UTC');
843 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
844 return $dt->format('U') +
$dest_tz->getOffset($dt);
847 function make_local_datetime($timestamp, $long, $owner_uid = false,
848 $no_smart_dt = false) {
850 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
851 if (!$timestamp) $timestamp = '1970-01-01 0:00';
856 if (!$utc_tz) $utc_tz = new DateTimeZone('UTC');
858 $timestamp = substr($timestamp, 0, 19);
860 # We store date in UTC internally
861 $dt = new DateTime($timestamp, $utc_tz);
863 $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
866 if (!$user_tz) $user_tz = new DateTimeZone($user_tz_string);
867 } catch (Exception
$e) {
871 $tz_offset = $user_tz->getOffset($dt);
873 $user_timestamp = $dt->format('U') +
$tz_offset;
876 return smart_date_time($user_timestamp,
877 $tz_offset, $owner_uid);
880 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
882 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
884 return date($format, $user_timestamp);
888 function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false) {
889 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
891 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
892 return date("G:i", $timestamp);
893 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
894 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
895 return date($format, $timestamp);
897 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
898 return date($format, $timestamp);
902 function sql_bool_to_bool($s) {
903 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
910 function bool_to_sql_bool($s) {
918 // Session caching removed due to causing wrong redirects to upgrade
919 // script when get_schema_version() is called on an obsolete session
920 // created on a previous schema version.
921 function get_schema_version($nocache = false) {
922 global $schema_version;
924 if (!$schema_version && !$nocache) {
925 $result = db_query("SELECT schema_version FROM ttrss_version");
926 $version = db_fetch_result($result, 0, "schema_version");
927 $schema_version = $version;
930 return $schema_version;
934 function sanity_check() {
935 require_once 'errors.php';
938 $schema_version = get_schema_version(true);
940 if ($schema_version != SCHEMA_VERSION
) {
944 if (DB_TYPE
== "mysql") {
945 $result = db_query("SELECT true", false);
946 if (db_num_rows($result) != 1) {
951 if (db_escape_string("testTEST") != "testTEST") {
955 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
958 function file_is_locked($filename) {
959 if (function_exists('flock')) {
960 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
962 if (flock($fp, LOCK_EX | LOCK_NB
)) {
973 return true; // consider the file always locked and skip the test
976 function make_lockfile($filename) {
977 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
979 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
980 if (function_exists('posix_getpid')) {
981 fwrite($fp, posix_getpid() . "\n");
989 function make_stampfile($filename) {
990 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
992 if (flock($fp, LOCK_EX | LOCK_NB
)) {
993 fwrite($fp, time() . "\n");
1002 function sql_random_function() {
1003 if (DB_TYPE
== "mysql") {
1010 function catchup_feed($feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
1012 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
1014 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
1016 // Todo: all this interval stuff needs some generic generator function
1018 $date_qpart = "false";
1022 if (DB_TYPE
== "pgsql") {
1023 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
1025 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
1029 if (DB_TYPE
== "pgsql") {
1030 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
1032 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
1036 if (DB_TYPE
== "pgsql") {
1037 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
1039 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
1043 $date_qpart = "true";
1046 if (is_numeric($feed)) {
1052 $children = getChildCategories($feed, $owner_uid);
1053 array_push($children, $feed);
1055 $children = join(",", $children);
1057 $cat_qpart = "cat_id IN ($children)";
1059 $cat_qpart = "cat_id IS NULL";
1062 db_query("UPDATE ttrss_user_entries
1063 SET unread = false, last_read = NOW() WHERE ref_id IN
1065 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1066 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1067 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1069 } else if ($feed == -2) {
1071 db_query("UPDATE ttrss_user_entries
1072 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1073 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1074 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1077 } else if ($feed > 0) {
1079 db_query("UPDATE ttrss_user_entries
1080 SET unread = false, last_read = NOW() WHERE ref_id IN
1082 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1083 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1085 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1088 db_query("UPDATE ttrss_user_entries
1089 SET unread = false, last_read = NOW() WHERE ref_id IN
1091 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1092 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1096 db_query("UPDATE ttrss_user_entries
1097 SET unread = false, last_read = NOW() WHERE ref_id IN
1099 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1100 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1105 $intl = get_pref("FRESH_ARTICLE_MAX_AGE");
1107 if (DB_TYPE
== "pgsql") {
1108 $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
1110 $match_part = "date_entered > DATE_SUB(NOW(),
1111 INTERVAL $intl HOUR) ";
1114 db_query("UPDATE ttrss_user_entries
1115 SET unread = false, last_read = NOW() WHERE ref_id IN
1117 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1118 AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
1122 db_query("UPDATE ttrss_user_entries
1123 SET unread = false, last_read = NOW() WHERE ref_id IN
1125 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1126 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1129 } else if ($feed < LABEL_BASE_INDEX
) { // label
1131 $label_id = feed_to_label_id($feed);
1133 db_query("UPDATE ttrss_user_entries
1134 SET unread = false, last_read = NOW() WHERE ref_id IN
1136 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1137 AND label_id = '$label_id' AND ref_id = article_id
1138 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1142 ccache_update($feed, $owner_uid, $cat_view);
1145 db_query("UPDATE ttrss_user_entries
1146 SET unread = false, last_read = NOW() WHERE ref_id IN
1148 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1149 AND post_int_id = int_id AND tag_name = '$feed'
1150 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1155 function getAllCounters() {
1156 $data = getGlobalCounters();
1158 $data = array_merge($data, getVirtCounters());
1159 $data = array_merge($data, getLabelCounters());
1160 $data = array_merge($data, getFeedCounters($active_feed));
1161 $data = array_merge($data, getCategoryCounters());
1166 function getCategoryTitle($cat_id) {
1168 if ($cat_id == -1) {
1169 return __("Special");
1170 } else if ($cat_id == -2) {
1171 return __("Labels");
1174 $result = db_query("SELECT title FROM ttrss_feed_categories WHERE
1177 if (db_num_rows($result) == 1) {
1178 return db_fetch_result($result, 0, "title");
1180 return __("Uncategorized");
1186 function getCategoryCounters() {
1189 /* Labels category */
1191 $cv = array("id" => -2, "kind" => "cat",
1192 "counter" => getCategoryUnread(-2));
1194 array_push($ret_arr, $cv);
1196 $result = db_query("SELECT id AS cat_id, value AS unread,
1197 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1198 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1199 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1200 WHERE ttrss_cat_counters_cache.feed_id = id AND
1201 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1202 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1204 while ($line = db_fetch_assoc($result)) {
1205 $line["cat_id"] = (int) $line["cat_id"];
1207 if ($line["num_children"] > 0) {
1208 $child_counter = getCategoryChildrenUnread($line["cat_id"], $_SESSION["uid"]);
1213 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1214 "counter" => $line["unread"] +
$child_counter);
1216 array_push($ret_arr, $cv);
1219 /* Special case: NULL category doesn't actually exist in the DB */
1221 $cv = array("id" => 0, "kind" => "cat",
1222 "counter" => (int) ccache_find(0, $_SESSION["uid"], true));
1224 array_push($ret_arr, $cv);
1229 // only accepts real cats (>= 0)
1230 function getCategoryChildrenUnread($cat, $owner_uid = false) {
1231 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1233 $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1234 AND owner_uid = $owner_uid");
1238 while ($line = db_fetch_assoc($result)) {
1239 $unread +
= getCategoryUnread($line["id"], $owner_uid);
1240 $unread +
= getCategoryChildrenUnread($line["id"], $owner_uid);
1246 function getCategoryUnread($cat, $owner_uid = false) {
1248 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1253 $cat_query = "cat_id = '$cat'";
1255 $cat_query = "cat_id IS NULL";
1258 $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query
1259 AND owner_uid = " . $owner_uid);
1261 $cat_feeds = array();
1262 while ($line = db_fetch_assoc($result)) {
1263 array_push($cat_feeds, "feed_id = " . $line["id"]);
1266 if (count($cat_feeds) == 0) return 0;
1268 $match_part = implode(" OR ", $cat_feeds);
1270 $result = db_query("SELECT COUNT(int_id) AS unread
1271 FROM ttrss_user_entries
1272 WHERE unread = true AND ($match_part)
1273 AND owner_uid = " . $owner_uid);
1277 # this needs to be rewritten
1278 while ($line = db_fetch_assoc($result)) {
1279 $unread +
= $line["unread"];
1283 } else if ($cat == -1) {
1284 return getFeedUnread(-1) +
getFeedUnread($link, -2) +
getFeedUnread($link, -3) +
getFeedUnread($link, 0);
1285 } else if ($cat == -2) {
1287 $result = db_query("
1288 SELECT COUNT(unread) AS unread FROM
1289 ttrss_user_entries, ttrss_user_labels2
1290 WHERE article_id = ref_id AND unread = true
1291 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1293 $unread = db_fetch_result($result, 0, "unread");
1300 function getFeedUnread($feed, $is_cat = false) {
1301 return getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
1304 function getLabelUnread($label_id, $owner_uid = false) {
1305 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1307 $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1308 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1310 if (db_num_rows($result) != 0) {
1311 return db_fetch_result($result, 0, "unread");
1317 function getFeedArticles($feed, $is_cat = false, $unread_only = false,
1318 $owner_uid = false) {
1320 $n_feed = (int) $feed;
1321 $need_entries = false;
1323 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1326 $unread_qpart = "unread = true";
1328 $unread_qpart = "true";
1332 return getCategoryUnread($n_feed, $owner_uid);
1333 } else if ($n_feed == -6) {
1335 } else if ($feed != "0" && $n_feed == 0) {
1337 $feed = db_escape_string($feed);
1339 $result = db_query("SELECT SUM((SELECT COUNT(int_id)
1340 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1341 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1342 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1343 return db_fetch_result($result, 0, "count");
1345 } else if ($n_feed == -1) {
1346 $match_part = "marked = true";
1347 } else if ($n_feed == -2) {
1348 $match_part = "published = true";
1349 } else if ($n_feed == -3) {
1350 $match_part = "unread = true AND score >= 0";
1352 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1354 if (DB_TYPE
== "pgsql") {
1355 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1357 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1360 $need_entries = true;
1362 } else if ($n_feed == -4) {
1363 $match_part = "true";
1364 } else if ($n_feed >= 0) {
1367 $match_part = "feed_id = '$n_feed'";
1369 $match_part = "feed_id IS NULL";
1372 } else if ($feed < LABEL_BASE_INDEX
) {
1374 $label_id = feed_to_label_id($feed);
1376 return getLabelUnread($label_id, $owner_uid);
1382 if ($need_entries) {
1383 $from_qpart = "ttrss_user_entries,ttrss_entries";
1384 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1386 $from_qpart = "ttrss_user_entries";
1389 $query = "SELECT count(int_id) AS unread
1390 FROM $from_qpart WHERE
1391 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1393 //echo "[$feed/$query]\n";
1395 $result = db_query($query);
1399 $result = db_query("SELECT COUNT(post_int_id) AS unread
1400 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1401 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1402 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1405 $unread = db_fetch_result($result, 0, "unread");
1410 function getGlobalUnread($user_id = false) {
1413 $user_id = $_SESSION["uid"];
1416 $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1417 WHERE owner_uid = '$user_id' AND feed_id > 0");
1419 $c_id = db_fetch_result($result, 0, "c_id");
1424 function getGlobalCounters($global_unread = -1) {
1427 if ($global_unread == -1) {
1428 $global_unread = getGlobalUnread();
1431 $cv = array("id" => "global-unread",
1432 "counter" => (int) $global_unread);
1434 array_push($ret_arr, $cv);
1436 $result = db_query("SELECT COUNT(id) AS fn FROM
1437 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1439 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1441 $cv = array("id" => "subscribed-feeds",
1442 "counter" => (int) $subscribed_feeds);
1444 array_push($ret_arr, $cv);
1449 function getVirtCounters() {
1453 for ($i = 0; $i >= -4; $i--) {
1455 $count = getFeedUnread($i);
1457 $cv = array("id" => $i,
1458 "counter" => (int) $count);
1460 // if (get_pref('EXTENDED_FEEDLIST'))
1461 // $cv["xmsg"] = getFeedArticles($i)." ".__("total");
1463 array_push($ret_arr, $cv);
1466 $feeds = PluginHost
::getInstance()->get_feeds(-1);
1468 if (is_array($feeds)) {
1469 foreach ($feeds as $feed) {
1470 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1471 "counter" => $feed['sender']->get_unread($feed['id']));
1472 array_push($ret_arr, $cv);
1479 function getLabelCounters($descriptions = false) {
1483 $owner_uid = $_SESSION["uid"];
1485 $result = db_query("SELECT id,caption,COUNT(unread) AS unread
1486 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1487 (ttrss_labels2.id = label_id)
1488 LEFT JOIN ttrss_user_entries ON (ref_id = article_id AND unread = true
1489 AND ttrss_user_entries.owner_uid = $owner_uid)
1490 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1491 ttrss_labels2.caption");
1493 while ($line = db_fetch_assoc($result)) {
1495 $id = label_to_feed_id($line["id"]);
1497 $label_name = $line["caption"];
1498 $count = $line["unread"];
1500 $cv = array("id" => $id,
1501 "counter" => (int) $count);
1504 $cv["description"] = $label_name;
1506 // if (get_pref('EXTENDED_FEEDLIST'))
1507 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1509 array_push($ret_arr, $cv);
1515 function getFeedCounters($active_feed = false) {
1519 $query = "SELECT ttrss_feeds.id,
1521 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1522 last_error, value AS count
1523 FROM ttrss_feeds, ttrss_counters_cache
1524 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1525 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1526 AND ttrss_counters_cache.feed_id = id";
1528 $result = db_query($query);
1529 $fctrs_modified = false;
1531 while ($line = db_fetch_assoc($result)) {
1534 $count = $line["count"];
1535 $last_error = htmlspecialchars($line["last_error"]);
1537 $last_updated = make_local_datetime($line['last_updated'], false);
1539 $has_img = feed_has_icon($id);
1541 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1544 $cv = array("id" => $id,
1545 "updated" => $last_updated,
1546 "counter" => (int) $count,
1547 "has_img" => (int) $has_img);
1550 $cv["error"] = $last_error;
1552 // if (get_pref('EXTENDED_FEEDLIST'))
1553 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1555 if ($active_feed && $id == $active_feed)
1556 $cv["title"] = truncate_string($line["title"], 30);
1558 array_push($ret_arr, $cv);
1565 function get_pgsql_version() {
1566 $result = db_query("SELECT version() AS version");
1567 $version = explode(" ", db_fetch_result($result, 0, "version"));
1572 * @return array (code => Status code, message => error message if available)
1574 * 0 - OK, Feed already exists
1575 * 1 - OK, Feed added
1577 * 3 - URL content is HTML, no feeds available
1578 * 4 - URL content is HTML which contains multiple feeds.
1579 * Here you should call extractfeedurls in rpc-backend
1580 * to get all possible feeds.
1581 * 5 - Couldn't download the URL content.
1582 * 6 - Content is an invalid XML.
1584 function subscribe_to_feed($url, $cat_id = 0,
1585 $auth_login = '', $auth_pass = '') {
1587 global $fetch_last_error;
1589 require_once "include/rssfuncs.php";
1591 $url = fix_url($url);
1593 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1595 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1598 return array("code" => 5, "message" => $fetch_last_error);
1601 if (is_html($contents)) {
1602 $feedUrls = get_feeds_from_html($url, $contents);
1604 if (count($feedUrls) == 0) {
1605 return array("code" => 3);
1606 } else if (count($feedUrls) > 1) {
1607 return array("code" => 4, "feeds" => $feedUrls);
1609 //use feed url as new URL
1610 $url = key($feedUrls);
1613 /* libxml_use_internal_errors(true);
1614 $doc = new DOMDocument();
1615 $doc->loadXML($contents);
1616 $error = libxml_get_last_error();
1617 libxml_clear_errors();
1620 $error_message = format_libxml_error($error);
1622 return array("code" => 6, "message" => $error_message);
1625 if ($cat_id == "0" ||
!$cat_id) {
1626 $cat_qpart = "NULL";
1628 $cat_qpart = "'$cat_id'";
1632 "SELECT id FROM ttrss_feeds
1633 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1635 if (strlen(FEED_CRYPT_KEY
) > 0) {
1636 require_once "crypt.php";
1637 $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
1638 $auth_pass_encrypted = 'true';
1640 $auth_pass_encrypted = 'false';
1643 $auth_pass = db_escape_string($auth_pass);
1645 if (db_num_rows($result) == 0) {
1647 "INSERT INTO ttrss_feeds
1648 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
1649 VALUES ('".$_SESSION["uid"]."', '$url',
1650 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, $auth_pass_encrypted)");
1653 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1654 AND owner_uid = " . $_SESSION["uid"]);
1656 $feed_id = db_fetch_result($result, 0, "id");
1659 update_rss_feed($feed_id, true);
1662 return array("code" => 1);
1664 return array("code" => 0);
1668 function print_feed_select($id, $default_id = "",
1669 $attributes = "", $include_all_feeds = true,
1670 $root_id = false, $nest_level = 0) {
1673 print "<select id=\"$id\" name=\"$id\" $attributes>";
1674 if ($include_all_feeds) {
1675 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1676 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1680 if (get_pref('ENABLE_FEED_CATS')) {
1683 $parent_qpart = "parent_cat = '$root_id'";
1685 $parent_qpart = "parent_cat IS NULL";
1687 $result = db_query("SELECT id,title,
1688 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1689 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1690 FROM ttrss_feed_categories
1691 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1693 while ($line = db_fetch_assoc($result)) {
1695 for ($i = 0; $i < $nest_level; $i++
)
1696 $line["title"] = " - " . $line["title"];
1698 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1700 printf("<option $is_selected value='CAT:%d'>%s</option>",
1701 $line["id"], htmlspecialchars($line["title"]));
1703 if ($line["num_children"] > 0)
1704 print_feed_select($id, $default_id, $attributes,
1705 $include_all_feeds, $line["id"], $nest_level+
1);
1707 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1708 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1710 while ($fline = db_fetch_assoc($feed_result)) {
1711 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1713 $fline["title"] = " + " . $fline["title"];
1715 for ($i = 0; $i < $nest_level; $i++
)
1716 $fline["title"] = " - " . $fline["title"];
1718 printf("<option $is_selected value='%d'>%s</option>",
1719 $fline["id"], htmlspecialchars($fline["title"]));
1724 $is_selected = ($default_id == "CAT:0") ?
"selected=\"1\"" : "";
1726 printf("<option $is_selected value='CAT:0'>%s</option>",
1727 __("Uncategorized"));
1729 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1730 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1732 while ($fline = db_fetch_assoc($feed_result)) {
1733 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1735 $fline["title"] = " + " . $fline["title"];
1737 for ($i = 0; $i < $nest_level; $i++
)
1738 $fline["title"] = " - " . $fline["title"];
1740 printf("<option $is_selected value='%d'>%s</option>",
1741 $fline["id"], htmlspecialchars($fline["title"]));
1746 $result = db_query("SELECT id,title FROM ttrss_feeds
1747 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1749 while ($line = db_fetch_assoc($result)) {
1751 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1753 printf("<option $is_selected value='%d'>%s</option>",
1754 $line["id"], htmlspecialchars($line["title"]));
1763 function print_feed_cat_select($id, $default_id,
1764 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1767 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1771 $parent_qpart = "parent_cat = '$root_id'";
1773 $parent_qpart = "parent_cat IS NULL";
1775 $result = db_query("SELECT id,title,
1776 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1777 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1778 FROM ttrss_feed_categories
1779 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1781 while ($line = db_fetch_assoc($result)) {
1782 if ($line["id"] == $default_id) {
1783 $is_selected = "selected=\"1\"";
1788 for ($i = 0; $i < $nest_level; $i++
)
1789 $line["title"] = " - " . $line["title"];
1792 printf("<option $is_selected value='%d'>%s</option>",
1793 $line["id"], htmlspecialchars($line["title"]));
1795 if ($line["num_children"] > 0)
1796 print_feed_cat_select($id, $default_id, $attributes,
1797 $include_all_cats, $line["id"], $nest_level+
1);
1801 if ($include_all_cats) {
1802 if (db_num_rows($result) > 0) {
1803 print "<option disabled=\"1\">--------</option>";
1806 if ($default_id == 0) {
1807 $is_selected = "selected=\"1\"";
1812 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1818 function checkbox_to_sql_bool($val) {
1819 return ($val == "on") ?
"true" : "false";
1822 function getFeedCatTitle($id) {
1824 return __("Special");
1825 } else if ($id < LABEL_BASE_INDEX
) {
1826 return __("Labels");
1827 } else if ($id > 0) {
1828 $result = db_query("SELECT ttrss_feed_categories.title
1829 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1830 cat_id = ttrss_feed_categories.id");
1831 if (db_num_rows($result) == 1) {
1832 return db_fetch_result($result, 0, "title");
1834 return __("Uncategorized");
1837 return "getFeedCatTitle($id) failed";
1842 function getFeedIcon($id) {
1845 return "images/archive.png";
1848 return "images/mark_set.svg";
1851 return "images/pub_set.svg";
1854 return "images/fresh.png";
1857 return "images/tag.png";
1860 return "images/recently_read.png";
1863 if ($id < LABEL_BASE_INDEX
) {
1864 return "images/label.png";
1866 if (file_exists(ICONS_DIR
. "/$id.ico"))
1867 return ICONS_URL
. "/$id.ico";
1875 function getFeedTitle($id, $cat = false) {
1877 return getCategoryTitle($id);
1878 } else if ($id == -1) {
1879 return __("Starred articles");
1880 } else if ($id == -2) {
1881 return __("Published articles");
1882 } else if ($id == -3) {
1883 return __("Fresh articles");
1884 } else if ($id == -4) {
1885 return __("All articles");
1886 } else if ($id === 0 ||
$id === "0") {
1887 return __("Archived articles");
1888 } else if ($id == -6) {
1889 return __("Recently read");
1890 } else if ($id < LABEL_BASE_INDEX
) {
1891 $label_id = feed_to_label_id($id);
1892 $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1893 if (db_num_rows($result) == 1) {
1894 return db_fetch_result($result, 0, "caption");
1896 return "Unknown label ($label_id)";
1899 } else if (is_numeric($id) && $id > 0) {
1900 $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'");
1901 if (db_num_rows($result) == 1) {
1902 return db_fetch_result($result, 0, "title");
1904 return "Unknown feed ($id)";
1911 function make_init_params() {
1914 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1915 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1916 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
1917 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1919 $params[strtolower($param)] = (int) get_pref($param);
1922 $params["icons_url"] = ICONS_URL
;
1923 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1924 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
1925 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
1926 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
1927 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1928 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1930 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1931 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1933 $max_feed_id = db_fetch_result($result, 0, "mid");
1934 $num_feeds = db_fetch_result($result, 0, "nf");
1936 $params["max_feed_id"] = (int) $max_feed_id;
1937 $params["num_feeds"] = (int) $num_feeds;
1939 $params["collapsed_feedlist"] = (int) get_pref("_COLLAPSED_FEEDLIST");
1940 $params["hotkeys"] = get_hotkeys_map();
1942 $params["csrf_token"] = $_SESSION["csrf_token"];
1943 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1945 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1950 function get_hotkeys_info() {
1952 __("Navigation") => array(
1953 "next_feed" => __("Open next feed"),
1954 "prev_feed" => __("Open previous feed"),
1955 "next_article" => __("Open next article"),
1956 "prev_article" => __("Open previous article"),
1957 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1958 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1959 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
1960 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
1961 "search_dialog" => __("Show search dialog")),
1962 __("Article") => array(
1963 "toggle_mark" => __("Toggle starred"),
1964 "toggle_publ" => __("Toggle published"),
1965 "toggle_unread" => __("Toggle unread"),
1966 "edit_tags" => __("Edit tags"),
1967 "dismiss_selected" => __("Dismiss selected"),
1968 "dismiss_read" => __("Dismiss read"),
1969 "open_in_new_window" => __("Open in new window"),
1970 "catchup_below" => __("Mark below as read"),
1971 "catchup_above" => __("Mark above as read"),
1972 "article_scroll_down" => __("Scroll down"),
1973 "article_scroll_up" => __("Scroll up"),
1974 "select_article_cursor" => __("Select article under cursor"),
1975 "email_article" => __("Email article"),
1976 "close_article" => __("Close/collapse article"),
1977 "toggle_expand" => __("Toggle article expansion (combined mode)"),
1978 "toggle_widescreen" => __("Toggle widescreen mode"),
1979 "toggle_embed_original" => __("Toggle embed original")),
1980 __("Article selection") => array(
1981 "select_all" => __("Select all articles"),
1982 "select_unread" => __("Select unread"),
1983 "select_marked" => __("Select starred"),
1984 "select_published" => __("Select published"),
1985 "select_invert" => __("Invert selection"),
1986 "select_none" => __("Deselect everything")),
1987 __("Feed") => array(
1988 "feed_refresh" => __("Refresh current feed"),
1989 "feed_unhide_read" => __("Un/hide read feeds"),
1990 "feed_subscribe" => __("Subscribe to feed"),
1991 "feed_edit" => __("Edit feed"),
1992 "feed_catchup" => __("Mark as read"),
1993 "feed_reverse" => __("Reverse headlines"),
1994 "feed_debug_update" => __("Debug feed update"),
1995 "catchup_all" => __("Mark all feeds as read"),
1996 "cat_toggle_collapse" => __("Un/collapse current category"),
1997 "toggle_combined_mode" => __("Toggle combined mode"),
1998 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
1999 __("Go to") => array(
2000 "goto_all" => __("All articles"),
2001 "goto_fresh" => __("Fresh"),
2002 "goto_marked" => __("Starred"),
2003 "goto_published" => __("Published"),
2004 "goto_tagcloud" => __("Tag cloud"),
2005 "goto_prefs" => __("Preferences")),
2006 __("Other") => array(
2007 "create_label" => __("Create label"),
2008 "create_filter" => __("Create filter"),
2009 "collapse_sidebar" => __("Un/collapse sidebar"),
2010 "help_dialog" => __("Show help dialog"))
2013 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_INFO
) as $plugin) {
2014 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
2020 function get_hotkeys_map() {
2022 // "navigation" => array(
2025 "n" => "next_article",
2026 "p" => "prev_article",
2027 "(38)|up" => "prev_article",
2028 "(40)|down" => "next_article",
2029 // "^(38)|Ctrl-up" => "prev_article_noscroll",
2030 // "^(40)|Ctrl-down" => "next_article_noscroll",
2031 "(191)|/" => "search_dialog",
2032 // "article" => array(
2033 "s" => "toggle_mark",
2034 "*s" => "toggle_publ",
2035 "u" => "toggle_unread",
2036 "*t" => "edit_tags",
2037 "*d" => "dismiss_selected",
2038 "*x" => "dismiss_read",
2039 "o" => "open_in_new_window",
2040 "c p" => "catchup_below",
2041 "c n" => "catchup_above",
2042 "*n" => "article_scroll_down",
2043 "*p" => "article_scroll_up",
2044 "*(38)|Shift+up" => "article_scroll_up",
2045 "*(40)|Shift+down" => "article_scroll_down",
2046 "a *w" => "toggle_widescreen",
2047 "a e" => "toggle_embed_original",
2048 "e" => "email_article",
2049 "a q" => "close_article",
2050 // "article_selection" => array(
2051 "a a" => "select_all",
2052 "a u" => "select_unread",
2053 "a *u" => "select_marked",
2054 "a p" => "select_published",
2055 "a i" => "select_invert",
2056 "a n" => "select_none",
2058 "f r" => "feed_refresh",
2059 "f a" => "feed_unhide_read",
2060 "f s" => "feed_subscribe",
2061 "f e" => "feed_edit",
2062 "f q" => "feed_catchup",
2063 "f x" => "feed_reverse",
2064 "f *d" => "feed_debug_update",
2065 "f *c" => "toggle_combined_mode",
2066 "f c" => "toggle_cdm_expanded",
2067 "*q" => "catchup_all",
2068 "x" => "cat_toggle_collapse",
2070 "g a" => "goto_all",
2071 "g f" => "goto_fresh",
2072 "g s" => "goto_marked",
2073 "g p" => "goto_published",
2074 "g t" => "goto_tagcloud",
2075 "g *p" => "goto_prefs",
2076 // "other" => array(
2077 "(9)|Tab" => "select_article_cursor", // tab
2078 "c l" => "create_label",
2079 "c f" => "create_filter",
2080 "c s" => "collapse_sidebar",
2081 "^(191)|Ctrl+/" => "help_dialog",
2084 if (get_pref('COMBINED_DISPLAY_MODE')) {
2085 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2086 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2089 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_MAP
) as $plugin) {
2090 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2093 $prefixes = array();
2095 foreach (array_keys($hotkeys) as $hotkey) {
2096 $pair = explode(" ", $hotkey, 2);
2098 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2099 array_push($prefixes, $pair[0]);
2103 return array($prefixes, $hotkeys);
2106 function make_runtime_info() {
2109 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2110 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2112 $max_feed_id = db_fetch_result($result, 0, "mid");
2113 $num_feeds = db_fetch_result($result, 0, "nf");
2115 $data["max_feed_id"] = (int) $max_feed_id;
2116 $data["num_feeds"] = (int) $num_feeds;
2118 $data['last_article_id'] = getLastArticleId();
2119 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
2121 $data['dep_ts'] = calculate_dep_timestamp();
2122 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2124 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2126 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2128 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2130 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2133 $stamp_delta = time() - $stamp;
2135 if ($stamp_delta > 1800) {
2139 $_SESSION["daemon_stamp_check"] = time();
2142 $data['daemon_stamp_ok'] = $stamp_check;
2144 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2146 $data['daemon_stamp'] = $stamp_fmt;
2151 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2152 $new_version_details = @check_for_update
();
2154 $data['new_version_available'] = (int) ($new_version_details != false);
2156 $_SESSION["last_version_check"] = time();
2157 $_SESSION["version_data"] = $new_version_details;
2163 function search_to_sql($search) {
2165 $search_query_part = "";
2167 $keywords = explode(" ", $search);
2168 $query_keywords = array();
2170 foreach ($keywords as $k) {
2171 if (strpos($k, "-") === 0) {
2178 $commandpair = explode(":", mb_strtolower($k), 2);
2180 switch ($commandpair[0]) {
2182 if ($commandpair[1]) {
2183 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
2184 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2186 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2187 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2191 if ($commandpair[1]) {
2192 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
2193 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2195 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2196 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2200 if ($commandpair[1]) {
2201 if ($commandpair[1] == "true")
2202 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2203 else if ($commandpair[1] == "false")
2204 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2206 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
2207 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2209 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2210 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2215 if ($commandpair[1]) {
2216 if ($commandpair[1] == "true")
2217 array_push($query_keywords, "($not (marked = true))");
2219 array_push($query_keywords, "($not (marked = false))");
2221 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2222 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2226 if ($commandpair[1]) {
2227 if ($commandpair[1] == "true")
2228 array_push($query_keywords, "($not (published = true))");
2230 array_push($query_keywords, "($not (published = false))");
2233 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2234 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2238 if (strpos($k, "@") === 0) {
2240 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
2241 $orig_ts = strtotime(substr($k, 1));
2242 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2244 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2246 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2248 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2249 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2254 $search_query_part = implode("AND", $query_keywords);
2256 return $search_query_part;
2259 function getParentCategories($cat, $owner_uid) {
2262 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
2263 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2265 while ($line = db_fetch_assoc($result)) {
2266 array_push($rv, $line["parent_cat"]);
2267 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
2273 function getChildCategories($cat, $owner_uid) {
2276 $result = db_query("SELECT id FROM ttrss_feed_categories
2277 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2279 while ($line = db_fetch_assoc($result)) {
2280 array_push($rv, $line["id"]);
2281 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
2287 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) {
2289 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2291 $ext_tables_part = "";
2295 if (SPHINX_ENABLED
) {
2296 $ids = join(",", @sphinx_search
($search, 0, 500));
2299 $search_query_part = "ref_id IN ($ids) AND ";
2301 $search_query_part = "ref_id = -1 AND ";
2304 $search_query_part = search_to_sql($search);
2305 $search_query_part .= " AND ";
2309 $search_query_part = "";
2314 if (DB_TYPE
== "pgsql") {
2315 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2317 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2320 $override_order = "updated DESC";
2322 $filter_query_part = filter_to_sql($filter, $owner_uid);
2324 // Try to check if SQL regexp implementation chokes on a valid regexp
2325 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
2326 ttrss_user_entries, ttrss_feeds, ttrss_feed_categories
2327 WHERE $filter_query_part LIMIT 1", false);
2330 $test = db_fetch_result($result, 0, "true_val");
2333 $filter_query_part = "false AND";
2335 $filter_query_part .= " AND";
2338 $filter_query_part = "false AND";
2342 $filter_query_part = "";
2346 $since_id_part = "ttrss_entries.id > $since_id AND ";
2348 $since_id_part = "";
2351 $view_query_part = "";
2353 if ($view_mode == "adaptive") {
2355 $view_query_part = " ";
2356 } else if ($feed != -1) {
2358 $unread = getFeedUnread($feed, $cat_view);
2360 if ($cat_view && $feed > 0 && $include_children)
2361 $unread +
= getCategoryChildrenUnread($feed);
2364 $view_query_part = " unread = true AND ";
2369 if ($view_mode == "marked") {
2370 $view_query_part = " marked = true AND ";
2373 if ($view_mode == "has_note") {
2374 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2377 if ($view_mode == "published") {
2378 $view_query_part = " published = true AND ";
2381 if ($view_mode == "unread" && $feed != -6) {
2382 $view_query_part = " unread = true AND ";
2386 $limit_query_part = "LIMIT " . $limit;
2389 $allow_archived = false;
2391 $vfeed_query_part = "";
2393 // override query strategy and enable feed display when searching globally
2394 if ($search && $search_mode == "all_feeds") {
2395 $query_strategy_part = "true";
2396 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2398 } else if (!is_numeric($feed)) {
2399 $query_strategy_part = "true";
2400 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2401 id = feed_id) as feed_title,";
2402 } else if ($search && $search_mode == "this_cat") {
2403 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2406 if ($include_children) {
2407 $subcats = getChildCategories($feed, $owner_uid);
2408 array_push($subcats, $feed);
2409 $cats_qpart = join(",", $subcats);
2411 $cats_qpart = $feed;
2414 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2417 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2420 } else if ($feed > 0) {
2425 if ($include_children) {
2427 $subcats = getChildCategories($feed, $owner_uid);
2429 array_push($subcats, $feed);
2430 $query_strategy_part = "cat_id IN (".
2431 implode(",", $subcats).")";
2434 $query_strategy_part = "cat_id = '$feed'";
2438 $query_strategy_part = "cat_id IS NULL";
2441 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2444 $query_strategy_part = "feed_id = '$feed'";
2446 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2447 $query_strategy_part = "feed_id IS NULL";
2448 $allow_archived = true;
2449 } else if ($feed == 0 && $cat_view) { // uncategorized
2450 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2451 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2452 } else if ($feed == -1) { // starred virtual feed
2453 $query_strategy_part = "marked = true";
2454 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2455 $allow_archived = true;
2457 if (!$override_order) {
2458 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2461 } else if ($feed == -2) { // published virtual feed OR labels category
2464 $query_strategy_part = "published = true";
2465 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2466 $allow_archived = true;
2468 if (!$override_order) {
2469 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2473 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2475 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2477 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2478 ttrss_user_labels2.article_id = ref_id";
2481 } else if ($feed == -6) { // recently read
2482 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2483 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2484 $allow_archived = true;
2486 if (!$override_order) $override_order = "last_read DESC";
2487 } else if ($feed == -3) { // fresh virtual feed
2488 $query_strategy_part = "unread = true AND score >= 0";
2490 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
2492 if (DB_TYPE
== "pgsql") {
2493 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2495 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2498 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2499 } else if ($feed == -4) { // all articles virtual feed
2500 $allow_archived = true;
2501 $query_strategy_part = "true";
2502 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2503 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2504 $label_id = feed_to_label_id($feed);
2506 $query_strategy_part = "label_id = '$label_id' AND
2507 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2508 ttrss_user_labels2.article_id = ref_id";
2510 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2511 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2512 $allow_archived = true;
2515 $query_strategy_part = "true";
2518 $order_by = "score DESC, date_entered DESC, updated DESC";
2520 if ($view_mode == "unread_first") {
2521 $order_by = "unread DESC, $order_by";
2524 if ($override_order) {
2525 $order_by = $override_order;
2531 $feed_title = T_sprintf("Search results: %s", $search);
2534 $feed_title = getCategoryTitle($feed);
2536 if (is_numeric($feed) && $feed > 0) {
2537 $result = db_query("SELECT title,site_url,last_error,last_updated
2538 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2540 $feed_title = db_fetch_result($result, 0, "title");
2541 $feed_site_url = db_fetch_result($result, 0, "site_url");
2542 $last_error = db_fetch_result($result, 0, "last_error");
2543 $last_updated = db_fetch_result($result, 0, "last_updated");
2545 $feed_title = getFeedTitle($feed);
2550 $content_query_part = "content as content_preview, cached_content, ";
2552 if (is_numeric($feed)) {
2555 $feed_kind = "Feeds";
2557 $feed_kind = "Labels";
2560 if ($limit_query_part) {
2561 $offset_query_part = "OFFSET $offset";
2564 // proper override_order applied above
2565 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
2566 if (!$override_order) {
2567 $order_by = "ttrss_feeds.title, $order_by";
2569 $order_by = "ttrss_feeds.title, $override_order";
2573 if (!$allow_archived) {
2574 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2575 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2578 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2579 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2582 if ($vfeed_query_part)
2583 $vfeed_query_part .= "favicon_avg_color,";
2585 $query = "SELECT DISTINCT
2588 ttrss_entries.id,ttrss_entries.title,
2592 always_display_enclosures,
2599 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2600 last_marked, last_published,
2608 ttrss_user_entries.ref_id = ttrss_entries.id AND
2609 ttrss_user_entries.owner_uid = '$owner_uid' AND
2614 $query_strategy_part ORDER BY $order_by
2615 $limit_query_part $offset_query_part";
2617 if ($_REQUEST["debug"]) print $query;
2619 $result = db_query($query);
2624 $select_qpart = "SELECT DISTINCT " .
2628 "ttrss_entries.id as id," .
2641 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2642 "last_marked, last_published, " .
2645 $content_query_part .
2648 $feed_kind = "Tags";
2649 $all_tags = explode(",", $feed);
2650 if ($search_mode == 'any') {
2651 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2652 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2653 $where_qpart = " WHERE " .
2654 "ref_id = ttrss_entries.id AND " .
2655 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2656 "post_int_id = int_id AND $tag_sql AND " .
2658 $search_query_part .
2659 $query_strategy_part . " ORDER BY $order_by " .
2664 $sub_selects = array();
2665 $sub_ands = array();
2666 foreach ($all_tags as $term) {
2667 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");
2674 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2679 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2680 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2681 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2682 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2684 // error_log("TAG SQL: " . $tag_sql);
2685 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2687 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2688 $result = db_query($select_qpart . $from_qpart . $where_qpart);
2691 return array($result, $feed_title, $feed_site_url, $last_error, $last_updated);
2695 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false) {
2696 if (!$owner) $owner = $_SESSION["uid"];
2698 $res = trim($str); if (!$res) return '';
2700 if (strpos($res, "href=") === false)
2701 $res = rewrite_urls($res);
2703 $charset_hack = '<head>
2704 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2707 $res = trim($res); if (!$res) return '';
2709 libxml_use_internal_errors(true);
2711 $doc = new DOMDocument();
2712 $doc->loadHTML($charset_hack . $res);
2713 $xpath = new DOMXPath($doc);
2715 $entries = $xpath->query('(//a[@href]|//img[@src])');
2717 foreach ($entries as $entry) {
2721 if ($entry->hasAttribute('href'))
2722 $entry->setAttribute('href',
2723 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2725 if ($entry->hasAttribute('src')) {
2726 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2728 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2730 if (file_exists($cached_filename)) {
2731 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2734 $entry->setAttribute('src', $src);
2737 if ($entry->nodeName
== 'img') {
2738 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
2739 $force_remove_images ||
$_SESSION["bw_limit"]) {
2741 $p = $doc->createElement('p');
2743 $a = $doc->createElement('a');
2744 $a->setAttribute('href', $entry->getAttribute('src'));
2746 $a->appendChild(new DOMText($entry->getAttribute('src')));
2747 $a->setAttribute('target', '_blank');
2749 $p->appendChild($a);
2751 $entry->parentNode
->replaceChild($p, $entry);
2756 if (strtolower($entry->nodeName
) == "a") {
2757 $entry->setAttribute("target", "_blank");
2761 $entries = $xpath->query('//iframe');
2762 foreach ($entries as $entry) {
2763 $entry->setAttribute('sandbox', 'allow-scripts');
2767 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
2768 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
2769 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
2770 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
2771 'dt', 'em', 'footer', 'figure', 'figcaption',
2772 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
2773 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
2774 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
2775 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2776 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
2777 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2779 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2781 $disallowed_attributes = array('id', 'style', 'class');
2783 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_SANITIZE
) as $plugin) {
2784 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2785 if (is_array($retval)) {
2787 $allowed_elements = $retval[1];
2788 $disallowed_attributes = $retval[2];
2794 $doc->removeChild($doc->firstChild
); //remove doctype
2795 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2796 $res = $doc->saveHTML();
2800 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2801 $xpath = new DOMXPath($doc);
2802 $entries = $xpath->query('//*');
2804 foreach ($entries as $entry) {
2805 if (!in_array($entry->nodeName
, $allowed_elements)) {
2806 $entry->parentNode
->removeChild($entry);
2809 if ($entry->hasAttributes()) {
2810 $attrs_to_remove = array();
2812 foreach ($entry->attributes
as $attr) {
2814 if (strpos($attr->nodeName
, 'on') === 0) {
2815 array_push($attrs_to_remove, $attr);
2818 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2819 array_push($attrs_to_remove, $attr);
2823 foreach ($attrs_to_remove as $attr) {
2824 $entry->removeAttributeNode($attr);
2832 function check_for_update() {
2833 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2834 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2835 "&iid=" . sha1(SELF_URL_PATH
);
2837 $version_data = @fetch_file_contents
($version_url);
2839 if ($version_data) {
2840 $version_data = json_decode($version_data, true);
2841 if ($version_data && $version_data['version']) {
2843 if (version_compare(VERSION
, $version_data['version']) == -1) {
2844 return $version_data;
2852 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
2854 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2855 if (count($ids) == 0) return;
2859 foreach ($ids as $id) {
2860 array_push($tmp_ids, "ref_id = '$id'");
2863 $ids_qpart = join(" OR ", $tmp_ids);
2866 db_query("UPDATE ttrss_user_entries SET
2867 unread = false,last_read = NOW()
2868 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2869 } else if ($cmode == 1) {
2870 db_query("UPDATE ttrss_user_entries SET
2872 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2874 db_query("UPDATE ttrss_user_entries SET
2875 unread = NOT unread,last_read = NOW()
2876 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2881 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
2882 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2884 while ($line = db_fetch_assoc($result)) {
2885 ccache_update($line["feed_id"], $owner_uid);
2889 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
2891 $a_id = db_escape_string($id);
2893 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2895 $query = "SELECT DISTINCT tag_name,
2896 owner_uid as owner FROM
2897 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2898 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2900 $obj_id = md5("TAGS:$owner_uid:$id");
2903 /* check cache first */
2905 if ($tag_cache === false) {
2906 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
2907 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2909 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2913 $tags = explode(",", $tag_cache);
2916 /* do it the hard way */
2918 $tmp_result = db_query($query);
2920 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2921 array_push($tags, $tmp_line["tag_name"]);
2924 /* update the cache */
2926 $tags_str = db_escape_string(join(",", $tags));
2928 db_query("UPDATE ttrss_user_entries
2929 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2930 AND owner_uid = $owner_uid");
2936 function trim_array($array) {
2938 array_walk($tmp, 'trim');
2942 function tag_is_valid($tag) {
2943 if ($tag == '') return false;
2944 if (preg_match("/^[0-9]*$/", $tag)) return false;
2945 if (mb_strlen($tag) > 250) return false;
2947 if (function_exists('iconv')) {
2948 $tag = iconv("utf-8", "utf-8", $tag);
2951 if (!$tag) return false;
2956 function render_login_form() {
2957 header('Cache-Control: public');
2959 require_once "login_form.php";
2963 function format_warning($msg, $id = "") {
2965 return "<div class=\"warning\" id=\"$id\">
2966 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2969 function format_notice($msg, $id = "") {
2971 return "<div class=\"notice\" id=\"$id\">
2972 <span><img src=\"images/sign_info.svg\"></span><span>$msg</span></div>";
2975 function format_error($msg, $id = "") {
2977 return "<div class=\"error\" id=\"$id\">
2978 <span><img src=\"images/sign_excl.svg\"></span><span>$msg</span></div>";
2981 function print_notice($msg) {
2982 return print format_notice($msg);
2985 function print_warning($msg) {
2986 return print format_warning($msg);
2989 function print_error($msg) {
2990 return print format_error($msg);
2994 function T_sprintf() {
2995 $args = func_get_args();
2996 return vsprintf(__(array_shift($args)), $args);
2999 function format_inline_player($url, $ctype) {
3003 $url = htmlspecialchars($url);
3005 if (strpos($ctype, "audio/") === 0) {
3007 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
3008 $_SESSION["hasMp3"])) {
3010 $entry .= "<audio controls>
3011 <source type=\"$ctype\" src=\"$url\"></source>
3016 $entry .= "<object type=\"application/x-shockwave-flash\"
3017 data=\"lib/button/musicplayer.swf?song_url=$url\"
3018 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
3019 <param name=\"movie\"
3020 value=\"lib/button/musicplayer.swf?song_url=$url\" />
3024 if ($entry) $entry .= " <a target=\"_blank\"
3025 href=\"$url\">" . basename($url) . "</a>";
3033 /* $filename = substr($url, strrpos($url, "/")+1);
3035 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3036 $filename . " (" . $ctype . ")" . "</a>"; */
3040 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
3041 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3047 /* we can figure out feed_id from article id anyway, why do we
3048 * pass feed_id here? let's ignore the argument :(*/
3050 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3051 WHERE ref_id = '$id'");
3053 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
3055 $rv['feed_id'] = $feed_id;
3057 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3059 if ($mark_as_read) {
3060 $result = db_query("UPDATE ttrss_user_entries
3061 SET unread = false,last_read = NOW()
3062 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
3064 ccache_update($feed_id, $owner_uid);
3067 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,
3068 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
3069 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
3070 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
3071 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
3078 FROM ttrss_entries,ttrss_user_entries
3079 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3083 $line = db_fetch_assoc($result);
3085 $tag_cache = $line["tag_cache"];
3087 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
3088 unset($line["tag_cache"]);
3090 $line["content"] = sanitize($line["content"], false, $owner_uid, $line["site_url"]);
3092 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_RENDER_ARTICLE
) as $p) {
3093 $line = $p->hook_render_article($line);
3096 $num_comments = $line["num_comments"];
3097 $entry_comments = "";
3099 if ($num_comments > 0) {
3100 if ($line["comments"]) {
3101 $comments_url = htmlspecialchars($line["comments"]);
3103 $comments_url = htmlspecialchars($line["link"]);
3105 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3107 if ($line["comments"] && $line["link"] != $line["comments"]) {
3108 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3113 header("Content-Type: text/html");
3114 $rv['content'] .= "<html><head>
3115 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3116 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3117 <link rel=\"stylesheet\" type=\"text/css\" href=\"tt-rss.css\">
3118 </head><body id=\"ttrssZoom\">";
3121 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3123 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3125 $entry_author = $line["author"];
3127 if ($entry_author) {
3128 $entry_author = __(" - ") . $entry_author;
3131 $parsed_updated = make_local_datetime($line["updated"], true,
3134 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3136 if ($line["link"]) {
3137 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3138 title=\"".htmlspecialchars($line['title'])."\"
3140 htmlspecialchars($line["link"]) . "\">" .
3141 $line["title"] . "</a>" .
3142 "<span class='author'>$entry_author</span></div>";
3144 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3147 $tags_str = format_tags_string($line["tags"], $id);
3148 $tags_str_full = join(", ", $line["tags"]);
3150 if (!$tags_str_full) $tags_str_full = __("no tags");
3152 if (!$entry_comments) $entry_comments = " "; # placeholder
3154 $rv['content'] .= "<div class='postTags' style='float : right'>
3155 <img src='images/tag.png'
3156 class='tagsPic' alt='Tags' title='Tags'> ";
3159 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3160 <a title=\"".__('Edit tags for this article')."\"
3161 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3163 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3164 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3165 position=\"below\">$tags_str_full</div>";
3167 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_BUTTON
) as $p) {
3168 $rv['content'] .= $p->hook_article_button($line);
3172 $tags_str = strip_tags($tags_str);
3173 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3175 $rv['content'] .= "</div>";
3176 $rv['content'] .= "<div clear='both'>";
3178 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_LEFT_BUTTON
) as $p) {
3179 $rv['content'] .= $p->hook_article_left_button($line);
3182 $rv['content'] .= "$entry_comments</div>";
3184 if ($line["orig_feed_id"]) {
3186 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
3187 WHERE id = ".$line["orig_feed_id"]);
3189 if (db_num_rows($tmp_result) != 0) {
3191 $rv['content'] .= "<div clear='both'>";
3192 $rv['content'] .= __("Originally from:");
3194 $rv['content'] .= " ";
3196 $tmp_line = db_fetch_assoc($tmp_result);
3198 $rv['content'] .= "<a target='_blank'
3199 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3200 $tmp_line['title'] . "</a>";
3202 $rv['content'] .= " ";
3204 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3205 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3207 $rv['content'] .= "</div>";
3211 $rv['content'] .= "</div>";
3213 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3214 if ($line['note']) {
3215 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3217 $rv['content'] .= "</div>";
3219 $rv['content'] .= "<div class=\"postContent\">";
3221 $rv['content'] .= $line["content"];
3222 $rv['content'] .= format_article_enclosures($id,
3223 sql_bool_to_bool($line["always_display_enclosures"]),
3225 sql_bool_to_bool($line["hide_images"]));
3227 $rv['content'] .= "</div>";
3229 $rv['content'] .= "</div>";
3235 <div class='footer'>
3236 <button onclick=\"return window.close()\">".
3237 __("Close this window")."</button></div>";
3238 $rv['content'] .= "</body></html>";
3245 function print_checkpoint($n, $s) {
3246 $ts = microtime(true);
3247 echo sprintf("<!-- CP[$n] %.4f seconds -->", $ts - $s);
3251 function sanitize_tag($tag) {
3254 $tag = mb_strtolower($tag, 'utf-8');
3256 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3258 // $tag = str_replace('"', "", $tag);
3259 // $tag = str_replace("+", " ", $tag);
3260 $tag = str_replace("technorati tag: ", "", $tag);
3265 function get_self_url_prefix() {
3266 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3267 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3269 return SELF_URL_PATH
;
3274 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3276 * @return string The Mozilla Firefox feed adding URL.
3278 function add_feed_url() {
3279 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3281 $url_path = get_self_url_prefix() .
3282 "/public.php?op=subscribe&feed_url=%s";
3284 } // function add_feed_url
3286 function encrypt_password($pass, $salt = '', $mode2 = false) {
3287 if ($salt && $mode2) {
3288 return "MODE2:" . hash('sha256', $salt . $pass);
3290 return "SHA1X:" . sha1("$salt:$pass");
3292 return "SHA1:" . sha1($pass);
3294 } // function encrypt_password
3296 function load_filters($feed_id, $owner_uid, $action_id = false) {
3299 $cat_id = (int)getFeedCategory($feed_id);
3301 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
3302 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3304 $check_cats = join(",", array_merge(
3305 getParentCategories($cat_id, $owner_uid),
3308 while ($line = db_fetch_assoc($result)) {
3309 $filter_id = $line["id"];
3311 $result2 = db_query("SELECT
3312 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3313 FROM ttrss_filters2_rules AS r,
3314 ttrss_filter_types AS t
3316 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3317 (feed_id IS NULL OR feed_id = '$feed_id') AND
3318 filter_type = t.id AND filter_id = '$filter_id'");
3323 while ($rule_line = db_fetch_assoc($result2)) {
3324 # print_r($rule_line);
3327 $rule["reg_exp"] = $rule_line["reg_exp"];
3328 $rule["type"] = $rule_line["type_name"];
3329 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3331 array_push($rules, $rule);
3334 $result2 = db_query("SELECT a.action_param,t.name AS type_name
3335 FROM ttrss_filters2_actions AS a,
3336 ttrss_filter_actions AS t
3338 action_id = t.id AND filter_id = '$filter_id'");
3340 while ($action_line = db_fetch_assoc($result2)) {
3341 # print_r($action_line);
3344 $action["type"] = $action_line["type_name"];
3345 $action["param"] = $action_line["action_param"];
3347 array_push($actions, $action);
3352 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3353 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3354 $filter["rules"] = $rules;
3355 $filter["actions"] = $actions;
3357 if (count($rules) > 0 && count($actions) > 0) {
3358 array_push($filters, $filter);
3365 function get_score_pic($score) {
3367 return "score_high.png";
3368 } else if ($score > 0) {
3369 return "score_half_high.png";
3370 } else if ($score < -100) {
3371 return "score_low.png";
3372 } else if ($score < 0) {
3373 return "score_half_low.png";
3375 return "score_neutral.png";
3379 function feed_has_icon($id) {
3380 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3383 function init_plugins() {
3384 PluginHost
::getInstance()->load(PLUGINS
, PluginHost
::KIND_ALL
);
3389 function format_tags_string($tags, $id) {
3392 $tags_nolinks_str = "";
3398 $formatted_tags = array();
3400 foreach ($tags as $tag) {
3402 $tag_escaped = str_replace("'", "\\'", $tag);
3404 if (mb_strlen($tag) > 30) {
3405 $tag = truncate_string($tag, 30);
3408 $tag_str = "<a href=\"javascript:viewfeed('$tag_escaped')\">$tag</a>";
3410 array_push($formatted_tags, $tag_str);
3412 $tmp_tags_str = implode(", ", $formatted_tags);
3414 if ($num_tags == $tag_limit ||
mb_strlen($tmp_tags_str) > 150) {
3419 $tags_str = implode(", ", $formatted_tags);
3421 if ($num_tags < count($tags)) {
3422 $tags_str .= ", …";
3425 if ($num_tags == 0) {
3426 $tags_str = __("no tags");
3433 function format_article_labels($labels, $id) {
3435 if (is_array($labels)) return '';
3439 foreach ($labels as $l) {
3440 $labels_str .= sprintf("<span class='hlLabelRef'
3441 style='color : %s; background-color : %s'>%s</span>",
3442 $l[2], $l[3], $l[1]);
3449 function format_article_note($id, $note, $allow_edit = true) {
3451 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3452 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3453 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3459 function get_feed_category($feed_cat, $parent_cat_id = false) {
3460 if ($parent_cat_id) {
3461 $parent_qpart = "parent_cat = '$parent_cat_id'";
3462 $parent_insert = "'$parent_cat_id'";
3464 $parent_qpart = "parent_cat IS NULL";
3465 $parent_insert = "NULL";
3469 "SELECT id FROM ttrss_feed_categories
3470 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3472 if (db_num_rows($result) == 0) {
3475 return db_fetch_result($result, 0, "id");
3479 function add_feed_category($feed_cat, $parent_cat_id = false) {
3481 if (!$feed_cat) return false;
3485 if ($parent_cat_id) {
3486 $parent_qpart = "parent_cat = '$parent_cat_id'";
3487 $parent_insert = "'$parent_cat_id'";
3489 $parent_qpart = "parent_cat IS NULL";
3490 $parent_insert = "NULL";
3493 $feed_cat = mb_substr($feed_cat, 0, 250);
3496 "SELECT id FROM ttrss_feed_categories
3497 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3499 if (db_num_rows($result) == 0) {
3502 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3503 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3513 function getArticleFeed($id) {
3514 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3515 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3517 if (db_num_rows($result) != 0) {
3518 return db_fetch_result($result, 0, "feed_id");
3525 * Fixes incomplete URLs by prepending "http://".
3526 * Also replaces feed:// with http://, and
3527 * prepends a trailing slash if the url is a domain name only.
3529 * @param string $url Possibly incomplete URL
3531 * @return string Fixed URL.
3533 function fix_url($url) {
3534 if (strpos($url, '://') === false) {
3535 $url = 'http://' . $url;
3536 } else if (substr($url, 0, 5) == 'feed:') {
3537 $url = 'http:' . substr($url, 5);
3540 //prepend slash if the URL has no slash in it
3541 // "http://www.example" -> "http://www.example/"
3542 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3546 if ($url != "http:///")
3552 function validate_feed_url($url) {
3553 $parts = parse_url($url);
3555 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3559 function get_article_enclosures($id) {
3561 $query = "SELECT * FROM ttrss_enclosures
3562 WHERE post_id = '$id' AND content_url != ''";
3566 $result = db_query($query);
3568 if (db_num_rows($result) > 0) {
3569 while ($line = db_fetch_assoc($result)) {
3570 array_push($rv, $line);
3577 function save_email_address($email) {
3578 // FIXME: implement persistent storage of emails
3580 if (!$_SESSION['stored_emails'])
3581 $_SESSION['stored_emails'] = array();
3583 if (!in_array($email, $_SESSION['stored_emails']))
3584 array_push($_SESSION['stored_emails'], $email);
3588 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
3590 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3592 $sql_is_cat = bool_to_sql_bool($is_cat);
3594 $result = db_query("SELECT access_key FROM ttrss_access_keys
3595 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3596 AND owner_uid = " . $owner_uid);
3598 if (db_num_rows($result) == 1) {
3599 return db_fetch_result($result, 0, "access_key");
3601 $key = db_escape_string(sha1(uniqid(rand(), true)));
3603 $result = db_query("INSERT INTO ttrss_access_keys
3604 (access_key, feed_id, is_cat, owner_uid)
3605 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3612 function get_feeds_from_html($url, $content)
3614 $url = fix_url($url);
3615 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3617 libxml_use_internal_errors(true);
3619 $doc = new DOMDocument();
3620 $doc->loadHTML($content);
3621 $xpath = new DOMXPath($doc);
3622 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3623 $feedUrls = array();
3624 foreach ($entries as $entry) {
3625 if ($entry->hasAttribute('href')) {
3626 $title = $entry->getAttribute('title');
3628 $title = $entry->getAttribute('type');
3630 $feedUrl = rewrite_relative_url(
3631 $baseUrl, $entry->getAttribute('href')
3633 $feedUrls[$feedUrl] = $title;
3639 function is_html($content) {
3640 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3643 function url_is_html($url, $login = false, $pass = false) {
3644 return is_html(fetch_file_contents($url, false, $login, $pass));
3647 function print_label_select($name, $value, $attributes = "") {
3649 $result = db_query("SELECT caption FROM ttrss_labels2
3650 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3652 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3653 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3655 while ($line = db_fetch_assoc($result)) {
3657 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3659 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3660 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3664 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3671 function format_article_enclosures($id, $always_display_enclosures,
3672 $article_content, $hide_images = false) {
3674 $result = get_article_enclosures($id);
3677 if (count($result) > 0) {
3679 $entries_html = array();
3681 $entries_inline = array();
3683 foreach ($result as $line) {
3685 $url = $line["content_url"];
3686 $ctype = $line["content_type"];
3688 if (!$ctype) $ctype = __("unknown type");
3690 $filename = substr($url, strrpos($url, "/")+
1);
3692 $player = format_inline_player($url, $ctype);
3694 if ($player) array_push($entries_inline, $player);
3696 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3697 # $filename . " (" . $ctype . ")" . "</a>";
3699 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3700 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3702 array_push($entries_html, $entry);
3706 $entry["type"] = $ctype;
3707 $entry["filename"] = $filename;
3708 $entry["url"] = $url;
3710 array_push($entries, $entry);
3713 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3714 if ($always_display_enclosures ||
3715 !preg_match("/<img/i", $article_content)) {
3717 foreach ($entries as $entry) {
3719 if (preg_match("/image/", $entry["type"]) ||
3720 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3722 if (!$hide_images) {
3724 alt=\"".htmlspecialchars($entry["filename"])."\"
3725 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3727 $rv .= "<p><a target=\"_blank\"
3728 href=\"".htmlspecialchars($entry["url"])."\"
3729 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3737 if (count($entries_inline) > 0) {
3738 $rv .= "<hr clear='both'/>";
3739 foreach ($entries_inline as $entry) { $rv .= $entry; };
3740 $rv .= "<hr clear='both'/>";
3743 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3744 "<option value=''>" . __('Attachments')."</option>";
3746 foreach ($entries as $entry) {
3747 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3757 function getLastArticleId() {
3758 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3759 WHERE owner_uid = " . $_SESSION["uid"]);
3761 if (db_num_rows($result) == 1) {
3762 return db_fetch_result($result, 0, "id");
3768 function build_url($parts) {
3769 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3773 * Converts a (possibly) relative URL to a absolute one.
3775 * @param string $url Base URL (i.e. from where the document is)
3776 * @param string $rel_url Possibly relative URL in the document
3778 * @return string Absolute URL
3780 function rewrite_relative_url($url, $rel_url) {
3781 if (strpos($rel_url, "magnet:") === 0) {
3783 } else if (strpos($rel_url, "://") !== false) {
3785 } else if (strpos($rel_url, "//") === 0) {
3786 # protocol-relative URL (rare but they exist)
3788 } else if (strpos($rel_url, "/") === 0)
3790 $parts = parse_url($url);
3791 $parts['path'] = $rel_url;
3793 return build_url($parts);
3796 $parts = parse_url($url);
3797 if (!isset($parts['path'])) {
3798 $parts['path'] = '/';
3800 $dir = $parts['path'];
3801 if (substr($dir, -1) !== '/') {
3802 $dir = dirname($parts['path']);
3803 $dir !== '/' && $dir .= '/';
3805 $parts['path'] = $dir . $rel_url;
3807 return build_url($parts);
3811 function sphinx_search($query, $offset = 0, $limit = 30) {
3812 require_once 'lib/sphinxapi.php';
3814 $sphinxClient = new SphinxClient();
3816 $sphinxpair = explode(":", SPHINX_SERVER
, 2);
3818 $sphinxClient->SetServer($sphinxpair[0], $sphinxpair[1]);
3819 $sphinxClient->SetConnectTimeout(1);
3821 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3822 'feed_title' => 20));
3824 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3825 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3826 $sphinxClient->SetLimits($offset, $limit, 1000);
3827 $sphinxClient->SetArrayResult(false);
3828 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3830 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3834 if (is_array($result['matches'])) {
3835 foreach (array_keys($result['matches']) as $int_id) {
3836 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3837 array_push($ids, $ref_id);
3844 function cleanup_tags($days = 14, $limit = 1000) {
3846 if (DB_TYPE
== "pgsql") {
3847 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3848 } else if (DB_TYPE
== "mysql") {
3849 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3854 while ($limit > 0) {
3857 $query = "SELECT ttrss_tags.id AS id
3858 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3859 WHERE post_int_id = int_id AND $interval_query AND
3860 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3862 $result = db_query($query);
3866 while ($line = db_fetch_assoc($result)) {
3867 array_push($ids, $line['id']);
3870 if (count($ids) > 0) {
3871 $ids = join(",", $ids);
3873 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
3874 $tags_deleted +
= db_affected_rows($tmp_result);
3879 $limit -= $limit_part;
3882 return $tags_deleted;
3885 function print_user_stylesheet() {
3886 $value = get_pref('USER_STYLESHEET');
3889 print "<style type=\"text/css\">";
3890 print str_replace("<br/>", "\n", $value);
3896 function rewrite_urls($html) {
3897 libxml_use_internal_errors(true);
3899 $charset_hack = '<head>
3900 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3903 $doc = new DOMDocument();
3904 $doc->loadHTML($charset_hack . $html);
3905 $xpath = new DOMXPath($doc);
3907 $entries = $xpath->query('//*/text()');
3909 foreach ($entries as $entry) {
3910 if (strstr($entry->wholeText
, "://") !== false) {
3911 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3912 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3914 if ($text != $entry->wholeText
) {
3915 $cdoc = new DOMDocument();
3916 $cdoc->loadHTML($charset_hack . $text);
3919 foreach ($cdoc->childNodes
as $cnode) {
3920 $cnode = $doc->importNode($cnode, true);
3923 $entry->parentNode
->insertBefore($cnode);
3927 $entry->parentNode
->removeChild($entry);
3933 $node = $doc->getElementsByTagName('body')->item(0);
3935 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3937 return $doc->saveXML($node);
3942 function filter_to_sql($filter, $owner_uid) {
3945 if (DB_TYPE
== "pgsql")
3948 $reg_qpart = "REGEXP";
3950 foreach ($filter["rules"] AS $rule) {
3951 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3952 $rule['reg_exp']) !== FALSE;
3954 if ($regexp_valid) {
3956 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
3958 switch ($rule["type"]) {
3960 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3961 $rule['reg_exp'] . "')";
3964 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3965 $rule['reg_exp'] . "')";
3968 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3969 $rule['reg_exp'] . "') OR LOWER(" .
3970 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3973 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3974 $rule['reg_exp'] . "')";
3977 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3978 $rule['reg_exp'] . "')";
3981 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3982 $rule['reg_exp'] . "')";
3986 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3988 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
3989 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
3992 if (isset($rule["cat_id"])) {
3994 if ($rule["cat_id"] > 0) {
3995 $children = getChildCategories($rule["cat_id"], $owner_uid);
3996 array_push($children, $rule["cat_id"]);
3998 $children = join(",", $children);
4000 $cat_qpart = "cat_id IN ($children)";
4002 $cat_qpart = "cat_id IS NULL";
4005 $qpart .= " AND $cat_qpart";
4008 array_push($query, "($qpart)");
4013 if (count($query) > 0) {
4014 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
4016 $fullquery = "(false)";
4019 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
4024 if (!function_exists('gzdecode')) {
4025 function gzdecode($string) { // no support for 2nd argument
4026 return file_get_contents('compress.zlib://data:who/cares;base64,'.
4027 base64_encode($string));
4031 function get_random_bytes($length) {
4032 if (function_exists('openssl_random_pseudo_bytes')) {
4033 return openssl_random_pseudo_bytes($length);
4037 for ($i = 0; $i < $length; $i++
)
4038 $output .= chr(mt_rand(0, 255));
4044 function read_stdin() {
4045 $fp = fopen("php://stdin", "r");
4048 $line = trim(fgets($fp));
4056 function tmpdirname($path, $prefix) {
4057 // Use PHP's tmpfile function to create a temporary
4058 // directory name. Delete the file and keep the name.
4059 $tempname = tempnam($path,$prefix);
4063 if (!unlink($tempname))
4069 function getFeedCategory($feed) {
4070 $result = db_query("SELECT cat_id FROM ttrss_feeds
4071 WHERE id = '$feed'");
4073 if (db_num_rows($result) > 0) {
4074 return db_fetch_result($result, 0, "cat_id");
4081 function implements_interface($class, $interface) {
4082 return in_array($interface, class_implements($class));
4085 function geturl($url){
4087 if (!function_exists('curl_init'))
4088 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
);
4090 $curl = curl_init();
4091 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4092 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4093 $header[] = "Cache-Control: max-age=0";
4094 $header[] = "Connection: keep-alive";
4095 $header[] = "Keep-Alive: 300";
4096 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4097 $header[] = "Accept-Language: en-us,en;q=0.5";
4098 $header[] = "Pragma: ";
4100 curl_setopt($curl, CURLOPT_URL
, $url);
4101 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4102 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4103 curl_setopt($curl, CURLOPT_HEADER
, true);
4104 curl_setopt($curl, CURLOPT_REFERER
, $url);
4105 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4106 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4107 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4108 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4109 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4111 $html = curl_exec($curl);
4113 $status = curl_getinfo($curl);
4116 if($status['http_code']!=200){
4117 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4118 list($header) = explode("\r\n\r\n", $html, 2);
4120 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4121 $url = trim(str_replace($matches[1],"",$matches[0]));
4122 $url_parsed = parse_url($url);
4123 return (isset($url_parsed))?
geturl($url, $referer):'';
4126 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4127 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4128 # $handle = @fopen('./curl.error.log', 'a');
4129 # fwrite($handle, $line);
4135 function get_minified_js($files) {
4136 require_once 'lib/jshrink/Minifier.php';
4140 foreach ($files as $js) {
4141 if (!isset($_GET['debug'])) {
4142 $cached_file = CACHE_DIR
. "/js/$js.js";
4144 if (file_exists($cached_file) &&
4145 is_readable($cached_file) &&
4146 filemtime($cached_file) >= filemtime("js/$js.js")) {
4148 $rv .= file_get_contents($cached_file);
4151 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4152 file_put_contents($cached_file, $minified);
4156 $rv .= file_get_contents("js/$js.js");
4163 function stylesheet_tag($filename) {
4164 $timestamp = filemtime($filename);
4166 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4169 function javascript_tag($filename) {
4172 if (!(strpos($filename, "?") === FALSE)) {
4173 $query = substr($filename, strpos($filename, "?")+
1);
4174 $filename = substr($filename, 0, strpos($filename, "?"));
4177 $timestamp = filemtime($filename);
4179 if ($query) $timestamp .= "&$query";
4181 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4184 function calculate_dep_timestamp() {
4185 $files = array_merge(glob("js/*.js"), glob("*.css"));
4189 foreach ($files as $file) {
4190 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4196 function T_js_decl($s1, $s2) {
4198 $s1 = preg_replace("/\n/", "", $s1);
4199 $s2 = preg_replace("/\n/", "", $s2);
4201 $s1 = preg_replace("/\"/", "\\\"", $s1);
4202 $s2 = preg_replace("/\"/", "\\\"", $s2);
4204 return "T_messages[\"$s1\"] = \"$s2\";\n";
4208 function init_js_translations() {
4210 print 'var T_messages = new Object();
4213 if (T_messages[msg]) {
4214 return T_messages[msg];
4220 function ngettext(msg1, msg2, n) {
4221 return (parseInt(n) > 1) ? msg2 : msg1;
4224 $l10n = _get_reader();
4226 for ($i = 0; $i < $l10n->total
; $i++
) {
4227 $orig = $l10n->get_original_string($i);
4228 $translation = __($orig);
4230 print T_js_decl($orig, $translation);
4234 function label_to_feed_id($label) {
4235 return LABEL_BASE_INDEX
- 1 - abs($label);
4238 function feed_to_label_id($feed) {
4239 return LABEL_BASE_INDEX
- 1 +
abs($feed);
4242 function format_libxml_error($error) {
4243 return T_sprintf("LibXML error %s at line %d (column %d): %s",
4244 $error->code
, $error->line
, $error->column
,