2 define('EXPECTED_CONFIG_VERSION', 26);
3 define('SCHEMA_VERSION', 121);
5 define('LABEL_BASE_INDEX', -1024);
6 define('PLUGIN_FEED_BASE_INDEX', -128);
8 define('COOKIE_LIFETIME_LONG', 86400*365);
10 $fetch_last_error = false;
11 $fetch_last_error_code = false;
12 $fetch_last_content_type = false;
13 $fetch_curl_used = false;
15 mb_internal_encoding("UTF-8");
16 date_default_timezone_set('UTC');
17 if (defined('E_DEPRECATED')) {
18 error_reporting(E_ALL
& ~E_NOTICE
& ~E_DEPRECATED
);
20 error_reporting(E_ALL
& ~E_NOTICE
);
23 require_once 'config.php';
26 * Define a constant if not already defined
28 * @param string $name The constant name.
29 * @param mixed $value The constant value.
31 * @return boolean True if defined successfully or not.
33 function define_default($name, $value) {
34 defined($name) or define($name, $value);
37 ///// Some defaults that you can override in config.php //////
39 define_default('FEED_FETCH_TIMEOUT', 45);
40 // How may seconds to wait for response when requesting feed from a site
41 define_default('FEED_FETCH_NO_CACHE_TIMEOUT', 15);
42 // How may seconds to wait for response when requesting feed from a
43 // site when that feed wasn't cached before
44 define_default('FILE_FETCH_TIMEOUT', 45);
45 // Default timeout when fetching files from remote sites
46 define_default('FILE_FETCH_CONNECT_TIMEOUT', 15);
47 // How many seconds to wait for initial response from website when
48 // fetching files from remote sites
50 if (DB_TYPE
== "pgsql") {
51 define('SUBSTRING_FOR_DATE', 'SUBSTRING_FOR_DATE');
53 define('SUBSTRING_FOR_DATE', 'SUBSTRING');
57 * Return available translations names.
60 * @return array A array of available translations.
62 function get_translations() {
64 "auto" => "Detect automatically",
70 "fr_FR" => "Français",
71 "hu_HU" => "Magyar (Hungarian)",
72 "it_IT" => "Italiano",
73 "ja_JP" => "日本語 (Japanese)",
74 "ko_KR" => "한국어 (Korean)",
75 "lv_LV" => "Latviešu",
76 "nb_NO" => "Norwegian bokmål",
80 "pt_BR" => "Portuguese/Brazil",
81 "zh_CN" => "Simplified Chinese",
88 require_once "lib/accept-to-gettext.php";
89 require_once "lib/gettext/gettext.inc";
92 function startup_gettext() {
94 # Get locale from Accept-Language header
95 $lang = al2gt(array_keys(get_translations()), "text/html");
97 if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) {
98 $lang = _TRANSLATION_OVERRIDE_DEFAULT
;
101 if ($_SESSION["uid"] && get_schema_version() >= 120) {
102 $pref_lang = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
104 if ($pref_lang && $pref_lang != 'auto') {
110 if (defined('LC_MESSAGES')) {
111 _setlocale(LC_MESSAGES
, $lang);
112 } else if (defined('LC_ALL')) {
113 _setlocale(LC_ALL
, $lang);
116 _bindtextdomain("messages", "locale");
118 _textdomain("messages");
119 _bind_textdomain_codeset("messages", "UTF-8");
123 require_once 'db-prefs.php';
124 require_once 'version.php';
125 require_once 'ccache.php';
126 require_once 'labels.php';
128 define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . VERSION
. ' (http://tt-rss.org/)');
129 ini_set('user_agent', SELF_USER_AGENT
);
131 require_once 'lib/pubsubhubbub/publisher.php';
133 $schema_version = false;
136 * Print a timestamped debug message.
138 * @param string $msg The debug message.
141 function _debug($msg, $show = true) {
143 $ts = strftime("%H:%M:%S", time());
144 if (function_exists('posix_getpid')) {
145 $ts = "$ts/" . posix_getpid();
148 if ($show && !(defined('QUIET') && QUIET
)) {
149 print "[$ts] $msg\n";
152 if (defined('LOGFILE')) {
153 $fp = fopen(LOGFILE
, 'a+');
156 fputs($fp, "[$ts] $msg\n");
164 * Purge a feed old posts.
166 * @param mixed $link A database connection.
167 * @param mixed $feed_id The id of the purged feed.
168 * @param mixed $purge_interval Olderness of purged posts.
169 * @param boolean $debug Set to True to enable the debug. False by default.
173 function purge_feed($feed_id, $purge_interval, $debug = false) {
175 if (!$purge_interval) $purge_interval = feed_purge_interval($feed_id);
180 "SELECT owner_uid FROM ttrss_feeds WHERE id = '$feed_id'");
184 if (db_num_rows($result) == 1) {
185 $owner_uid = db_fetch_result($result, 0, "owner_uid");
188 if ($purge_interval == -1 ||
!$purge_interval) {
190 ccache_update($feed_id, $owner_uid);
195 if (!$owner_uid) return;
197 if (FORCE_ARTICLE_PURGE
== 0) {
198 $purge_unread = get_pref("PURGE_UNREAD_ARTICLES",
201 $purge_unread = true;
202 $purge_interval = FORCE_ARTICLE_PURGE
;
205 if (!$purge_unread) $query_limit = " unread = false AND ";
207 if (DB_TYPE
== "pgsql") {
208 $pg_version = get_pgsql_version();
210 if (preg_match("/^7\./", $pg_version) ||
preg_match("/^8\.0/", $pg_version)) {
212 $result = db_query("DELETE FROM ttrss_user_entries WHERE
213 ttrss_entries.id = ref_id AND
215 feed_id = '$feed_id' AND
217 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
221 $result = db_query("DELETE FROM ttrss_user_entries
223 WHERE ttrss_entries.id = ref_id AND
225 feed_id = '$feed_id' AND
227 ttrss_entries.date_updated < NOW() - INTERVAL '$purge_interval days'");
232 /* $result = db_query("DELETE FROM ttrss_user_entries WHERE
233 marked = false AND feed_id = '$feed_id' AND
234 (SELECT date_updated FROM ttrss_entries WHERE
235 id = ref_id) < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)"); */
237 $result = db_query("DELETE FROM ttrss_user_entries
238 USING ttrss_user_entries, ttrss_entries
239 WHERE ttrss_entries.id = ref_id AND
241 feed_id = '$feed_id' AND
243 ttrss_entries.date_updated < DATE_SUB(NOW(), INTERVAL $purge_interval DAY)");
246 $rows = db_affected_rows($result);
248 ccache_update($feed_id, $owner_uid);
251 _debug("Purged feed $feed_id ($purge_interval): deleted $rows articles");
255 } // function purge_feed
257 function feed_purge_interval($feed_id) {
259 $result = db_query("SELECT purge_interval, owner_uid FROM ttrss_feeds
260 WHERE id = '$feed_id'");
262 if (db_num_rows($result) == 1) {
263 $purge_interval = db_fetch_result($result, 0, "purge_interval");
264 $owner_uid = db_fetch_result($result, 0, "owner_uid");
266 if ($purge_interval == 0) $purge_interval = get_pref(
267 'PURGE_OLD_DAYS', $owner_uid);
269 return $purge_interval;
276 function purge_orphans($do_output = false) {
278 // purge orphaned posts in main content table
279 $result = db_query("DELETE FROM ttrss_entries WHERE
280 (SELECT COUNT(int_id) FROM ttrss_user_entries WHERE ref_id = id) = 0");
283 $rows = db_affected_rows($result);
284 _debug("Purged $rows orphaned posts.");
288 function get_feed_update_interval($feed_id) {
289 $result = db_query("SELECT owner_uid, update_interval FROM
290 ttrss_feeds WHERE id = '$feed_id'");
292 if (db_num_rows($result) == 1) {
293 $update_interval = db_fetch_result($result, 0, "update_interval");
294 $owner_uid = db_fetch_result($result, 0, "owner_uid");
296 if ($update_interval != 0) {
297 return $update_interval;
299 return get_pref('DEFAULT_UPDATE_INTERVAL', $owner_uid, false);
307 function fetch_file_contents($url, $type = false, $login = false, $pass = false, $post_query = false, $timeout = false, $timestamp = 0) {
309 global $fetch_last_error;
310 global $fetch_last_error_code;
311 global $fetch_last_content_type;
312 global $fetch_curl_used;
314 $url = str_replace(' ', '%20', $url);
316 if (!defined('NO_CURL') && function_exists('curl_init')) {
318 $fetch_curl_used = true;
320 if (ini_get("safe_mode") ||
ini_get("open_basedir")) {
321 $new_url = geturl($url);
323 // geturl has already populated $fetch_last_error
326 $ch = curl_init($new_url);
328 $ch = curl_init($url);
331 if ($timestamp && !$post_query) {
332 curl_setopt($ch, CURLOPT_HTTPHEADER
,
333 array("If-Modified-Since: ".gmdate('D, d M Y H:i:s \G\M\T', $timestamp)));
336 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT
, $timeout ?
$timeout : FILE_FETCH_CONNECT_TIMEOUT
);
337 curl_setopt($ch, CURLOPT_TIMEOUT
, $timeout ?
$timeout : FILE_FETCH_TIMEOUT
);
338 curl_setopt($ch, CURLOPT_FOLLOWLOCATION
, !ini_get("safe_mode") && !ini_get("open_basedir"));
339 curl_setopt($ch, CURLOPT_MAXREDIRS
, 20);
340 curl_setopt($ch, CURLOPT_BINARYTRANSFER
, true);
341 curl_setopt($ch, CURLOPT_RETURNTRANSFER
, true);
342 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER
, false);
343 curl_setopt($ch, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
344 curl_setopt($ch, CURLOPT_USERAGENT
, SELF_USER_AGENT
);
345 curl_setopt($ch, CURLOPT_ENCODING
, "");
346 curl_setopt($ch, CURLOPT_REFERER
, $url);
349 curl_setopt($ch, CURLOPT_POST
, true);
350 curl_setopt($ch, CURLOPT_POSTFIELDS
, $post_query);
354 curl_setopt($ch, CURLOPT_USERPWD
, "$login:$pass");
356 $contents = @curl_exec
($ch);
358 if (curl_errno($ch) === 23 ||
curl_errno($ch) === 61) {
359 curl_setopt($ch, CURLOPT_ENCODING
, 'none');
360 $contents = @curl_exec
($ch);
363 if ($contents === false) {
364 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
369 $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE
);
370 $fetch_last_content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE
);
372 $fetch_last_error_code = $http_code;
374 if ($http_code != 200 ||
$type && strpos($fetch_last_content_type, "$type") === false) {
375 if (curl_errno($ch) != 0) {
376 $fetch_last_error = curl_errno($ch) . " " . curl_error($ch);
378 $fetch_last_error = "HTTP Code: $http_code";
389 $fetch_curl_used = false;
391 if ($login && $pass){
392 $url_parts = array();
394 preg_match("/(^[^:]*):\/\/(.*)/", $url, $url_parts);
396 $pass = urlencode($pass);
398 if ($url_parts[1] && $url_parts[2]) {
399 $url = $url_parts[1] . "://$login:$pass@" . $url_parts[2];
403 if (!$post_query && $timestamp) {
404 $context = stream_context_create(array(
407 'header' => "If-Modified-Since: ".gmdate("D, d M Y H:i:s \\G\\M\\T\r\n", $timestamp)
413 $old_error = error_get_last();
415 $data = @file_get_contents
($url, false, $context);
417 $fetch_last_content_type = false; // reset if no type was sent from server
418 if (isset($http_response_header) && is_array($http_response_header)) {
419 foreach ($http_response_header as $h) {
420 if (substr(strtolower($h), 0, 13) == 'content-type:') {
421 $fetch_last_content_type = substr($h, 14);
422 // don't abort here b/c there might be more than one
423 // e.g. if we were being redirected -- last one is the right one
426 if (substr(strtolower($h), 0, 7) == 'http/1.') {
427 $fetch_last_error_code = (int) substr($h, 9, 3);
433 $error = error_get_last();
435 if ($error['message'] != $old_error['message']) {
436 $fetch_last_error = $error["message"];
438 $fetch_last_error = "HTTP Code: $fetch_last_error_code";
447 * Try to determine the favicon URL for a feed.
448 * adapted from wordpress favicon plugin by Jeff Minard (http://thecodepro.com/)
449 * http://dev.wp-plugins.org/file/favatars/trunk/favatars.php
451 * @param string $url A feed or page URL
453 * @return mixed The favicon URL, or false if none was found.
455 function get_favicon_url($url) {
457 $favicon_url = false;
459 if ($html = @fetch_file_contents
($url)) {
461 libxml_use_internal_errors(true);
463 $doc = new DOMDocument();
464 $doc->loadHTML($html);
465 $xpath = new DOMXPath($doc);
467 $base = $xpath->query('/html/head/base');
468 foreach ($base as $b) {
469 $url = $b->getAttribute("href");
473 $entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
474 if (count($entries) > 0) {
475 foreach ($entries as $entry) {
476 $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
483 $favicon_url = rewrite_relative_url($url, "/favicon.ico");
486 } // function get_favicon_url
488 function check_feed_favicon($site_url, $feed) {
489 # print "FAVICON [$site_url]: $favicon_url\n";
491 $icon_file = ICONS_DIR
. "/$feed.ico";
493 if (!file_exists($icon_file)) {
494 $favicon_url = get_favicon_url($site_url);
497 // Limiting to "image" type misses those served with text/plain
498 $contents = fetch_file_contents($favicon_url); // , "image");
501 // Crude image type matching.
502 // Patterns gleaned from the file(1) source code.
503 if (preg_match('/^\x00\x00\x01\x00/', $contents)) {
504 // 0 string \000\000\001\000 MS Windows icon resource
505 //error_log("check_feed_favicon: favicon_url=$favicon_url isa MS Windows icon resource");
507 elseif (preg_match('/^GIF8/', $contents)) {
508 // 0 string GIF8 GIF image data
509 //error_log("check_feed_favicon: favicon_url=$favicon_url isa GIF image");
511 elseif (preg_match('/^\x89PNG\x0d\x0a\x1a\x0a/', $contents)) {
512 // 0 string \x89PNG\x0d\x0a\x1a\x0a PNG image data
513 //error_log("check_feed_favicon: favicon_url=$favicon_url isa PNG image");
515 elseif (preg_match('/^\xff\xd8/', $contents)) {
516 // 0 beshort 0xffd8 JPEG image data
517 //error_log("check_feed_favicon: favicon_url=$favicon_url isa JPG image");
520 //error_log("check_feed_favicon: favicon_url=$favicon_url isa UNKNOWN type");
526 $fp = @fopen
($icon_file, "w");
529 fwrite($fp, $contents);
531 chmod($icon_file, 0644);
539 function print_select($id, $default, $values, $attributes = "") {
540 print "<select name=\"$id\" id=\"$id\" $attributes>";
541 foreach ($values as $v) {
543 $sel = "selected=\"1\"";
549 print "<option value=\"$v\" $sel>$v</option>";
554 function print_select_hash($id, $default, $values, $attributes = "") {
555 print "<select name=\"$id\" id='$id' $attributes>";
556 foreach (array_keys($values) as $v) {
558 $sel = 'selected="selected"';
564 print "<option $sel value=\"$v\">".$values[$v]."</option>";
570 function print_radio($id, $default, $true_is, $values, $attributes = "") {
571 foreach ($values as $v) {
578 if ($v == $true_is) {
579 $sel .= " value=\"1\"";
581 $sel .= " value=\"0\"";
584 print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\"
585 type=\"radio\" $sel $attributes name=\"$id\"> $v ";
590 function initialize_user_prefs($uid, $profile = false) {
592 $uid = db_escape_string($uid);
596 $profile_qpart = "AND profile IS NULL";
598 $profile_qpart = "AND profile = '$profile'";
601 if (get_schema_version() < 63) $profile_qpart = "";
605 $result = db_query("SELECT pref_name,def_value FROM ttrss_prefs");
607 $u_result = db_query("SELECT pref_name
608 FROM ttrss_user_prefs WHERE owner_uid = '$uid' $profile_qpart");
610 $active_prefs = array();
612 while ($line = db_fetch_assoc($u_result)) {
613 array_push($active_prefs, $line["pref_name"]);
616 while ($line = db_fetch_assoc($result)) {
617 if (array_search($line["pref_name"], $active_prefs) === FALSE) {
618 // print "adding " . $line["pref_name"] . "<br>";
620 $line["def_value"] = db_escape_string($line["def_value"]);
621 $line["pref_name"] = db_escape_string($line["pref_name"]);
623 if (get_schema_version() < 63) {
624 db_query("INSERT INTO ttrss_user_prefs
625 (owner_uid,pref_name,value) VALUES
626 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."')");
629 db_query("INSERT INTO ttrss_user_prefs
630 (owner_uid,pref_name,value, profile) VALUES
631 ('$uid', '".$line["pref_name"]."','".$line["def_value"]."', $profile)");
641 function get_ssl_certificate_id() {
642 if ($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"]) {
643 return sha1($_SERVER["REDIRECT_SSL_CLIENT_M_SERIAL"] .
644 $_SERVER["REDIRECT_SSL_CLIENT_V_START"] .
645 $_SERVER["REDIRECT_SSL_CLIENT_V_END"] .
646 $_SERVER["REDIRECT_SSL_CLIENT_S_DN"]);
651 function authenticate_user($login, $password, $check_only = false) {
653 if (!SINGLE_USER_MODE
) {
656 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_AUTH_USER
) as $plugin) {
658 $user_id = (int) $plugin->authenticate($login, $password);
661 $_SESSION["auth_module"] = strtolower(get_class($plugin));
666 if ($user_id && !$check_only) {
669 $_SESSION["uid"] = $user_id;
670 $_SESSION["version"] = VERSION_STATIC
;
672 $result = db_query("SELECT login,access_level,pwd_hash FROM ttrss_users
673 WHERE id = '$user_id'");
675 $_SESSION["name"] = db_fetch_result($result, 0, "login");
676 $_SESSION["access_level"] = db_fetch_result($result, 0, "access_level");
677 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
679 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
682 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
683 $_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
684 $_SESSION["pwd_hash"] = db_fetch_result($result, 0, "pwd_hash");
686 $_SESSION["last_version_check"] = time();
688 initialize_user_prefs($_SESSION["uid"]);
697 $_SESSION["uid"] = 1;
698 $_SESSION["name"] = "admin";
699 $_SESSION["access_level"] = 10;
701 $_SESSION["hide_hello"] = true;
702 $_SESSION["hide_logout"] = true;
704 $_SESSION["auth_module"] = false;
706 if (!$_SESSION["csrf_token"]) {
707 $_SESSION["csrf_token"] = sha1(uniqid(rand(), true));
710 $_SESSION["ip_address"] = $_SERVER["REMOTE_ADDR"];
712 initialize_user_prefs($_SESSION["uid"]);
718 function make_password($length = 8) {
721 $possible = "0123456789abcdfghjkmnpqrstvwxyzABCDFGHJKMNPQRSTVWXYZ";
725 while ($i < $length) {
726 $char = substr($possible, mt_rand(0, strlen($possible)-1), 1);
728 if (!strstr($password, $char)) {
736 // this is called after user is created to initialize default feeds, labels
739 // user preferences are checked on every login, not here
741 function initialize_user($uid) {
743 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
744 values ('$uid', 'Tiny Tiny RSS: New Releases',
745 'http://tt-rss.org/releases.rss')");
747 db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
748 values ('$uid', 'Tiny Tiny RSS: Forum',
749 'http://tt-rss.org/forum/rss.php')");
752 function logout_user() {
754 if (isset($_COOKIE[session_name()])) {
755 setcookie(session_name(), '', time()-42000, '/');
759 function validate_csrf($csrf_token) {
760 return $csrf_token == $_SESSION['csrf_token'];
763 function load_user_plugins($owner_uid) {
765 $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
767 PluginHost
::getInstance()->load($plugins, PluginHost
::KIND_USER
, $owner_uid);
769 if (get_schema_version() > 100) {
770 PluginHost
::getInstance()->load_data();
775 function login_sequence() {
776 if (SINGLE_USER_MODE
) {
778 authenticate_user("admin", null);
779 load_user_plugins($_SESSION["uid"]);
781 if (!validate_session()) $_SESSION["uid"] = false;
783 if (!$_SESSION["uid"]) {
785 if (AUTH_AUTO_LOGIN
&& authenticate_user(null, null)) {
786 $_SESSION["ref_schema_version"] = get_schema_version(true);
788 authenticate_user(null, null, true);
791 if (!$_SESSION["uid"]) {
793 setcookie(session_name(), '', time()-42000, '/');
800 /* bump login timestamp */
801 db_query("UPDATE ttrss_users SET last_login = NOW() WHERE id = " .
803 $_SESSION["last_login_update"] = time();
806 if ($_SESSION["uid"]) {
808 load_user_plugins($_SESSION["uid"]);
812 db_query("DELETE FROM ttrss_counters_cache WHERE owner_uid = ".
813 $_SESSION["uid"] . " AND
814 (SELECT COUNT(id) FROM ttrss_feeds WHERE
815 ttrss_feeds.id = feed_id) = 0");
817 db_query("DELETE FROM ttrss_cat_counters_cache WHERE owner_uid = ".
818 $_SESSION["uid"] . " AND
819 (SELECT COUNT(id) FROM ttrss_feed_categories WHERE
820 ttrss_feed_categories.id = feed_id) = 0");
827 function truncate_string($str, $max_len, $suffix = '…') {
828 if (mb_strlen($str, "utf-8") > $max_len - 3) {
829 return mb_substr($str, 0, $max_len, "utf-8") . $suffix;
835 function convert_timestamp($timestamp, $source_tz, $dest_tz) {
838 $source_tz = new DateTimeZone($source_tz);
839 } catch (Exception
$e) {
840 $source_tz = new DateTimeZone('UTC');
844 $dest_tz = new DateTimeZone($dest_tz);
845 } catch (Exception
$e) {
846 $dest_tz = new DateTimeZone('UTC');
849 $dt = new DateTime(date('Y-m-d H:i:s', $timestamp), $source_tz);
850 return $dt->format('U') +
$dest_tz->getOffset($dt);
853 function make_local_datetime($timestamp, $long, $owner_uid = false,
854 $no_smart_dt = false) {
856 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
857 if (!$timestamp) $timestamp = '1970-01-01 0:00';
862 if (!$utc_tz) $utc_tz = new DateTimeZone('UTC');
864 $timestamp = substr($timestamp, 0, 19);
866 # We store date in UTC internally
867 $dt = new DateTime($timestamp, $utc_tz);
869 $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
871 if ($user_tz_string != 'Automatic') {
874 if (!$user_tz) $user_tz = new DateTimeZone($user_tz_string);
875 } catch (Exception
$e) {
879 $tz_offset = $user_tz->getOffset($dt);
881 $tz_offset = (int) -$_SESSION["clientTzOffset"];
884 $user_timestamp = $dt->format('U') +
$tz_offset;
887 return smart_date_time($user_timestamp,
888 $tz_offset, $owner_uid);
891 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
893 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
895 return date($format, $user_timestamp);
899 function smart_date_time($timestamp, $tz_offset = 0, $owner_uid = false) {
900 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
902 if (date("Y.m.d", $timestamp) == date("Y.m.d", time() +
$tz_offset)) {
903 return date("G:i", $timestamp);
904 } else if (date("Y", $timestamp) == date("Y", time() +
$tz_offset)) {
905 $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
906 return date($format, $timestamp);
908 $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
909 return date($format, $timestamp);
913 function sql_bool_to_bool($s) {
914 if ($s == "t" ||
$s == "1" ||
strtolower($s) == "true") {
921 function bool_to_sql_bool($s) {
929 // Session caching removed due to causing wrong redirects to upgrade
930 // script when get_schema_version() is called on an obsolete session
931 // created on a previous schema version.
932 function get_schema_version($nocache = false) {
933 global $schema_version;
935 if (!$schema_version && !$nocache) {
936 $result = db_query("SELECT schema_version FROM ttrss_version");
937 $version = db_fetch_result($result, 0, "schema_version");
938 $schema_version = $version;
941 return $schema_version;
945 function sanity_check() {
946 require_once 'errors.php';
949 $schema_version = get_schema_version(true);
951 if ($schema_version != SCHEMA_VERSION
) {
955 if (DB_TYPE
== "mysql") {
956 $result = db_query("SELECT true", false);
957 if (db_num_rows($result) != 1) {
962 if (db_escape_string("testTEST") != "testTEST") {
966 return array("code" => $error_code, "message" => $ERRORS[$error_code]);
969 function file_is_locked($filename) {
970 if (file_exists(LOCK_DIRECTORY
. "/$filename")) {
971 if (function_exists('flock')) {
972 $fp = @fopen
(LOCK_DIRECTORY
. "/$filename", "r");
974 if (flock($fp, LOCK_EX | LOCK_NB
)) {
985 return true; // consider the file always locked and skip the test
992 function make_lockfile($filename) {
993 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
995 if ($fp && flock($fp, LOCK_EX | LOCK_NB
)) {
996 $stat_h = fstat($fp);
997 $stat_f = stat(LOCK_DIRECTORY
. "/$filename");
999 if (strtoupper(substr(PHP_OS
, 0, 3)) !== 'WIN') {
1000 if ($stat_h["ino"] != $stat_f["ino"] ||
1001 $stat_h["dev"] != $stat_f["dev"]) {
1007 if (function_exists('posix_getpid')) {
1008 fwrite($fp, posix_getpid() . "\n");
1016 function make_stampfile($filename) {
1017 $fp = fopen(LOCK_DIRECTORY
. "/$filename", "w");
1019 if (flock($fp, LOCK_EX | LOCK_NB
)) {
1020 fwrite($fp, time() . "\n");
1021 flock($fp, LOCK_UN
);
1029 function sql_random_function() {
1030 if (DB_TYPE
== "mysql") {
1037 function catchup_feed($feed, $cat_view, $owner_uid = false, $max_id = false, $mode = 'all') {
1039 if (!$owner_uid) $owner_uid = $_SESSION['uid'];
1041 //if (preg_match("/^-?[0-9][0-9]*$/", $feed) != false) {
1043 // Todo: all this interval stuff needs some generic generator function
1045 $date_qpart = "false";
1049 if (DB_TYPE
== "pgsql") {
1050 $date_qpart = "date_entered < NOW() - INTERVAL '1 day' ";
1052 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 DAY) ";
1056 if (DB_TYPE
== "pgsql") {
1057 $date_qpart = "date_entered < NOW() - INTERVAL '1 week' ";
1059 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 1 WEEK) ";
1063 if (DB_TYPE
== "pgsql") {
1064 $date_qpart = "date_entered < NOW() - INTERVAL '2 week' ";
1066 $date_qpart = "date_entered < DATE_SUB(NOW(), INTERVAL 2 WEEK) ";
1070 $date_qpart = "true";
1073 if (is_numeric($feed)) {
1079 $children = getChildCategories($feed, $owner_uid);
1080 array_push($children, $feed);
1082 $children = join(",", $children);
1084 $cat_qpart = "cat_id IN ($children)";
1086 $cat_qpart = "cat_id IS NULL";
1089 db_query("UPDATE ttrss_user_entries
1090 SET unread = false, last_read = NOW() WHERE ref_id IN
1092 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1093 AND owner_uid = $owner_uid AND unread = true AND feed_id IN
1094 (SELECT id FROM ttrss_feeds WHERE $cat_qpart) AND $date_qpart) as tmp)");
1096 } else if ($feed == -2) {
1098 db_query("UPDATE ttrss_user_entries
1099 SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
1100 FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
1101 AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
1104 } else if ($feed > 0) {
1106 db_query("UPDATE ttrss_user_entries
1107 SET unread = false, last_read = NOW() WHERE ref_id IN
1109 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1110 AND owner_uid = $owner_uid AND unread = true AND feed_id = $feed AND $date_qpart) as tmp)");
1112 } else if ($feed < 0 && $feed > LABEL_BASE_INDEX
) { // special, like starred
1115 db_query("UPDATE ttrss_user_entries
1116 SET unread = false, last_read = NOW() WHERE ref_id IN
1118 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1119 AND owner_uid = $owner_uid AND unread = true AND marked = true AND $date_qpart) as tmp)");
1123 db_query("UPDATE ttrss_user_entries
1124 SET unread = false, last_read = NOW() WHERE ref_id IN
1126 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1127 AND owner_uid = $owner_uid AND unread = true AND published = true AND $date_qpart) as tmp)");
1132 $intl = get_pref("FRESH_ARTICLE_MAX_AGE");
1134 if (DB_TYPE
== "pgsql") {
1135 $match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
1137 $match_part = "date_entered > DATE_SUB(NOW(),
1138 INTERVAL $intl HOUR) ";
1141 db_query("UPDATE ttrss_user_entries
1142 SET unread = false, last_read = NOW() WHERE ref_id IN
1144 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1145 AND owner_uid = $owner_uid AND unread = true AND $date_qpart AND $match_part) as tmp)");
1149 db_query("UPDATE ttrss_user_entries
1150 SET unread = false, last_read = NOW() WHERE ref_id IN
1152 (SELECT id FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id
1153 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1156 } else if ($feed < LABEL_BASE_INDEX
) { // label
1158 $label_id = feed_to_label_id($feed);
1160 db_query("UPDATE ttrss_user_entries
1161 SET unread = false, last_read = NOW() WHERE ref_id IN
1163 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_user_labels2 WHERE ref_id = id
1164 AND label_id = '$label_id' AND ref_id = article_id
1165 AND owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1169 ccache_update($feed, $owner_uid, $cat_view);
1172 db_query("UPDATE ttrss_user_entries
1173 SET unread = false, last_read = NOW() WHERE ref_id IN
1175 (SELECT ttrss_entries.id FROM ttrss_entries, ttrss_user_entries, ttrss_tags WHERE ref_id = ttrss_entries.id
1176 AND post_int_id = int_id AND tag_name = '$feed'
1177 AND ttrss_user_entries.owner_uid = $owner_uid AND unread = true AND $date_qpart) as tmp)");
1182 function getAllCounters() {
1183 $data = getGlobalCounters();
1185 $data = array_merge($data, getVirtCounters());
1186 $data = array_merge($data, getLabelCounters());
1187 $data = array_merge($data, getFeedCounters());
1188 $data = array_merge($data, getCategoryCounters());
1193 function getCategoryTitle($cat_id) {
1195 if ($cat_id == -1) {
1196 return __("Special");
1197 } else if ($cat_id == -2) {
1198 return __("Labels");
1201 $result = db_query("SELECT title FROM ttrss_feed_categories WHERE
1204 if (db_num_rows($result) == 1) {
1205 return db_fetch_result($result, 0, "title");
1207 return __("Uncategorized");
1213 function getCategoryCounters() {
1216 /* Labels category */
1218 $cv = array("id" => -2, "kind" => "cat",
1219 "counter" => getCategoryUnread(-2));
1221 array_push($ret_arr, $cv);
1223 $result = db_query("SELECT id AS cat_id, value AS unread,
1224 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2
1225 WHERE c2.parent_cat = ttrss_feed_categories.id) AS num_children
1226 FROM ttrss_feed_categories, ttrss_cat_counters_cache
1227 WHERE ttrss_cat_counters_cache.feed_id = id AND
1228 ttrss_cat_counters_cache.owner_uid = ttrss_feed_categories.owner_uid AND
1229 ttrss_feed_categories.owner_uid = " . $_SESSION["uid"]);
1231 while ($line = db_fetch_assoc($result)) {
1232 $line["cat_id"] = (int) $line["cat_id"];
1234 if ($line["num_children"] > 0) {
1235 $child_counter = getCategoryChildrenUnread($line["cat_id"], $_SESSION["uid"]);
1240 $cv = array("id" => $line["cat_id"], "kind" => "cat",
1241 "counter" => $line["unread"] +
$child_counter);
1243 array_push($ret_arr, $cv);
1246 /* Special case: NULL category doesn't actually exist in the DB */
1248 $cv = array("id" => 0, "kind" => "cat",
1249 "counter" => (int) ccache_find(0, $_SESSION["uid"], true));
1251 array_push($ret_arr, $cv);
1256 // only accepts real cats (>= 0)
1257 function getCategoryChildrenUnread($cat, $owner_uid = false) {
1258 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1260 $result = db_query("SELECT id FROM ttrss_feed_categories WHERE parent_cat = '$cat'
1261 AND owner_uid = $owner_uid");
1265 while ($line = db_fetch_assoc($result)) {
1266 $unread +
= getCategoryUnread($line["id"], $owner_uid);
1267 $unread +
= getCategoryChildrenUnread($line["id"], $owner_uid);
1273 function getCategoryUnread($cat, $owner_uid = false) {
1275 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1280 $cat_query = "cat_id = '$cat'";
1282 $cat_query = "cat_id IS NULL";
1285 $result = db_query("SELECT id FROM ttrss_feeds WHERE $cat_query
1286 AND owner_uid = " . $owner_uid);
1288 $cat_feeds = array();
1289 while ($line = db_fetch_assoc($result)) {
1290 array_push($cat_feeds, "feed_id = " . $line["id"]);
1293 if (count($cat_feeds) == 0) return 0;
1295 $match_part = implode(" OR ", $cat_feeds);
1297 $result = db_query("SELECT COUNT(int_id) AS unread
1298 FROM ttrss_user_entries
1299 WHERE unread = true AND ($match_part)
1300 AND owner_uid = " . $owner_uid);
1304 # this needs to be rewritten
1305 while ($line = db_fetch_assoc($result)) {
1306 $unread +
= $line["unread"];
1310 } else if ($cat == -1) {
1311 return getFeedUnread(-1) +
getFeedUnread(-2) +
getFeedUnread(-3) +
getFeedUnread(0);
1312 } else if ($cat == -2) {
1314 $result = db_query("
1315 SELECT COUNT(unread) AS unread FROM
1316 ttrss_user_entries, ttrss_user_labels2
1317 WHERE article_id = ref_id AND unread = true
1318 AND ttrss_user_entries.owner_uid = '$owner_uid'");
1320 $unread = db_fetch_result($result, 0, "unread");
1327 function getFeedUnread($feed, $is_cat = false) {
1328 return getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]);
1331 function getLabelUnread($label_id, $owner_uid = false) {
1332 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1334 $result = db_query("SELECT COUNT(ref_id) AS unread FROM ttrss_user_entries, ttrss_user_labels2
1335 WHERE owner_uid = '$owner_uid' AND unread = true AND label_id = '$label_id' AND article_id = ref_id");
1337 if (db_num_rows($result) != 0) {
1338 return db_fetch_result($result, 0, "unread");
1344 function getFeedArticles($feed, $is_cat = false, $unread_only = false,
1345 $owner_uid = false) {
1347 $n_feed = (int) $feed;
1348 $need_entries = false;
1350 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
1353 $unread_qpart = "unread = true";
1355 $unread_qpart = "true";
1359 return getCategoryUnread($n_feed, $owner_uid);
1360 } else if ($n_feed == -6) {
1362 } else if ($feed != "0" && $n_feed == 0) {
1364 $feed = db_escape_string($feed);
1366 $result = db_query("SELECT SUM((SELECT COUNT(int_id)
1367 FROM ttrss_user_entries,ttrss_entries WHERE int_id = post_int_id
1368 AND ref_id = id AND $unread_qpart)) AS count FROM ttrss_tags
1369 WHERE owner_uid = $owner_uid AND tag_name = '$feed'");
1370 return db_fetch_result($result, 0, "count");
1372 } else if ($n_feed == -1) {
1373 $match_part = "marked = true";
1374 } else if ($n_feed == -2) {
1375 $match_part = "published = true";
1376 } else if ($n_feed == -3) {
1377 $match_part = "unread = true AND score >= 0";
1379 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
1381 if (DB_TYPE
== "pgsql") {
1382 $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
1384 $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
1387 $need_entries = true;
1389 } else if ($n_feed == -4) {
1390 $match_part = "true";
1391 } else if ($n_feed >= 0) {
1394 $match_part = "feed_id = '$n_feed'";
1396 $match_part = "feed_id IS NULL";
1399 } else if ($feed < LABEL_BASE_INDEX
) {
1401 $label_id = feed_to_label_id($feed);
1403 return getLabelUnread($label_id, $owner_uid);
1409 if ($need_entries) {
1410 $from_qpart = "ttrss_user_entries,ttrss_entries";
1411 $from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
1413 $from_qpart = "ttrss_user_entries";
1416 $query = "SELECT count(int_id) AS unread
1417 FROM $from_qpart WHERE
1418 $unread_qpart AND $from_where ($match_part) AND ttrss_user_entries.owner_uid = $owner_uid";
1420 //echo "[$feed/$query]\n";
1422 $result = db_query($query);
1426 $result = db_query("SELECT COUNT(post_int_id) AS unread
1427 FROM ttrss_tags,ttrss_user_entries,ttrss_entries
1428 WHERE tag_name = '$feed' AND post_int_id = int_id AND ref_id = ttrss_entries.id
1429 AND $unread_qpart AND ttrss_tags.owner_uid = " . $owner_uid);
1432 $unread = db_fetch_result($result, 0, "unread");
1437 function getGlobalUnread($user_id = false) {
1440 $user_id = $_SESSION["uid"];
1443 $result = db_query("SELECT SUM(value) AS c_id FROM ttrss_counters_cache
1444 WHERE owner_uid = '$user_id' AND feed_id > 0");
1446 $c_id = db_fetch_result($result, 0, "c_id");
1451 function getGlobalCounters($global_unread = -1) {
1454 if ($global_unread == -1) {
1455 $global_unread = getGlobalUnread();
1458 $cv = array("id" => "global-unread",
1459 "counter" => (int) $global_unread);
1461 array_push($ret_arr, $cv);
1463 $result = db_query("SELECT COUNT(id) AS fn FROM
1464 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1466 $subscribed_feeds = db_fetch_result($result, 0, "fn");
1468 $cv = array("id" => "subscribed-feeds",
1469 "counter" => (int) $subscribed_feeds);
1471 array_push($ret_arr, $cv);
1476 function getVirtCounters() {
1480 for ($i = 0; $i >= -4; $i--) {
1482 $count = getFeedUnread($i);
1484 if ($i == 0 ||
$i == -1 ||
$i == -2)
1485 $auxctr = getFeedArticles($i, false);
1489 $cv = array("id" => $i,
1490 "counter" => (int) $count,
1491 "auxcounter" => $auxctr);
1493 // if (get_pref('EXTENDED_FEEDLIST'))
1494 // $cv["xmsg"] = getFeedArticles($i)." ".__("total");
1496 array_push($ret_arr, $cv);
1499 $feeds = PluginHost
::getInstance()->get_feeds(-1);
1501 if (is_array($feeds)) {
1502 foreach ($feeds as $feed) {
1503 $cv = array("id" => PluginHost
::pfeed_to_feed_id($feed['id']),
1504 "counter" => $feed['sender']->get_unread($feed['id']));
1505 array_push($ret_arr, $cv);
1512 function getLabelCounters($descriptions = false) {
1516 $owner_uid = $_SESSION["uid"];
1518 $result = db_query("SELECT id,caption,COUNT(u1.unread) AS unread,COUNT(u2.unread) AS total
1519 FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
1520 (ttrss_labels2.id = label_id)
1521 LEFT JOIN ttrss_user_entries AS u1 ON (u1.ref_id = article_id AND u1.unread = true
1522 AND u1.owner_uid = $owner_uid)
1523 LEFT JOIN ttrss_user_entries AS u2 ON (u2.ref_id = article_id AND u2.unread = false
1524 AND u2.owner_uid = $owner_uid)
1525 WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
1526 ttrss_labels2.caption");
1528 while ($line = db_fetch_assoc($result)) {
1530 $id = label_to_feed_id($line["id"]);
1532 $cv = array("id" => $id,
1533 "counter" => (int) $line["unread"],
1534 "auxcounter" => (int) $line["total"]);
1537 $cv["description"] = $line["caption"];
1539 array_push($ret_arr, $cv);
1545 function getFeedCounters($active_feed = false) {
1549 $query = "SELECT ttrss_feeds.id,
1551 ".SUBSTRING_FOR_DATE
."(ttrss_feeds.last_updated,1,19) AS last_updated,
1552 last_error, value AS count
1553 FROM ttrss_feeds, ttrss_counters_cache
1554 WHERE ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
1555 AND ttrss_counters_cache.owner_uid = ttrss_feeds.owner_uid
1556 AND ttrss_counters_cache.feed_id = id";
1558 $result = db_query($query);
1559 $fctrs_modified = false;
1561 while ($line = db_fetch_assoc($result)) {
1564 $count = $line["count"];
1565 $last_error = htmlspecialchars($line["last_error"]);
1567 $last_updated = make_local_datetime($line['last_updated'], false);
1569 $has_img = feed_has_icon($id);
1571 if (date('Y') - date('Y', strtotime($line['last_updated'])) > 2)
1574 $cv = array("id" => $id,
1575 "updated" => $last_updated,
1576 "counter" => (int) $count,
1577 "has_img" => (int) $has_img);
1580 $cv["error"] = $last_error;
1582 // if (get_pref('EXTENDED_FEEDLIST'))
1583 // $cv["xmsg"] = getFeedArticles($id)." ".__("total");
1585 if ($active_feed && $id == $active_feed)
1586 $cv["title"] = truncate_string($line["title"], 30);
1588 array_push($ret_arr, $cv);
1595 function get_pgsql_version() {
1596 $result = db_query("SELECT version() AS version");
1597 $version = explode(" ", db_fetch_result($result, 0, "version"));
1602 * @return array (code => Status code, message => error message if available)
1604 * 0 - OK, Feed already exists
1605 * 1 - OK, Feed added
1607 * 3 - URL content is HTML, no feeds available
1608 * 4 - URL content is HTML which contains multiple feeds.
1609 * Here you should call extractfeedurls in rpc-backend
1610 * to get all possible feeds.
1611 * 5 - Couldn't download the URL content.
1612 * 6 - Content is an invalid XML.
1614 function subscribe_to_feed($url, $cat_id = 0,
1615 $auth_login = '', $auth_pass = '') {
1617 global $fetch_last_error;
1619 require_once "include/rssfuncs.php";
1621 $url = fix_url($url);
1623 if (!$url ||
!validate_feed_url($url)) return array("code" => 2);
1625 $contents = @fetch_file_contents
($url, false, $auth_login, $auth_pass);
1628 return array("code" => 5, "message" => $fetch_last_error);
1631 if (is_html($contents)) {
1632 $feedUrls = get_feeds_from_html($url, $contents);
1634 if (count($feedUrls) == 0) {
1635 return array("code" => 3);
1636 } else if (count($feedUrls) > 1) {
1637 return array("code" => 4, "feeds" => $feedUrls);
1639 //use feed url as new URL
1640 $url = key($feedUrls);
1643 /* libxml_use_internal_errors(true);
1644 $doc = new DOMDocument();
1645 $doc->loadXML($contents);
1646 $error = libxml_get_last_error();
1647 libxml_clear_errors();
1650 $error_message = format_libxml_error($error);
1652 return array("code" => 6, "message" => $error_message);
1655 if ($cat_id == "0" ||
!$cat_id) {
1656 $cat_qpart = "NULL";
1658 $cat_qpart = "'$cat_id'";
1662 "SELECT id FROM ttrss_feeds
1663 WHERE feed_url = '$url' AND owner_uid = ".$_SESSION["uid"]);
1665 if (strlen(FEED_CRYPT_KEY
) > 0) {
1666 require_once "crypt.php";
1667 $auth_pass = substr(encrypt_string($auth_pass), 0, 250);
1668 $auth_pass_encrypted = 'true';
1670 $auth_pass_encrypted = 'false';
1673 $auth_pass = db_escape_string($auth_pass);
1675 if (db_num_rows($result) == 0) {
1677 "INSERT INTO ttrss_feeds
1678 (owner_uid,feed_url,title,cat_id, auth_login,auth_pass,update_method,auth_pass_encrypted)
1679 VALUES ('".$_SESSION["uid"]."', '$url',
1680 '[Unknown]', $cat_qpart, '$auth_login', '$auth_pass', 0, $auth_pass_encrypted)");
1683 "SELECT id FROM ttrss_feeds WHERE feed_url = '$url'
1684 AND owner_uid = " . $_SESSION["uid"]);
1686 $feed_id = db_fetch_result($result, 0, "id");
1689 update_rss_feed($feed_id, true);
1692 return array("code" => 1);
1694 return array("code" => 0);
1698 function print_feed_select($id, $default_id = "",
1699 $attributes = "", $include_all_feeds = true,
1700 $root_id = false, $nest_level = 0) {
1703 print "<select id=\"$id\" name=\"$id\" $attributes>";
1704 if ($include_all_feeds) {
1705 $is_selected = ("0" == $default_id) ?
"selected=\"1\"" : "";
1706 print "<option $is_selected value=\"0\">".__('All feeds')."</option>";
1710 if (get_pref('ENABLE_FEED_CATS')) {
1713 $parent_qpart = "parent_cat = '$root_id'";
1715 $parent_qpart = "parent_cat IS NULL";
1717 $result = db_query("SELECT id,title,
1718 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1719 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1720 FROM ttrss_feed_categories
1721 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1723 while ($line = db_fetch_assoc($result)) {
1725 for ($i = 0; $i < $nest_level; $i++
)
1726 $line["title"] = " - " . $line["title"];
1728 $is_selected = ("CAT:".$line["id"] == $default_id) ?
"selected=\"1\"" : "";
1730 printf("<option $is_selected value='CAT:%d'>%s</option>",
1731 $line["id"], htmlspecialchars($line["title"]));
1733 if ($line["num_children"] > 0)
1734 print_feed_select($id, $default_id, $attributes,
1735 $include_all_feeds, $line["id"], $nest_level+
1);
1737 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1738 WHERE cat_id = '".$line["id"]."' AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1740 while ($fline = db_fetch_assoc($feed_result)) {
1741 $is_selected = ($fline["id"] == $default_id) ?
"selected=\"1\"" : "";
1743 $fline["title"] = " + " . $fline["title"];
1745 for ($i = 0; $i < $nest_level; $i++
)
1746 $fline["title"] = " - " . $fline["title"];
1748 printf("<option $is_selected value='%d'>%s</option>",
1749 $fline["id"], htmlspecialchars($fline["title"]));
1754 $default_is_cat = ($default_id == "CAT:0");
1755 $is_selected = $default_is_cat ?
"selected=\"1\"" : "";
1757 printf("<option $is_selected value='CAT:0'>%s</option>",
1758 __("Uncategorized"));
1760 $feed_result = db_query("SELECT id,title FROM ttrss_feeds
1761 WHERE cat_id IS NULL AND owner_uid = ".$_SESSION["uid"] . " ORDER BY title");
1763 while ($fline = db_fetch_assoc($feed_result)) {
1764 $is_selected = ($fline["id"] == $default_id && !$default_is_cat) ?
"selected=\"1\"" : "";
1766 $fline["title"] = " + " . $fline["title"];
1768 for ($i = 0; $i < $nest_level; $i++
)
1769 $fline["title"] = " - " . $fline["title"];
1771 printf("<option $is_selected value='%d'>%s</option>",
1772 $fline["id"], htmlspecialchars($fline["title"]));
1777 $result = db_query("SELECT id,title FROM ttrss_feeds
1778 WHERE owner_uid = ".$_SESSION["uid"]." ORDER BY title");
1780 while ($line = db_fetch_assoc($result)) {
1782 $is_selected = ($line["id"] == $default_id) ?
"selected=\"1\"" : "";
1784 printf("<option $is_selected value='%d'>%s</option>",
1785 $line["id"], htmlspecialchars($line["title"]));
1794 function print_feed_cat_select($id, $default_id,
1795 $attributes, $include_all_cats = true, $root_id = false, $nest_level = 0) {
1798 print "<select id=\"$id\" name=\"$id\" default=\"$default_id\" onchange=\"catSelectOnChange(this)\" $attributes>";
1802 $parent_qpart = "parent_cat = '$root_id'";
1804 $parent_qpart = "parent_cat IS NULL";
1806 $result = db_query("SELECT id,title,
1807 (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE
1808 c2.parent_cat = ttrss_feed_categories.id) AS num_children
1809 FROM ttrss_feed_categories
1810 WHERE owner_uid = ".$_SESSION["uid"]." AND $parent_qpart ORDER BY title");
1812 while ($line = db_fetch_assoc($result)) {
1813 if ($line["id"] == $default_id) {
1814 $is_selected = "selected=\"1\"";
1819 for ($i = 0; $i < $nest_level; $i++
)
1820 $line["title"] = " - " . $line["title"];
1823 printf("<option $is_selected value='%d'>%s</option>",
1824 $line["id"], htmlspecialchars($line["title"]));
1826 if ($line["num_children"] > 0)
1827 print_feed_cat_select($id, $default_id, $attributes,
1828 $include_all_cats, $line["id"], $nest_level+
1);
1832 if ($include_all_cats) {
1833 if (db_num_rows($result) > 0) {
1834 print "<option disabled=\"1\">--------</option>";
1837 if ($default_id == 0) {
1838 $is_selected = "selected=\"1\"";
1843 print "<option $is_selected value=\"0\">".__('Uncategorized')."</option>";
1849 function checkbox_to_sql_bool($val) {
1850 return ($val == "on") ?
"true" : "false";
1853 function getFeedCatTitle($id) {
1855 return __("Special");
1856 } else if ($id < LABEL_BASE_INDEX
) {
1857 return __("Labels");
1858 } else if ($id > 0) {
1859 $result = db_query("SELECT ttrss_feed_categories.title
1860 FROM ttrss_feeds, ttrss_feed_categories WHERE ttrss_feeds.id = '$id' AND
1861 cat_id = ttrss_feed_categories.id");
1862 if (db_num_rows($result) == 1) {
1863 return db_fetch_result($result, 0, "title");
1865 return __("Uncategorized");
1868 return "getFeedCatTitle($id) failed";
1873 function getFeedIcon($id) {
1876 return "images/archive.png";
1879 return "images/star.png";
1882 return "images/feed.png";
1885 return "images/fresh.png";
1888 return "images/folder.png";
1891 return "images/time.png";
1894 if ($id < LABEL_BASE_INDEX
) {
1895 return "images/label.png";
1897 if (file_exists(ICONS_DIR
. "/$id.ico"))
1898 return ICONS_URL
. "/$id.ico";
1906 function getFeedTitle($id, $cat = false) {
1908 return getCategoryTitle($id);
1909 } else if ($id == -1) {
1910 return __("Starred articles");
1911 } else if ($id == -2) {
1912 return __("Published articles");
1913 } else if ($id == -3) {
1914 return __("Fresh articles");
1915 } else if ($id == -4) {
1916 return __("All articles");
1917 } else if ($id === 0 ||
$id === "0") {
1918 return __("Archived articles");
1919 } else if ($id == -6) {
1920 return __("Recently read");
1921 } else if ($id < LABEL_BASE_INDEX
) {
1922 $label_id = feed_to_label_id($id);
1923 $result = db_query("SELECT caption FROM ttrss_labels2 WHERE id = '$label_id'");
1924 if (db_num_rows($result) == 1) {
1925 return db_fetch_result($result, 0, "caption");
1927 return "Unknown label ($label_id)";
1930 } else if (is_numeric($id) && $id > 0) {
1931 $result = db_query("SELECT title FROM ttrss_feeds WHERE id = '$id'");
1932 if (db_num_rows($result) == 1) {
1933 return db_fetch_result($result, 0, "title");
1935 return "Unknown feed ($id)";
1942 function make_init_params() {
1945 foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
1946 "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
1947 "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
1948 "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
1950 $params[strtolower($param)] = (int) get_pref($param);
1953 $params["icons_url"] = ICONS_URL
;
1954 $params["cookie_lifetime"] = SESSION_COOKIE_LIFETIME
;
1955 $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
1956 $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
1957 $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
1958 $params["bw_limit"] = (int) $_SESSION["bw_limit"];
1959 $params["label_base_index"] = (int) LABEL_BASE_INDEX
;
1961 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
1962 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
1964 $max_feed_id = db_fetch_result($result, 0, "mid");
1965 $num_feeds = db_fetch_result($result, 0, "nf");
1967 $params["max_feed_id"] = (int) $max_feed_id;
1968 $params["num_feeds"] = (int) $num_feeds;
1970 $params["hotkeys"] = get_hotkeys_map();
1972 $params["csrf_token"] = $_SESSION["csrf_token"];
1973 $params["widescreen"] = (int) $_COOKIE["ttrss_widescreen"];
1975 $params['simple_update'] = defined('SIMPLE_UPDATE_MODE') && SIMPLE_UPDATE_MODE
;
1980 function get_hotkeys_info() {
1982 __("Navigation") => array(
1983 "next_feed" => __("Open next feed"),
1984 "prev_feed" => __("Open previous feed"),
1985 "next_article" => __("Open next article"),
1986 "prev_article" => __("Open previous article"),
1987 "next_article_noscroll" => __("Open next article (don't scroll long articles)"),
1988 "prev_article_noscroll" => __("Open previous article (don't scroll long articles)"),
1989 "next_article_noexpand" => __("Move to next article (don't expand or mark read)"),
1990 "prev_article_noexpand" => __("Move to previous article (don't expand or mark read)"),
1991 "search_dialog" => __("Show search dialog")),
1992 __("Article") => array(
1993 "toggle_mark" => __("Toggle starred"),
1994 "toggle_publ" => __("Toggle published"),
1995 "toggle_unread" => __("Toggle unread"),
1996 "edit_tags" => __("Edit tags"),
1997 "dismiss_selected" => __("Dismiss selected"),
1998 "dismiss_read" => __("Dismiss read"),
1999 "open_in_new_window" => __("Open in new window"),
2000 "catchup_below" => __("Mark below as read"),
2001 "catchup_above" => __("Mark above as read"),
2002 "article_scroll_down" => __("Scroll down"),
2003 "article_scroll_up" => __("Scroll up"),
2004 "select_article_cursor" => __("Select article under cursor"),
2005 "email_article" => __("Email article"),
2006 "close_article" => __("Close/collapse article"),
2007 "toggle_expand" => __("Toggle article expansion (combined mode)"),
2008 "toggle_widescreen" => __("Toggle widescreen mode"),
2009 "toggle_embed_original" => __("Toggle embed original")),
2010 __("Article selection") => array(
2011 "select_all" => __("Select all articles"),
2012 "select_unread" => __("Select unread"),
2013 "select_marked" => __("Select starred"),
2014 "select_published" => __("Select published"),
2015 "select_invert" => __("Invert selection"),
2016 "select_none" => __("Deselect everything")),
2017 __("Feed") => array(
2018 "feed_refresh" => __("Refresh current feed"),
2019 "feed_unhide_read" => __("Un/hide read feeds"),
2020 "feed_subscribe" => __("Subscribe to feed"),
2021 "feed_edit" => __("Edit feed"),
2022 "feed_catchup" => __("Mark as read"),
2023 "feed_reverse" => __("Reverse headlines"),
2024 "feed_debug_update" => __("Debug feed update"),
2025 "catchup_all" => __("Mark all feeds as read"),
2026 "cat_toggle_collapse" => __("Un/collapse current category"),
2027 "toggle_combined_mode" => __("Toggle combined mode"),
2028 "toggle_cdm_expanded" => __("Toggle auto expand in combined mode")),
2029 __("Go to") => array(
2030 "goto_all" => __("All articles"),
2031 "goto_fresh" => __("Fresh"),
2032 "goto_marked" => __("Starred"),
2033 "goto_published" => __("Published"),
2034 "goto_tagcloud" => __("Tag cloud"),
2035 "goto_prefs" => __("Preferences")),
2036 __("Other") => array(
2037 "create_label" => __("Create label"),
2038 "create_filter" => __("Create filter"),
2039 "collapse_sidebar" => __("Un/collapse sidebar"),
2040 "help_dialog" => __("Show help dialog"))
2043 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_INFO
) as $plugin) {
2044 $hotkeys = $plugin->hook_hotkey_info($hotkeys);
2050 function get_hotkeys_map() {
2052 // "navigation" => array(
2055 "n" => "next_article",
2056 "p" => "prev_article",
2057 "(38)|up" => "prev_article",
2058 "(40)|down" => "next_article",
2059 // "^(38)|Ctrl-up" => "prev_article_noscroll",
2060 // "^(40)|Ctrl-down" => "next_article_noscroll",
2061 "(191)|/" => "search_dialog",
2062 // "article" => array(
2063 "s" => "toggle_mark",
2064 "*s" => "toggle_publ",
2065 "u" => "toggle_unread",
2066 "*t" => "edit_tags",
2067 "*d" => "dismiss_selected",
2068 "*x" => "dismiss_read",
2069 "o" => "open_in_new_window",
2070 "c p" => "catchup_below",
2071 "c n" => "catchup_above",
2072 "*n" => "article_scroll_down",
2073 "*p" => "article_scroll_up",
2074 "*(38)|Shift+up" => "article_scroll_up",
2075 "*(40)|Shift+down" => "article_scroll_down",
2076 "a *w" => "toggle_widescreen",
2077 "a e" => "toggle_embed_original",
2078 "e" => "email_article",
2079 "a q" => "close_article",
2080 // "article_selection" => array(
2081 "a a" => "select_all",
2082 "a u" => "select_unread",
2083 "a *u" => "select_marked",
2084 "a p" => "select_published",
2085 "a i" => "select_invert",
2086 "a n" => "select_none",
2088 "f r" => "feed_refresh",
2089 "f a" => "feed_unhide_read",
2090 "f s" => "feed_subscribe",
2091 "f e" => "feed_edit",
2092 "f q" => "feed_catchup",
2093 "f x" => "feed_reverse",
2094 "f *d" => "feed_debug_update",
2095 "f *c" => "toggle_combined_mode",
2096 "f c" => "toggle_cdm_expanded",
2097 "*q" => "catchup_all",
2098 "x" => "cat_toggle_collapse",
2100 "g a" => "goto_all",
2101 "g f" => "goto_fresh",
2102 "g s" => "goto_marked",
2103 "g p" => "goto_published",
2104 "g t" => "goto_tagcloud",
2105 "g *p" => "goto_prefs",
2106 // "other" => array(
2107 "(9)|Tab" => "select_article_cursor", // tab
2108 "c l" => "create_label",
2109 "c f" => "create_filter",
2110 "c s" => "collapse_sidebar",
2111 "^(191)|Ctrl+/" => "help_dialog",
2114 if (get_pref('COMBINED_DISPLAY_MODE')) {
2115 $hotkeys["^(38)|Ctrl-up"] = "prev_article_noscroll";
2116 $hotkeys["^(40)|Ctrl-down"] = "next_article_noscroll";
2119 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_HOTKEY_MAP
) as $plugin) {
2120 $hotkeys = $plugin->hook_hotkey_map($hotkeys);
2123 $prefixes = array();
2125 foreach (array_keys($hotkeys) as $hotkey) {
2126 $pair = explode(" ", $hotkey, 2);
2128 if (count($pair) > 1 && !in_array($pair[0], $prefixes)) {
2129 array_push($prefixes, $pair[0]);
2133 return array($prefixes, $hotkeys);
2136 function make_runtime_info() {
2139 $result = db_query("SELECT MAX(id) AS mid, COUNT(*) AS nf FROM
2140 ttrss_feeds WHERE owner_uid = " . $_SESSION["uid"]);
2142 $max_feed_id = db_fetch_result($result, 0, "mid");
2143 $num_feeds = db_fetch_result($result, 0, "nf");
2145 $data["max_feed_id"] = (int) $max_feed_id;
2146 $data["num_feeds"] = (int) $num_feeds;
2148 $data['last_article_id'] = getLastArticleId();
2149 $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
2151 $data['dep_ts'] = calculate_dep_timestamp();
2152 $data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
2154 if (file_exists(LOCK_DIRECTORY
. "/update_daemon.lock")) {
2156 $data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
2158 if (time() - $_SESSION["daemon_stamp_check"] > 30) {
2160 $stamp = (int) @file_get_contents
(LOCK_DIRECTORY
. "/update_daemon.stamp");
2163 $stamp_delta = time() - $stamp;
2165 if ($stamp_delta > 1800) {
2169 $_SESSION["daemon_stamp_check"] = time();
2172 $data['daemon_stamp_ok'] = $stamp_check;
2174 $stamp_fmt = date("Y.m.d, G:i", $stamp);
2176 $data['daemon_stamp'] = $stamp_fmt;
2181 if ($_SESSION["last_version_check"] +
86400 +
rand(-1000, 1000) < time()) {
2182 $new_version_details = @check_for_update
();
2184 $data['new_version_available'] = (int) ($new_version_details != false);
2186 $_SESSION["last_version_check"] = time();
2187 $_SESSION["version_data"] = $new_version_details;
2193 function search_to_sql($search) {
2195 $search_query_part = "";
2197 $keywords = explode(" ", $search);
2198 $query_keywords = array();
2200 foreach ($keywords as $k) {
2201 if (strpos($k, "-") === 0) {
2208 $commandpair = explode(":", mb_strtolower($k), 2);
2210 switch ($commandpair[0]) {
2212 if ($commandpair[1]) {
2213 array_push($query_keywords, "($not (LOWER(ttrss_entries.title) LIKE '%".
2214 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2216 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2217 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2221 if ($commandpair[1]) {
2222 array_push($query_keywords, "($not (LOWER(author) LIKE '%".
2223 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2225 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2226 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2230 if ($commandpair[1]) {
2231 if ($commandpair[1] == "true")
2232 array_push($query_keywords, "($not (note IS NOT NULL AND note != ''))");
2233 else if ($commandpair[1] == "false")
2234 array_push($query_keywords, "($not (note IS NULL OR note = ''))");
2236 array_push($query_keywords, "($not (LOWER(note) LIKE '%".
2237 db_escape_string(mb_strtolower($commandpair[1]))."%'))");
2239 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2240 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2245 if ($commandpair[1]) {
2246 if ($commandpair[1] == "true")
2247 array_push($query_keywords, "($not (marked = true))");
2249 array_push($query_keywords, "($not (marked = false))");
2251 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2252 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2256 if ($commandpair[1]) {
2257 if ($commandpair[1] == "true")
2258 array_push($query_keywords, "($not (published = true))");
2260 array_push($query_keywords, "($not (published = false))");
2263 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2264 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2268 if (strpos($k, "@") === 0) {
2270 $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
2271 $orig_ts = strtotime(substr($k, 1));
2272 $k = date("Y-m-d", convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
2274 //$k = date("Y-m-d", strtotime(substr($k, 1)));
2276 array_push($query_keywords, "(".SUBSTRING_FOR_DATE
."(updated,1,LENGTH('$k')) $not = '$k')");
2278 array_push($query_keywords, "(UPPER(ttrss_entries.title) $not LIKE UPPER('%$k%')
2279 OR UPPER(ttrss_entries.content) $not LIKE UPPER('%$k%'))");
2284 $search_query_part = implode("AND", $query_keywords);
2286 return $search_query_part;
2289 function getParentCategories($cat, $owner_uid) {
2292 $result = db_query("SELECT parent_cat FROM ttrss_feed_categories
2293 WHERE id = '$cat' AND parent_cat IS NOT NULL AND owner_uid = $owner_uid");
2295 while ($line = db_fetch_assoc($result)) {
2296 array_push($rv, $line["parent_cat"]);
2297 $rv = array_merge($rv, getParentCategories($line["parent_cat"], $owner_uid));
2303 function getChildCategories($cat, $owner_uid) {
2306 $result = db_query("SELECT id FROM ttrss_feed_categories
2307 WHERE parent_cat = '$cat' AND owner_uid = $owner_uid");
2309 while ($line = db_fetch_assoc($result)) {
2310 array_push($rv, $line["id"]);
2311 $rv = array_merge($rv, getChildCategories($line["id"], $owner_uid));
2317 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) {
2319 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2321 $ext_tables_part = "";
2325 if (SPHINX_ENABLED
) {
2326 $ids = join(",", @sphinx_search
($search, 0, 500));
2329 $search_query_part = "ref_id IN ($ids) AND ";
2331 $search_query_part = "ref_id = -1 AND ";
2334 $search_query_part = search_to_sql($search);
2335 $search_query_part .= " AND ";
2339 $search_query_part = "";
2344 if (DB_TYPE
== "pgsql") {
2345 $query_strategy_part .= " AND updated > NOW() - INTERVAL '14 days' ";
2347 $query_strategy_part .= " AND updated > DATE_SUB(NOW(), INTERVAL 14 DAY) ";
2350 $override_order = "updated DESC";
2352 $filter_query_part = filter_to_sql($filter, $owner_uid);
2354 // Try to check if SQL regexp implementation chokes on a valid regexp
2357 $result = db_query("SELECT true AS true_val FROM ttrss_entries,
2358 ttrss_user_entries, ttrss_feeds
2359 WHERE $filter_query_part LIMIT 1", false);
2362 $test = db_fetch_result($result, 0, "true_val");
2365 $filter_query_part = "false AND";
2367 $filter_query_part .= " AND";
2370 $filter_query_part = "false AND";
2374 $filter_query_part = "";
2378 $since_id_part = "ttrss_entries.id > $since_id AND ";
2380 $since_id_part = "";
2383 $view_query_part = "";
2385 if ($view_mode == "adaptive") {
2387 $view_query_part = " ";
2388 } else if ($feed != -1) {
2390 $unread = getFeedUnread($feed, $cat_view);
2392 if ($cat_view && $feed > 0 && $include_children)
2393 $unread +
= getCategoryChildrenUnread($feed);
2396 $view_query_part = " unread = true AND ";
2401 if ($view_mode == "marked") {
2402 $view_query_part = " marked = true AND ";
2405 if ($view_mode == "has_note") {
2406 $view_query_part = " (note IS NOT NULL AND note != '') AND ";
2409 if ($view_mode == "published") {
2410 $view_query_part = " published = true AND ";
2413 if ($view_mode == "unread" && $feed != -6) {
2414 $view_query_part = " unread = true AND ";
2418 $limit_query_part = "LIMIT " . $limit;
2421 $allow_archived = false;
2423 $vfeed_query_part = "";
2425 // override query strategy and enable feed display when searching globally
2426 if ($search && $search_mode == "all_feeds") {
2427 $query_strategy_part = "true";
2428 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2430 } else if (!is_numeric($feed)) {
2431 $query_strategy_part = "true";
2432 $vfeed_query_part = "(SELECT title FROM ttrss_feeds WHERE
2433 id = feed_id) as feed_title,";
2434 } else if ($search && $search_mode == "this_cat") {
2435 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2438 if ($include_children) {
2439 $subcats = getChildCategories($feed, $owner_uid);
2440 array_push($subcats, $feed);
2441 $cats_qpart = join(",", $subcats);
2443 $cats_qpart = $feed;
2446 $query_strategy_part = "ttrss_feeds.cat_id IN ($cats_qpart)";
2449 $query_strategy_part = "ttrss_feeds.cat_id IS NULL";
2452 } else if ($feed > 0) {
2457 if ($include_children) {
2459 $subcats = getChildCategories($feed, $owner_uid);
2461 array_push($subcats, $feed);
2462 $query_strategy_part = "cat_id IN (".
2463 implode(",", $subcats).")";
2466 $query_strategy_part = "cat_id = '$feed'";
2470 $query_strategy_part = "cat_id IS NULL";
2473 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2476 $query_strategy_part = "feed_id = '$feed'";
2478 } else if ($feed == 0 && !$cat_view) { // archive virtual feed
2479 $query_strategy_part = "feed_id IS NULL";
2480 $allow_archived = true;
2481 } else if ($feed == 0 && $cat_view) { // uncategorized
2482 $query_strategy_part = "cat_id IS NULL AND feed_id IS NOT NULL";
2483 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2484 } else if ($feed == -1) { // starred virtual feed
2485 $query_strategy_part = "marked = true";
2486 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2487 $allow_archived = true;
2489 if (!$override_order) {
2490 $override_order = "last_marked DESC, date_entered DESC, updated DESC";
2493 } else if ($feed == -2) { // published virtual feed OR labels category
2496 $query_strategy_part = "published = true";
2497 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2498 $allow_archived = true;
2500 if (!$override_order) {
2501 $override_order = "last_published DESC, date_entered DESC, updated DESC";
2505 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2507 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2509 $query_strategy_part = "ttrss_labels2.id = ttrss_user_labels2.label_id AND
2510 ttrss_user_labels2.article_id = ref_id";
2513 } else if ($feed == -6) { // recently read
2514 $query_strategy_part = "unread = false AND last_read IS NOT NULL";
2515 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2516 $allow_archived = true;
2518 if (!$override_order) $override_order = "last_read DESC";
2519 } else if ($feed == -3) { // fresh virtual feed
2520 $query_strategy_part = "unread = true AND score >= 0";
2522 $intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
2524 if (DB_TYPE
== "pgsql") {
2525 $query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
2527 $query_strategy_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
2530 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2531 } else if ($feed == -4) { // all articles virtual feed
2532 $allow_archived = true;
2533 $query_strategy_part = "true";
2534 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2535 } else if ($feed <= LABEL_BASE_INDEX
) { // labels
2536 $label_id = feed_to_label_id($feed);
2538 $query_strategy_part = "label_id = '$label_id' AND
2539 ttrss_labels2.id = ttrss_user_labels2.label_id AND
2540 ttrss_user_labels2.article_id = ref_id";
2542 $vfeed_query_part = "ttrss_feeds.title AS feed_title,";
2543 $ext_tables_part = ",ttrss_labels2,ttrss_user_labels2";
2544 $allow_archived = true;
2547 $query_strategy_part = "true";
2550 $order_by = "score DESC, date_entered DESC, updated DESC";
2552 if ($view_mode == "unread_first") {
2553 $order_by = "unread DESC, $order_by";
2556 if ($override_order) {
2557 $order_by = $override_order;
2563 $feed_title = T_sprintf("Search results: %s", $search);
2566 $feed_title = getCategoryTitle($feed);
2568 if (is_numeric($feed) && $feed > 0) {
2569 $result = db_query("SELECT title,site_url,last_error,last_updated
2570 FROM ttrss_feeds WHERE id = '$feed' AND owner_uid = $owner_uid");
2572 $feed_title = db_fetch_result($result, 0, "title");
2573 $feed_site_url = db_fetch_result($result, 0, "site_url");
2574 $last_error = db_fetch_result($result, 0, "last_error");
2575 $last_updated = db_fetch_result($result, 0, "last_updated");
2577 $feed_title = getFeedTitle($feed);
2582 $content_query_part = "content as content_preview, cached_content, ";
2584 if (is_numeric($feed)) {
2587 $feed_kind = "Feeds";
2589 $feed_kind = "Labels";
2592 if ($limit_query_part) {
2593 $offset_query_part = "OFFSET $offset";
2596 // proper override_order applied above
2597 if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
2598 if (!$override_order) {
2599 $order_by = "ttrss_feeds.title, $order_by";
2601 $order_by = "ttrss_feeds.title, $override_order";
2605 if (!$allow_archived) {
2606 $from_qpart = "ttrss_entries,ttrss_user_entries,ttrss_feeds$ext_tables_part";
2607 $feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
2610 $from_qpart = "ttrss_entries$ext_tables_part,ttrss_user_entries
2611 LEFT JOIN ttrss_feeds ON (feed_id = ttrss_feeds.id)";
2614 if ($vfeed_query_part)
2615 $vfeed_query_part .= "favicon_avg_color,";
2617 $query = "SELECT DISTINCT
2620 ttrss_entries.id,ttrss_entries.title,
2624 always_display_enclosures,
2631 unread,feed_id,marked,published,link,last_read,orig_feed_id,
2632 last_marked, last_published,
2640 ttrss_user_entries.ref_id = ttrss_entries.id AND
2641 ttrss_user_entries.owner_uid = '$owner_uid' AND
2646 $query_strategy_part ORDER BY $order_by
2647 $limit_query_part $offset_query_part";
2649 if ($_REQUEST["debug"]) print $query;
2651 $result = db_query($query);
2656 $select_qpart = "SELECT DISTINCT " .
2660 "ttrss_entries.id as id," .
2673 "(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) AS hide_images," .
2674 "last_marked, last_published, " .
2677 $content_query_part .
2680 $feed_kind = "Tags";
2681 $all_tags = explode(",", $feed);
2682 if ($search_mode == 'any') {
2683 $tag_sql = "tag_name in (" . implode(", ", array_map("db_quote", $all_tags)) . ")";
2684 $from_qpart = " FROM ttrss_entries,ttrss_user_entries,ttrss_tags ";
2685 $where_qpart = " WHERE " .
2686 "ref_id = ttrss_entries.id AND " .
2687 "ttrss_user_entries.owner_uid = $owner_uid AND " .
2688 "post_int_id = int_id AND $tag_sql AND " .
2690 $search_query_part .
2691 $query_strategy_part . " ORDER BY $order_by " .
2696 $sub_selects = array();
2697 $sub_ands = array();
2698 foreach ($all_tags as $term) {
2699 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");
2706 array_push($sub_ands, "A$x.post_int_id = A$y.post_int_id");
2711 array_push($sub_ands, "A1.post_int_id = ttrss_user_entries.int_id and ttrss_user_entries.owner_uid = $owner_uid");
2712 array_push($sub_ands, "ttrss_user_entries.ref_id = ttrss_entries.id");
2713 $from_qpart = " FROM " . implode(", ", $sub_selects) . ", ttrss_user_entries, ttrss_entries";
2714 $where_qpart = " WHERE " . implode(" AND ", $sub_ands);
2716 // error_log("TAG SQL: " . $tag_sql);
2717 // $tag_sql = "tag_name = '$feed'"; DEFAULT way
2719 // error_log("[". $select_qpart . "][" . $from_qpart . "][" .$where_qpart . "]");
2720 $result = db_query($select_qpart . $from_qpart . $where_qpart);
2723 return array($result, $feed_title, $feed_site_url, $last_error, $last_updated);
2727 function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false) {
2728 if (!$owner) $owner = $_SESSION["uid"];
2730 $res = trim($str); if (!$res) return '';
2732 if (strpos($res, "href=") === false)
2733 $res = rewrite_urls($res);
2735 $charset_hack = '<head>
2736 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
2739 $res = trim($res); if (!$res) return '';
2741 libxml_use_internal_errors(true);
2743 $doc = new DOMDocument();
2744 $doc->loadHTML($charset_hack . $res);
2745 $xpath = new DOMXPath($doc);
2747 $entries = $xpath->query('(//a[@href]|//img[@src])');
2749 foreach ($entries as $entry) {
2753 if ($entry->hasAttribute('href')) {
2754 $entry->setAttribute('href',
2755 rewrite_relative_url($site_url, $entry->getAttribute('href')));
2757 $entry->setAttribute('rel', 'noreferrer');
2760 if ($entry->hasAttribute('src')) {
2761 $src = rewrite_relative_url($site_url, $entry->getAttribute('src'));
2763 $cached_filename = CACHE_DIR
. '/images/' . sha1($src) . '.png';
2765 if (file_exists($cached_filename)) {
2766 $src = SELF_URL_PATH
. '/image.php?hash=' . sha1($src);
2769 $entry->setAttribute('src', $src);
2772 if ($entry->nodeName
== 'img') {
2773 if (($owner && get_pref("STRIP_IMAGES", $owner)) ||
2774 $force_remove_images ||
$_SESSION["bw_limit"]) {
2776 $p = $doc->createElement('p');
2778 $a = $doc->createElement('a');
2779 $a->setAttribute('href', $entry->getAttribute('src'));
2781 $a->appendChild(new DOMText($entry->getAttribute('src')));
2782 $a->setAttribute('target', '_blank');
2784 $p->appendChild($a);
2786 $entry->parentNode
->replaceChild($p, $entry);
2791 if (strtolower($entry->nodeName
) == "a") {
2792 $entry->setAttribute("target", "_blank");
2796 $entries = $xpath->query('//iframe');
2797 foreach ($entries as $entry) {
2798 $entry->setAttribute('sandbox', 'allow-scripts');
2802 $allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
2803 'b', 'bdi', 'bdo', 'big', 'blockquote', 'body', 'br',
2804 'caption', 'cite', 'center', 'code', 'col', 'colgroup',
2805 'data', 'dd', 'del', 'details', 'div', 'dl', 'font',
2806 'dt', 'em', 'footer', 'figure', 'figcaption',
2807 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'html', 'i',
2808 'img', 'ins', 'kbd', 'li', 'main', 'mark', 'nav', 'noscript',
2809 'ol', 'p', 'pre', 'q', 'ruby', 'rp', 'rt', 's', 'samp', 'section',
2810 'small', 'source', 'span', 'strike', 'strong', 'sub', 'summary',
2811 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'time',
2812 'tr', 'track', 'tt', 'u', 'ul', 'var', 'wbr', 'video' );
2814 if ($_SESSION['hasSandbox']) $allowed_elements[] = 'iframe';
2816 $disallowed_attributes = array('id', 'style', 'class');
2818 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_SANITIZE
) as $plugin) {
2819 $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
2820 if (is_array($retval)) {
2822 $allowed_elements = $retval[1];
2823 $disallowed_attributes = $retval[2];
2829 $doc->removeChild($doc->firstChild
); //remove doctype
2830 $doc = strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes);
2831 $res = $doc->saveHTML();
2835 function strip_harmful_tags($doc, $allowed_elements, $disallowed_attributes) {
2836 $xpath = new DOMXPath($doc);
2837 $entries = $xpath->query('//*');
2839 foreach ($entries as $entry) {
2840 if (!in_array($entry->nodeName
, $allowed_elements)) {
2841 $entry->parentNode
->removeChild($entry);
2844 if ($entry->hasAttributes()) {
2845 $attrs_to_remove = array();
2847 foreach ($entry->attributes
as $attr) {
2849 if (strpos($attr->nodeName
, 'on') === 0) {
2850 array_push($attrs_to_remove, $attr);
2853 if (in_array($attr->nodeName
, $disallowed_attributes)) {
2854 array_push($attrs_to_remove, $attr);
2858 foreach ($attrs_to_remove as $attr) {
2859 $entry->removeAttributeNode($attr);
2867 function check_for_update() {
2868 if (CHECK_FOR_NEW_VERSION
&& $_SESSION['access_level'] >= 10) {
2869 $version_url = "http://tt-rss.org/version.php?ver=" . VERSION
.
2870 "&iid=" . sha1(SELF_URL_PATH
);
2872 $version_data = @fetch_file_contents
($version_url);
2874 if ($version_data) {
2875 $version_data = json_decode($version_data, true);
2876 if ($version_data && $version_data['version']) {
2877 if (version_compare(VERSION_STATIC
, $version_data['version']) == -1) {
2878 return $version_data;
2886 function catchupArticlesById($ids, $cmode, $owner_uid = false) {
2888 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2889 if (count($ids) == 0) return;
2893 foreach ($ids as $id) {
2894 array_push($tmp_ids, "ref_id = '$id'");
2897 $ids_qpart = join(" OR ", $tmp_ids);
2900 db_query("UPDATE ttrss_user_entries SET
2901 unread = false,last_read = NOW()
2902 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2903 } else if ($cmode == 1) {
2904 db_query("UPDATE ttrss_user_entries SET
2906 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2908 db_query("UPDATE ttrss_user_entries SET
2909 unread = NOT unread,last_read = NOW()
2910 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2915 $result = db_query("SELECT DISTINCT feed_id FROM ttrss_user_entries
2916 WHERE ($ids_qpart) AND owner_uid = $owner_uid");
2918 while ($line = db_fetch_assoc($result)) {
2919 ccache_update($line["feed_id"], $owner_uid);
2923 function get_article_tags($id, $owner_uid = 0, $tag_cache = false) {
2925 $a_id = db_escape_string($id);
2927 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
2929 $query = "SELECT DISTINCT tag_name,
2930 owner_uid as owner FROM
2931 ttrss_tags WHERE post_int_id = (SELECT int_id FROM ttrss_user_entries WHERE
2932 ref_id = '$a_id' AND owner_uid = '$owner_uid' LIMIT 1) ORDER BY tag_name";
2936 /* check cache first */
2938 if ($tag_cache === false) {
2939 $result = db_query("SELECT tag_cache FROM ttrss_user_entries
2940 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
2942 $tag_cache = db_fetch_result($result, 0, "tag_cache");
2946 $tags = explode(",", $tag_cache);
2949 /* do it the hard way */
2951 $tmp_result = db_query($query);
2953 while ($tmp_line = db_fetch_assoc($tmp_result)) {
2954 array_push($tags, $tmp_line["tag_name"]);
2957 /* update the cache */
2959 $tags_str = db_escape_string(join(",", $tags));
2961 db_query("UPDATE ttrss_user_entries
2962 SET tag_cache = '$tags_str' WHERE ref_id = '$id'
2963 AND owner_uid = $owner_uid");
2969 function trim_array($array) {
2971 array_walk($tmp, 'trim');
2975 function tag_is_valid($tag) {
2976 if ($tag == '') return false;
2977 if (preg_match("/^[0-9]*$/", $tag)) return false;
2978 if (mb_strlen($tag) > 250) return false;
2980 if (function_exists('iconv')) {
2981 $tag = iconv("utf-8", "utf-8", $tag);
2984 if (!$tag) return false;
2989 function render_login_form() {
2990 header('Cache-Control: public');
2992 require_once "login_form.php";
2996 function format_warning($msg, $id = "") {
2998 return "<div class=\"warning\" id=\"$id\">
2999 <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
3002 function format_notice($msg, $id = "") {
3004 return "<div class=\"notice\" id=\"$id\">
3005 <span><img src=\"images/information.png\"></span><span>$msg</span></div>";
3008 function format_error($msg, $id = "") {
3010 return "<div class=\"error\" id=\"$id\">
3011 <span><img src=\"images/alert.png\"></span><span>$msg</span></div>";
3014 function print_notice($msg) {
3015 return print format_notice($msg);
3018 function print_warning($msg) {
3019 return print format_warning($msg);
3022 function print_error($msg) {
3023 return print format_error($msg);
3027 function T_sprintf() {
3028 $args = func_get_args();
3029 return vsprintf(__(array_shift($args)), $args);
3032 function format_inline_player($url, $ctype) {
3036 $url = htmlspecialchars($url);
3038 if (strpos($ctype, "audio/") === 0) {
3040 if ($_SESSION["hasAudio"] && (strpos($ctype, "ogg") !== false ||
3041 $_SESSION["hasMp3"])) {
3043 $entry .= "<audio preload=\"none\" controls>
3044 <source type=\"$ctype\" src=\"$url\"></source>
3049 $entry .= "<object type=\"application/x-shockwave-flash\"
3050 data=\"lib/button/musicplayer.swf?song_url=$url\"
3051 width=\"17\" height=\"17\" style='float : left; margin-right : 5px;'>
3052 <param name=\"movie\"
3053 value=\"lib/button/musicplayer.swf?song_url=$url\" />
3057 if ($entry) $entry .= " <a target=\"_blank\"
3058 href=\"$url\">" . basename($url) . "</a>";
3066 /* $filename = substr($url, strrpos($url, "/")+1);
3068 $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3069 $filename . " (" . $ctype . ")" . "</a>"; */
3073 function format_article($id, $mark_as_read = true, $zoom_mode = false, $owner_uid = false) {
3074 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3080 /* we can figure out feed_id from article id anyway, why do we
3081 * pass feed_id here? let's ignore the argument :(*/
3083 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3084 WHERE ref_id = '$id'");
3086 $feed_id = (int) db_fetch_result($result, 0, "feed_id");
3088 $rv['feed_id'] = $feed_id;
3090 //if (!$zoom_mode) { print "<article id='$id'><![CDATA["; };
3092 if ($mark_as_read) {
3093 $result = db_query("UPDATE ttrss_user_entries
3094 SET unread = false,last_read = NOW()
3095 WHERE ref_id = '$id' AND owner_uid = $owner_uid");
3097 ccache_update($feed_id, $owner_uid);
3100 $result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,
3101 ".SUBSTRING_FOR_DATE
."(updated,1,16) as updated,
3102 (SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
3103 (SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
3104 (SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
3111 FROM ttrss_entries,ttrss_user_entries
3112 WHERE id = '$id' AND ref_id = id AND owner_uid = $owner_uid");
3116 $line = db_fetch_assoc($result);
3118 $tag_cache = $line["tag_cache"];
3120 $line["tags"] = get_article_tags($id, $owner_uid, $line["tag_cache"]);
3121 unset($line["tag_cache"]);
3123 $line["content"] = sanitize($line["content"],
3124 sql_bool_to_bool($line['hide_images']),
3125 $owner_uid, $line["site_url"]);
3127 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_RENDER_ARTICLE
) as $p) {
3128 $line = $p->hook_render_article($line);
3131 $num_comments = $line["num_comments"];
3132 $entry_comments = "";
3134 if ($num_comments > 0) {
3135 if ($line["comments"]) {
3136 $comments_url = htmlspecialchars($line["comments"]);
3138 $comments_url = htmlspecialchars($line["link"]);
3140 $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
3142 if ($line["comments"] && $line["link"] != $line["comments"]) {
3143 $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
3148 header("Content-Type: text/html");
3149 $rv['content'] .= "<html><head>
3150 <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
3151 <title>Tiny Tiny RSS - ".$line["title"]."</title>
3152 <link rel=\"stylesheet\" type=\"text/css\" href=\"css/tt-rss.css\">
3153 </head><body id=\"ttrssZoom\">";
3156 $rv['content'] .= "<div class=\"postReply\" id=\"POST-$id\">";
3158 $rv['content'] .= "<div class=\"postHeader\" id=\"POSTHDR-$id\">";
3160 $entry_author = $line["author"];
3162 if ($entry_author) {
3163 $entry_author = __(" - ") . $entry_author;
3166 $parsed_updated = make_local_datetime($line["updated"], true,
3169 $rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
3171 if ($line["link"]) {
3172 $rv['content'] .= "<div class='postTitle'><a target='_blank'
3173 title=\"".htmlspecialchars($line['title'])."\"
3175 htmlspecialchars($line["link"]) . "\">" .
3176 $line["title"] . "</a>" .
3177 "<span class='author'>$entry_author</span></div>";
3179 $rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
3182 $tags_str = format_tags_string($line["tags"], $id);
3183 $tags_str_full = join(", ", $line["tags"]);
3185 if (!$tags_str_full) $tags_str_full = __("no tags");
3187 if (!$entry_comments) $entry_comments = " "; # placeholder
3189 $rv['content'] .= "<div class='postTags' style='float : right'>
3190 <img src='images/tag.png'
3191 class='tagsPic' alt='Tags' title='Tags'> ";
3194 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>
3195 <a title=\"".__('Edit tags for this article')."\"
3196 href=\"#\" onclick=\"editArticleTags($id, $feed_id)\">(+)</a>";
3198 $rv['content'] .= "<div dojoType=\"dijit.Tooltip\"
3199 id=\"ATSTRTIP-$id\" connectId=\"ATSTR-$id\"
3200 position=\"below\">$tags_str_full</div>";
3202 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_BUTTON
) as $p) {
3203 $rv['content'] .= $p->hook_article_button($line);
3207 $tags_str = strip_tags($tags_str);
3208 $rv['content'] .= "<span id=\"ATSTR-$id\">$tags_str</span>";
3210 $rv['content'] .= "</div>";
3211 $rv['content'] .= "<div clear='both'>";
3213 foreach (PluginHost
::getInstance()->get_hooks(PluginHost
::HOOK_ARTICLE_LEFT_BUTTON
) as $p) {
3214 $rv['content'] .= $p->hook_article_left_button($line);
3217 $rv['content'] .= "$entry_comments</div>";
3219 if ($line["orig_feed_id"]) {
3221 $tmp_result = db_query("SELECT * FROM ttrss_archived_feeds
3222 WHERE id = ".$line["orig_feed_id"]);
3224 if (db_num_rows($tmp_result) != 0) {
3226 $rv['content'] .= "<div clear='both'>";
3227 $rv['content'] .= __("Originally from:");
3229 $rv['content'] .= " ";
3231 $tmp_line = db_fetch_assoc($tmp_result);
3233 $rv['content'] .= "<a target='_blank'
3234 href=' " . htmlspecialchars($tmp_line['site_url']) . "'>" .
3235 $tmp_line['title'] . "</a>";
3237 $rv['content'] .= " ";
3239 $rv['content'] .= "<a target='_blank' href='" . htmlspecialchars($tmp_line['feed_url']) . "'>";
3240 $rv['content'] .= "<img title='".__('Feed URL')."'class='tinyFeedIcon' src='images/pub_set.svg'></a>";
3242 $rv['content'] .= "</div>";
3246 $rv['content'] .= "</div>";
3248 $rv['content'] .= "<div id=\"POSTNOTE-$id\">";
3249 if ($line['note']) {
3250 $rv['content'] .= format_article_note($id, $line['note'], !$zoom_mode);
3252 $rv['content'] .= "</div>";
3254 $rv['content'] .= "<div class=\"postContent\">";
3256 $rv['content'] .= $line["content"];
3257 $rv['content'] .= format_article_enclosures($id,
3258 sql_bool_to_bool($line["always_display_enclosures"]),
3260 sql_bool_to_bool($line["hide_images"]));
3262 $rv['content'] .= "</div>";
3264 $rv['content'] .= "</div>";
3270 <div class='footer'>
3271 <button onclick=\"return window.close()\">".
3272 __("Close this window")."</button></div>";
3273 $rv['content'] .= "</body></html>";
3280 function print_checkpoint($n, $s) {
3281 $ts = microtime(true);
3282 echo sprintf("<!-- CP[$n] %.4f seconds -->\n", $ts - $s);
3286 function sanitize_tag($tag) {
3289 $tag = mb_strtolower($tag, 'utf-8');
3291 $tag = preg_replace('/[\'\"\+\>\<]/', "", $tag);
3293 // $tag = str_replace('"', "", $tag);
3294 // $tag = str_replace("+", " ", $tag);
3295 $tag = str_replace("technorati tag: ", "", $tag);
3300 function get_self_url_prefix() {
3301 if (strrpos(SELF_URL_PATH
, "/") === strlen(SELF_URL_PATH
)-1) {
3302 return substr(SELF_URL_PATH
, 0, strlen(SELF_URL_PATH
)-1);
3304 return SELF_URL_PATH
;
3309 * Compute the Mozilla Firefox feed adding URL from server HOST and REQUEST_URI.
3311 * @return string The Mozilla Firefox feed adding URL.
3313 function add_feed_url() {
3314 //$url_path = ($_SERVER['HTTPS'] != "on" ? 'http://' : 'https://') . $_SERVER["HTTP_HOST"] . parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
3316 $url_path = get_self_url_prefix() .
3317 "/public.php?op=subscribe&feed_url=%s";
3319 } // function add_feed_url
3321 function encrypt_password($pass, $salt = '', $mode2 = false) {
3322 if ($salt && $mode2) {
3323 return "MODE2:" . hash('sha256', $salt . $pass);
3325 return "SHA1X:" . sha1("$salt:$pass");
3327 return "SHA1:" . sha1($pass);
3329 } // function encrypt_password
3331 function load_filters($feed_id, $owner_uid, $action_id = false) {
3334 $cat_id = (int)getFeedCategory($feed_id);
3336 $result = db_query("SELECT * FROM ttrss_filters2 WHERE
3337 owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
3339 $check_cats = join(",", array_merge(
3340 getParentCategories($cat_id, $owner_uid),
3343 while ($line = db_fetch_assoc($result)) {
3344 $filter_id = $line["id"];
3346 $result2 = db_query("SELECT
3347 r.reg_exp, r.inverse, r.feed_id, r.cat_id, r.cat_filter, t.name AS type_name
3348 FROM ttrss_filters2_rules AS r,
3349 ttrss_filter_types AS t
3351 (cat_id IS NULL OR cat_id IN ($check_cats)) AND
3352 (feed_id IS NULL OR feed_id = '$feed_id') AND
3353 filter_type = t.id AND filter_id = '$filter_id'");
3358 while ($rule_line = db_fetch_assoc($result2)) {
3359 # print_r($rule_line);
3362 $rule["reg_exp"] = $rule_line["reg_exp"];
3363 $rule["type"] = $rule_line["type_name"];
3364 $rule["inverse"] = sql_bool_to_bool($rule_line["inverse"]);
3366 array_push($rules, $rule);
3369 $result2 = db_query("SELECT a.action_param,t.name AS type_name
3370 FROM ttrss_filters2_actions AS a,
3371 ttrss_filter_actions AS t
3373 action_id = t.id AND filter_id = '$filter_id'");
3375 while ($action_line = db_fetch_assoc($result2)) {
3376 # print_r($action_line);
3379 $action["type"] = $action_line["type_name"];
3380 $action["param"] = $action_line["action_param"];
3382 array_push($actions, $action);
3387 $filter["match_any_rule"] = sql_bool_to_bool($line["match_any_rule"]);
3388 $filter["inverse"] = sql_bool_to_bool($line["inverse"]);
3389 $filter["rules"] = $rules;
3390 $filter["actions"] = $actions;
3392 if (count($rules) > 0 && count($actions) > 0) {
3393 array_push($filters, $filter);
3400 function get_score_pic($score) {
3402 return "score_high.png";
3403 } else if ($score > 0) {
3404 return "score_half_high.png";
3405 } else if ($score < -100) {
3406 return "score_low.png";
3407 } else if ($score < 0) {
3408 return "score_half_low.png";
3410 return "score_neutral.png";
3414 function feed_has_icon($id) {
3415 return is_file(ICONS_DIR
. "/$id.ico") && filesize(ICONS_DIR
. "/$id.ico") > 0;
3418 function init_plugins() {
3419 PluginHost
::getInstance()->load(PLUGINS
, PluginHost
::KIND_ALL
);
3424 function format_tags_string($tags, $id) {
3425 if (!is_array($tags) ||
count($tags) == 0) {
3426 return __("no tags");
3428 $maxtags = min(5, count($tags));
3430 for ($i = 0; $i < $maxtags; $i++
) {
3431 $tags_str .= "<a class=\"tag\" href=\"#\" onclick=\"viewfeed('".$tags[$i]."')\">" . $tags[$i] . "</a>, ";
3434 $tags_str = mb_substr($tags_str, 0, mb_strlen($tags_str)-2);
3436 if (count($tags) > $maxtags)
3437 $tags_str .= ", …";
3443 function format_article_labels($labels, $id) {
3445 if (!is_array($labels)) return '';
3449 foreach ($labels as $l) {
3450 $labels_str .= sprintf("<span class='hlLabelRef'
3451 style='color : %s; background-color : %s'>%s</span>",
3452 $l[2], $l[3], $l[1]);
3459 function format_article_note($id, $note, $allow_edit = true) {
3461 $str = "<div class='articleNote' onclick=\"editArticleNote($id)\">
3462 <div class='noteEdit' onclick=\"editArticleNote($id)\">".
3463 ($allow_edit ?
__('(edit note)') : "")."</div>$note</div>";
3469 function get_feed_category($feed_cat, $parent_cat_id = false) {
3470 if ($parent_cat_id) {
3471 $parent_qpart = "parent_cat = '$parent_cat_id'";
3472 $parent_insert = "'$parent_cat_id'";
3474 $parent_qpart = "parent_cat IS NULL";
3475 $parent_insert = "NULL";
3479 "SELECT id FROM ttrss_feed_categories
3480 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3482 if (db_num_rows($result) == 0) {
3485 return db_fetch_result($result, 0, "id");
3489 function add_feed_category($feed_cat, $parent_cat_id = false) {
3491 if (!$feed_cat) return false;
3495 if ($parent_cat_id) {
3496 $parent_qpart = "parent_cat = '$parent_cat_id'";
3497 $parent_insert = "'$parent_cat_id'";
3499 $parent_qpart = "parent_cat IS NULL";
3500 $parent_insert = "NULL";
3503 $feed_cat = mb_substr($feed_cat, 0, 250);
3506 "SELECT id FROM ttrss_feed_categories
3507 WHERE $parent_qpart AND title = '$feed_cat' AND owner_uid = ".$_SESSION["uid"]);
3509 if (db_num_rows($result) == 0) {
3512 "INSERT INTO ttrss_feed_categories (owner_uid,title,parent_cat)
3513 VALUES ('".$_SESSION["uid"]."', '$feed_cat', $parent_insert)");
3523 function getArticleFeed($id) {
3524 $result = db_query("SELECT feed_id FROM ttrss_user_entries
3525 WHERE ref_id = '$id' AND owner_uid = " . $_SESSION["uid"]);
3527 if (db_num_rows($result) != 0) {
3528 return db_fetch_result($result, 0, "feed_id");
3535 * Fixes incomplete URLs by prepending "http://".
3536 * Also replaces feed:// with http://, and
3537 * prepends a trailing slash if the url is a domain name only.
3539 * @param string $url Possibly incomplete URL
3541 * @return string Fixed URL.
3543 function fix_url($url) {
3544 if (strpos($url, '://') === false) {
3545 $url = 'http://' . $url;
3546 } else if (substr($url, 0, 5) == 'feed:') {
3547 $url = 'http:' . substr($url, 5);
3550 //prepend slash if the URL has no slash in it
3551 // "http://www.example" -> "http://www.example/"
3552 if (strpos($url, '/', strpos($url, ':') +
3) === false) {
3556 if ($url != "http:///")
3562 function validate_feed_url($url) {
3563 $parts = parse_url($url);
3565 return ($parts['scheme'] == 'http' ||
$parts['scheme'] == 'feed' ||
$parts['scheme'] == 'https');
3569 function get_article_enclosures($id) {
3571 $query = "SELECT * FROM ttrss_enclosures
3572 WHERE post_id = '$id' AND content_url != ''";
3576 $result = db_query($query);
3578 if (db_num_rows($result) > 0) {
3579 while ($line = db_fetch_assoc($result)) {
3580 array_push($rv, $line);
3587 function save_email_address($email) {
3588 // FIXME: implement persistent storage of emails
3590 if (!$_SESSION['stored_emails'])
3591 $_SESSION['stored_emails'] = array();
3593 if (!in_array($email, $_SESSION['stored_emails']))
3594 array_push($_SESSION['stored_emails'], $email);
3598 function get_feed_access_key($feed_id, $is_cat, $owner_uid = false) {
3600 if (!$owner_uid) $owner_uid = $_SESSION["uid"];
3602 $sql_is_cat = bool_to_sql_bool($is_cat);
3604 $result = db_query("SELECT access_key FROM ttrss_access_keys
3605 WHERE feed_id = '$feed_id' AND is_cat = $sql_is_cat
3606 AND owner_uid = " . $owner_uid);
3608 if (db_num_rows($result) == 1) {
3609 return db_fetch_result($result, 0, "access_key");
3611 $key = db_escape_string(sha1(uniqid(rand(), true)));
3613 $result = db_query("INSERT INTO ttrss_access_keys
3614 (access_key, feed_id, is_cat, owner_uid)
3615 VALUES ('$key', '$feed_id', $sql_is_cat, '$owner_uid')");
3622 function get_feeds_from_html($url, $content)
3624 $url = fix_url($url);
3625 $baseUrl = substr($url, 0, strrpos($url, '/') +
1);
3627 libxml_use_internal_errors(true);
3629 $doc = new DOMDocument();
3630 $doc->loadHTML($content);
3631 $xpath = new DOMXPath($doc);
3632 $entries = $xpath->query('/html/head/link[@rel="alternate"]');
3633 $feedUrls = array();
3634 foreach ($entries as $entry) {
3635 if ($entry->hasAttribute('href')) {
3636 $title = $entry->getAttribute('title');
3638 $title = $entry->getAttribute('type');
3640 $feedUrl = rewrite_relative_url(
3641 $baseUrl, $entry->getAttribute('href')
3643 $feedUrls[$feedUrl] = $title;
3649 function is_html($content) {
3650 return preg_match("/<html|DOCTYPE html/i", substr($content, 0, 20)) !== 0;
3653 function url_is_html($url, $login = false, $pass = false) {
3654 return is_html(fetch_file_contents($url, false, $login, $pass));
3657 function print_label_select($name, $value, $attributes = "") {
3659 $result = db_query("SELECT caption FROM ttrss_labels2
3660 WHERE owner_uid = '".$_SESSION["uid"]."' ORDER BY caption");
3662 print "<select default=\"$value\" name=\"" . htmlspecialchars($name) .
3663 "\" $attributes onchange=\"labelSelectOnChange(this)\" >";
3665 while ($line = db_fetch_assoc($result)) {
3667 $issel = ($line["caption"] == $value) ?
"selected=\"1\"" : "";
3669 print "<option value=\"".htmlspecialchars($line["caption"])."\"
3670 $issel>" . htmlspecialchars($line["caption"]) . "</option>";
3674 # print "<option value=\"ADD_LABEL\">" .__("Add label...") . "</option>";
3681 function format_article_enclosures($id, $always_display_enclosures,
3682 $article_content, $hide_images = false) {
3684 $result = get_article_enclosures($id);
3687 if (count($result) > 0) {
3689 $entries_html = array();
3691 $entries_inline = array();
3693 foreach ($result as $line) {
3695 $url = $line["content_url"];
3696 $ctype = $line["content_type"];
3698 if (!$ctype) $ctype = __("unknown type");
3700 $filename = substr($url, strrpos($url, "/")+
1);
3702 $player = format_inline_player($url, $ctype);
3704 if ($player) array_push($entries_inline, $player);
3706 # $entry .= " <a target=\"_blank\" href=\"" . htmlspecialchars($url) . "\">" .
3707 # $filename . " (" . $ctype . ")" . "</a>";
3709 $entry = "<div onclick=\"window.open('".htmlspecialchars($url)."')\"
3710 dojoType=\"dijit.MenuItem\">$filename ($ctype)</div>";
3712 array_push($entries_html, $entry);
3716 $entry["type"] = $ctype;
3717 $entry["filename"] = $filename;
3718 $entry["url"] = $url;
3720 array_push($entries, $entry);
3723 if ($_SESSION['uid'] && !get_pref("STRIP_IMAGES") && !$_SESSION["bw_limit"]) {
3724 if ($always_display_enclosures ||
3725 !preg_match("/<img/i", $article_content)) {
3727 foreach ($entries as $entry) {
3729 if (preg_match("/image/", $entry["type"]) ||
3730 preg_match("/\.(jpg|png|gif|bmp)/i", $entry["filename"])) {
3732 if (!$hide_images) {
3734 alt=\"".htmlspecialchars($entry["filename"])."\"
3735 src=\"" .htmlspecialchars($entry["url"]) . "\"/></p>";
3737 $rv .= "<p><a target=\"_blank\"
3738 href=\"".htmlspecialchars($entry["url"])."\"
3739 >" .htmlspecialchars($entry["url"]) . "</a></p>";
3747 if (count($entries_inline) > 0) {
3748 $rv .= "<hr clear='both'/>";
3749 foreach ($entries_inline as $entry) { $rv .= $entry; };
3750 $rv .= "<hr clear='both'/>";
3753 $rv .= "<select class=\"attachments\" onchange=\"openSelectedAttachment(this)\">".
3754 "<option value=''>" . __('Attachments')."</option>";
3756 foreach ($entries as $entry) {
3757 $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
3767 function getLastArticleId() {
3768 $result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
3769 WHERE owner_uid = " . $_SESSION["uid"]);
3771 if (db_num_rows($result) == 1) {
3772 return db_fetch_result($result, 0, "id");
3778 function build_url($parts) {
3779 return $parts['scheme'] . "://" . $parts['host'] . $parts['path'];
3783 * Converts a (possibly) relative URL to a absolute one.
3785 * @param string $url Base URL (i.e. from where the document is)
3786 * @param string $rel_url Possibly relative URL in the document
3788 * @return string Absolute URL
3790 function rewrite_relative_url($url, $rel_url) {
3791 if (strpos($rel_url, ":") !== false) {
3793 } else if (strpos($rel_url, "://") !== false) {
3795 } else if (strpos($rel_url, "//") === 0) {
3796 # protocol-relative URL (rare but they exist)
3798 } else if (strpos($rel_url, "/") === 0)
3800 $parts = parse_url($url);
3801 $parts['path'] = $rel_url;
3803 return build_url($parts);
3806 $parts = parse_url($url);
3807 if (!isset($parts['path'])) {
3808 $parts['path'] = '/';
3810 $dir = $parts['path'];
3811 if (substr($dir, -1) !== '/') {
3812 $dir = dirname($parts['path']);
3813 $dir !== '/' && $dir .= '/';
3815 $parts['path'] = $dir . $rel_url;
3817 return build_url($parts);
3821 function sphinx_search($query, $offset = 0, $limit = 30) {
3822 require_once 'lib/sphinxapi.php';
3824 $sphinxClient = new SphinxClient();
3826 $sphinxpair = explode(":", SPHINX_SERVER
, 2);
3828 $sphinxClient->SetServer($sphinxpair[0], (int)$sphinxpair[1]);
3829 $sphinxClient->SetConnectTimeout(1);
3831 $sphinxClient->SetFieldWeights(array('title' => 70, 'content' => 30,
3832 'feed_title' => 20));
3834 $sphinxClient->SetMatchMode(SPH_MATCH_EXTENDED2
);
3835 $sphinxClient->SetRankingMode(SPH_RANK_PROXIMITY_BM25
);
3836 $sphinxClient->SetLimits($offset, $limit, 1000);
3837 $sphinxClient->SetArrayResult(false);
3838 $sphinxClient->SetFilter('owner_uid', array($_SESSION['uid']));
3840 $result = $sphinxClient->Query($query, SPHINX_INDEX
);
3844 if (is_array($result['matches'])) {
3845 foreach (array_keys($result['matches']) as $int_id) {
3846 $ref_id = $result['matches'][$int_id]['attrs']['ref_id'];
3847 array_push($ids, $ref_id);
3854 function cleanup_tags($days = 14, $limit = 1000) {
3856 if (DB_TYPE
== "pgsql") {
3857 $interval_query = "date_updated < NOW() - INTERVAL '$days days'";
3858 } else if (DB_TYPE
== "mysql") {
3859 $interval_query = "date_updated < DATE_SUB(NOW(), INTERVAL $days DAY)";
3864 while ($limit > 0) {
3867 $query = "SELECT ttrss_tags.id AS id
3868 FROM ttrss_tags, ttrss_user_entries, ttrss_entries
3869 WHERE post_int_id = int_id AND $interval_query AND
3870 ref_id = ttrss_entries.id AND tag_cache != '' LIMIT $limit_part";
3872 $result = db_query($query);
3876 while ($line = db_fetch_assoc($result)) {
3877 array_push($ids, $line['id']);
3880 if (count($ids) > 0) {
3881 $ids = join(",", $ids);
3883 $tmp_result = db_query("DELETE FROM ttrss_tags WHERE id IN ($ids)");
3884 $tags_deleted +
= db_affected_rows($tmp_result);
3889 $limit -= $limit_part;
3892 return $tags_deleted;
3895 function print_user_stylesheet() {
3896 $value = get_pref('USER_STYLESHEET');
3899 print "<style type=\"text/css\">";
3900 print str_replace("<br/>", "\n", $value);
3906 function rewrite_urls($html) {
3907 libxml_use_internal_errors(true);
3909 $charset_hack = '<head>
3910 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
3913 $doc = new DOMDocument();
3914 $doc->loadHTML($charset_hack . $html);
3915 $xpath = new DOMXPath($doc);
3917 $entries = $xpath->query('//*/text()');
3919 foreach ($entries as $entry) {
3920 if (strstr($entry->wholeText
, "://") !== false) {
3921 $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
3922 "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText
);
3924 if ($text != $entry->wholeText
) {
3925 $cdoc = new DOMDocument();
3926 $cdoc->loadHTML($charset_hack . $text);
3929 foreach ($cdoc->childNodes
as $cnode) {
3930 $cnode = $doc->importNode($cnode, true);
3933 $entry->parentNode
->insertBefore($cnode);
3937 $entry->parentNode
->removeChild($entry);
3943 $node = $doc->getElementsByTagName('body')->item(0);
3945 // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
3947 return $doc->saveXML($node);
3952 function filter_to_sql($filter, $owner_uid) {
3955 if (DB_TYPE
== "pgsql")
3958 $reg_qpart = "REGEXP";
3960 foreach ($filter["rules"] AS $rule) {
3961 $rule['reg_exp'] = str_replace('/', '\/', $rule["reg_exp"]);
3962 $regexp_valid = preg_match('/' . $rule['reg_exp'] . '/',
3963 $rule['reg_exp']) !== FALSE;
3965 if ($regexp_valid) {
3967 $rule['reg_exp'] = db_escape_string($rule['reg_exp']);
3969 switch ($rule["type"]) {
3971 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3972 $rule['reg_exp'] . "')";
3975 $qpart = "LOWER(ttrss_entries.content) $reg_qpart LOWER('".
3976 $rule['reg_exp'] . "')";
3979 $qpart = "LOWER(ttrss_entries.title) $reg_qpart LOWER('".
3980 $rule['reg_exp'] . "') OR LOWER(" .
3981 "ttrss_entries.content) $reg_qpart LOWER('" . $rule['reg_exp'] . "')";
3984 $qpart = "LOWER(ttrss_user_entries.tag_cache) $reg_qpart LOWER('".
3985 $rule['reg_exp'] . "')";
3988 $qpart = "LOWER(ttrss_entries.link) $reg_qpart LOWER('".
3989 $rule['reg_exp'] . "')";
3992 $qpart = "LOWER(ttrss_entries.author) $reg_qpart LOWER('".
3993 $rule['reg_exp'] . "')";
3997 if (isset($rule['inverse'])) $qpart = "NOT ($qpart)";
3999 if (isset($rule["feed_id"]) && $rule["feed_id"] > 0) {
4000 $qpart .= " AND feed_id = " . db_escape_string($rule["feed_id"]);
4003 if (isset($rule["cat_id"])) {
4005 if ($rule["cat_id"] > 0) {
4006 $children = getChildCategories($rule["cat_id"], $owner_uid);
4007 array_push($children, $rule["cat_id"]);
4009 $children = join(",", $children);
4011 $cat_qpart = "cat_id IN ($children)";
4013 $cat_qpart = "cat_id IS NULL";
4016 $qpart .= " AND $cat_qpart";
4019 array_push($query, "($qpart)");
4024 if (count($query) > 0) {
4025 $fullquery = "(" . join($filter["match_any_rule"] ?
"OR" : "AND", $query) . ")";
4027 $fullquery = "(false)";
4030 if ($filter['inverse']) $fullquery = "(NOT $fullquery)";
4035 if (!function_exists('gzdecode')) {
4036 function gzdecode($string) { // no support for 2nd argument
4037 return file_get_contents('compress.zlib://data:who/cares;base64,'.
4038 base64_encode($string));
4042 function get_random_bytes($length) {
4043 if (function_exists('openssl_random_pseudo_bytes')) {
4044 return openssl_random_pseudo_bytes($length);
4048 for ($i = 0; $i < $length; $i++
)
4049 $output .= chr(mt_rand(0, 255));
4055 function read_stdin() {
4056 $fp = fopen("php://stdin", "r");
4059 $line = trim(fgets($fp));
4067 function tmpdirname($path, $prefix) {
4068 // Use PHP's tmpfile function to create a temporary
4069 // directory name. Delete the file and keep the name.
4070 $tempname = tempnam($path,$prefix);
4074 if (!unlink($tempname))
4080 function getFeedCategory($feed) {
4081 $result = db_query("SELECT cat_id FROM ttrss_feeds
4082 WHERE id = '$feed'");
4084 if (db_num_rows($result) > 0) {
4085 return db_fetch_result($result, 0, "cat_id");
4092 function implements_interface($class, $interface) {
4093 return in_array($interface, class_implements($class));
4096 function geturl($url){
4098 if (!function_exists('curl_init'))
4099 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
);
4101 $curl = curl_init();
4102 $header[0] = "Accept: text/xml,application/xml,application/xhtml+xml,";
4103 $header[0] .= "text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
4104 $header[] = "Cache-Control: max-age=0";
4105 $header[] = "Connection: keep-alive";
4106 $header[] = "Keep-Alive: 300";
4107 $header[] = "Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7";
4108 $header[] = "Accept-Language: en-us,en;q=0.5";
4109 $header[] = "Pragma: ";
4111 curl_setopt($curl, CURLOPT_URL
, $url);
4112 curl_setopt($curl, CURLOPT_USERAGENT
, 'Mozilla/5.0 (Windows NT 5.1; rv:5.0) Gecko/20100101 Firefox/5.0 Firefox/5.0');
4113 curl_setopt($curl, CURLOPT_HTTPHEADER
, $header);
4114 curl_setopt($curl, CURLOPT_HEADER
, true);
4115 curl_setopt($curl, CURLOPT_REFERER
, $url);
4116 curl_setopt($curl, CURLOPT_ENCODING
, 'gzip,deflate');
4117 curl_setopt($curl, CURLOPT_AUTOREFERER
, true);
4118 curl_setopt($curl, CURLOPT_RETURNTRANSFER
, true);
4119 //curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); //CURLOPT_FOLLOWLOCATION Disabled...
4120 curl_setopt($curl, CURLOPT_TIMEOUT
, 60);
4121 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER
, false);
4123 $html = curl_exec($curl);
4125 $status = curl_getinfo($curl);
4127 if($status['http_code']!=200){
4128 if($status['http_code'] == 301 ||
$status['http_code'] == 302) {
4130 list($header) = explode("\r\n\r\n", $html, 2);
4132 preg_match("/(Location:|URI:)[^(\n)]*/", $header, $matches);
4133 $url = trim(str_replace($matches[1],"",$matches[0]));
4134 $url_parsed = parse_url($url);
4135 return (isset($url_parsed))?
geturl($url):'';
4138 global $fetch_last_error;
4140 $fetch_last_error = curl_errno($curl) . " " . curl_error($curl);
4144 foreach($status as $key=>$eline){$oline.='['.$key.']'.$eline.' ';}
4145 $line =$oline." \r\n ".$url."\r\n-----------------\r\n";
4146 # $handle = @fopen('./curl.error.log', 'a');
4147 # fwrite($handle, $line);
4154 function get_minified_js($files) {
4155 require_once 'lib/jshrink/Minifier.php';
4159 foreach ($files as $js) {
4160 if (!isset($_GET['debug'])) {
4161 $cached_file = CACHE_DIR
. "/js/".basename($js).".js";
4163 if (file_exists($cached_file) &&
4164 is_readable($cached_file) &&
4165 filemtime($cached_file) >= filemtime("js/$js.js")) {
4167 $rv .= file_get_contents($cached_file);
4170 $minified = JShrink\Minifier
::minify(file_get_contents("js/$js.js"));
4171 file_put_contents($cached_file, $minified);
4175 $rv .= file_get_contents("js/$js.js");
4182 function stylesheet_tag($filename) {
4183 $timestamp = filemtime($filename);
4185 echo "<link rel=\"stylesheet\" type=\"text/css\" href=\"$filename?$timestamp\"/>\n";
4188 function javascript_tag($filename) {
4191 if (!(strpos($filename, "?") === FALSE)) {
4192 $query = substr($filename, strpos($filename, "?")+
1);
4193 $filename = substr($filename, 0, strpos($filename, "?"));
4196 $timestamp = filemtime($filename);
4198 if ($query) $timestamp .= "&$query";
4200 echo "<script type=\"text/javascript\" charset=\"utf-8\" src=\"$filename?$timestamp\"></script>\n";
4203 function calculate_dep_timestamp() {
4204 $files = array_merge(glob("js/*.js"), glob("css/*.css"));
4208 foreach ($files as $file) {
4209 if (filemtime($file) > $max_ts) $max_ts = filemtime($file);
4215 function T_js_decl($s1, $s2) {
4217 $s1 = preg_replace("/\n/", "", $s1);
4218 $s2 = preg_replace("/\n/", "", $s2);
4220 $s1 = preg_replace("/\"/", "\\\"", $s1);
4221 $s2 = preg_replace("/\"/", "\\\"", $s2);
4223 return "T_messages[\"$s1\"] = \"$s2\";\n";
4227 function init_js_translations() {
4229 print 'var T_messages = new Object();
4232 if (T_messages[msg]) {
4233 return T_messages[msg];
4239 function ngettext(msg1, msg2, n) {
4240 return (parseInt(n) > 1) ? msg2 : msg1;
4243 $l10n = _get_reader();
4245 for ($i = 0; $i < $l10n->total
; $i++
) {
4246 $orig = $l10n->get_original_string($i);
4247 $translation = __($orig);
4249 print T_js_decl($orig, $translation);
4253 function label_to_feed_id($label) {
4254 return LABEL_BASE_INDEX
- 1 - abs($label);
4257 function feed_to_label_id($feed) {
4258 return LABEL_BASE_INDEX
- 1 +
abs($feed);
4261 function format_libxml_error($error) {
4262 return T_sprintf("LibXML error %s at line %d (column %d): %s",
4263 $error->code
, $error->line
, $error->column
,