<?php
define('EXPECTED_CONFIG_VERSION', 26);
- define('SCHEMA_VERSION', 122);
+ define('SCHEMA_VERSION', 123);
define('LABEL_BASE_INDEX', -1024);
define('PLUGIN_FEED_BASE_INDEX', -128);
$fetch_last_error_code = false;
$fetch_last_content_type = false;
$fetch_curl_used = false;
+ $suppress_debugging = false;
mb_internal_encoding("UTF-8");
date_default_timezone_set('UTC');
$schema_version = false;
+ function _debug_suppress($suppress) {
+ global $suppress_debugging;
+
+ $suppress_debugging = $suppress;
+ }
+
/**
* Print a timestamped debug message.
*
* @return void
*/
function _debug($msg, $show = true) {
+ global $suppress_debugging;
+
+ //echo "[$suppress_debugging] $msg $show\n";
+
+ if ($suppress_debugging) return false;
$ts = strftime("%H:%M:%S", time());
if (function_exists('posix_getpid')) {
$fp = fopen(LOGFILE, 'a+');
if ($fp) {
+ $locked = false;
+
+ if (function_exists("flock")) {
+ $tries = 0;
+
+ // try to lock logfile for writing
+ while ($tries < 5 && !$locked = flock($fp, LOCK_EX | LOCK_NB)) {
+ sleep(1);
+ ++$tries;
+ }
+
+ if (!$locked) {
+ fclose($fp);
+ return;
+ }
+ }
+
fputs($fp, "[$ts] $msg\n");
+
+ if (function_exists("flock")) {
+ flock($fp, LOCK_UN);
+ }
+
fclose($fp);
}
}
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_REFERER, $url);
+ if (!ini_get("safe_mode") && !ini_get("open_basedir")) {
+ curl_setopt($ch, CURLOPT_COOKIEJAR, "/dev/null");
+ }
+
+ if (defined('_CURL_HTTP_PROXY')) {
+ curl_setopt($ch, CURLOPT_PROXY, _CURL_HTTP_PROXY);
+ }
+
if ($post_query) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
}
function load_user_plugins($owner_uid) {
- if ($owner_uid) {
+ if ($owner_uid && SCHEMA_VERSION >= 100) {
$plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
PluginHost::getInstance()->load($plugins, PluginHost::KIND_USER, $owner_uid);
if (SINGLE_USER_MODE) {
@session_start();
authenticate_user("admin", null);
+ startup_gettext();
load_user_plugins($_SESSION["uid"]);
} else {
if (!validate_session()) $_SESSION["uid"] = false;
db_query("UPDATE ttrss_user_entries
SET unread = false,last_read = NOW() WHERE (SELECT COUNT(*)
- FROM ttrss_user_labels2 WHERE article_id = ref_id) > 0
- AND unread = true AND $date_qpart AND owner_uid = $owner_uid");
+ FROM ttrss_user_labels2, ttrss_entries WHERE article_id = ref_id AND id = ref_id AND $date_qpart) > 0
+ AND unread = true AND owner_uid = $owner_uid");
}
} else if ($feed > 0) {
$intl = get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
if (DB_TYPE == "pgsql") {
- $match_part .= " AND updated > NOW() - INTERVAL '$intl hour' ";
+ $match_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
} else {
- $match_part .= " AND updated > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
+ $match_part .= " AND date_entered > DATE_SUB(NOW(), INTERVAL $intl HOUR) ";
}
$need_entries = true;
$from_where = "ttrss_entries.id = ttrss_user_entries.ref_id AND";
} else {
$from_qpart = "ttrss_user_entries";
+ $from_where = "";
}
$query = "SELECT count(int_id) AS unread
$owner_uid = $_SESSION["uid"];
- $result = db_query("SELECT id,caption,COUNT(u1.unread) AS unread,COUNT(u2.unread) AS total
+ $result = db_query("SELECT id,caption,SUM(CASE WHEN u1.unread = true THEN 1 ELSE 0 END) AS unread, COUNT(u1.unread) AS total
FROM ttrss_labels2 LEFT JOIN ttrss_user_labels2 ON
(ttrss_labels2.id = label_id)
- LEFT JOIN ttrss_user_entries AS u1 ON (u1.ref_id = article_id AND u1.unread = true
- AND u1.owner_uid = $owner_uid)
- LEFT JOIN ttrss_user_entries AS u2 ON (u2.ref_id = article_id AND u2.unread = false
- AND u2.owner_uid = $owner_uid)
+ LEFT JOIN ttrss_user_entries AS u1 ON u1.ref_id = article_id
WHERE ttrss_labels2.owner_uid = $owner_uid GROUP BY ttrss_labels2.id,
ttrss_labels2.caption");
$search_query_part = "";
- $keywords = explode(" ", $search);
+ $keywords = str_getcsv($search, " ");
$query_keywords = array();
$search_words = array();
}
- $content_query_part = "content, content AS content_preview, ";
+ $content_query_part = "content, ";
if (is_numeric($feed)) {
}
- function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false) {
+ function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
if (!$owner) $owner = $_SESSION["uid"];
$res = trim($str); if (!$res) return '';
- if (strpos($res, "href=") === false)
- $res = rewrite_urls($res);
-
$charset_hack = '<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>';
$disallowed_attributes = array('id', 'style', 'class');
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SANITIZE) as $plugin) {
- $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes);
+ $retval = $plugin->hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes, $article_id);
if (is_array($retval)) {
$doc = $retval[0];
$allowed_elements = $retval[1];
// http://stackoverflow.com/questions/4081372/highlight-keywords-in-a-paragraph
- $elements = $xpath->query('//*[contains(.,"'.$word.'")]');
-
- foreach ($elements as $element) {
- foreach ($element->childNodes as $child) {
+ $elements = $xpath->query("//*/text()");
- if (!$child instanceof DomText) continue;
+ foreach ($elements as $child) {
- $fragment = $doc->createDocumentFragment();
- $text = $child->textContent;
- $stubs = array();
+ $fragment = $doc->createDocumentFragment();
+ $text = $child->textContent;
+ $stubs = array();
- while (($pos = stripos($text, $word)) !== false) {
- $fragment->appendChild(new DomText(substr($text, 0, $pos)));
- $word = substr($text, $pos, strlen($word));
- $highlight = $doc->createElement('span');
- $highlight->appendChild(new DomText($word));
- $highlight->setAttribute('class', 'highlight');
- $fragment->appendChild($highlight);
- $text = substr($text, $pos + strlen($word));
- }
+ while (($pos = mb_stripos($text, $word)) !== false) {
+ $fragment->appendChild(new DomText(mb_substr($text, 0, $pos)));
+ $word = mb_substr($text, $pos, mb_strlen($word));
+ $highlight = $doc->createElement('span');
+ $highlight->appendChild(new DomText($word));
+ $highlight->setAttribute('class', 'highlight');
+ $fragment->appendChild($highlight);
+ $text = mb_substr($text, $pos + mb_strlen($word));
+ }
- if (!empty($text)) $fragment->appendChild(new DomText($text));
+ if (!empty($text)) $fragment->appendChild(new DomText($text));
- $element->replaceChild($fragment, $child);
- }
+ $child->parentNode->replaceChild($fragment, $child);
}
}
}
if (preg_match("/^[0-9]*$/", $tag)) return false;
if (mb_strlen($tag) > 250) return false;
- if (function_exists('iconv')) {
- $tag = iconv("utf-8", "utf-8", $tag);
- }
-
if (!$tag) return false;
return true;
$result = db_query("SELECT id,title,link,content,feed_id,comments,int_id,lang,
".SUBSTRING_FOR_DATE."(updated,1,16) as updated,
(SELECT site_url FROM ttrss_feeds WHERE id = feed_id) as site_url,
+ (SELECT title FROM ttrss_feeds WHERE id = feed_id) as feed_title,
(SELECT hide_images FROM ttrss_feeds WHERE id = feed_id) as hide_images,
(SELECT always_display_enclosures FROM ttrss_feeds WHERE id = feed_id) as always_display_enclosures,
num_comments,
$line["content"] = sanitize($line["content"],
sql_bool_to_bool($line['hide_images']),
- $owner_uid, $line["site_url"]);
+ $owner_uid, $line["site_url"], false, $line["id"]);
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE) as $p) {
$line = $p->hook_render_article($line);
} else {
$comments_url = htmlspecialchars($line["link"]);
}
- $entry_comments = "<a target='_blank' href=\"$comments_url\">$num_comments comments</a>";
+ $entry_comments = "<a class=\"postComments\"
+ target='_blank' href=\"$comments_url\">$num_comments ".
+ _ngettext("comment", "comments", $num_comments)."</a>";
+
} else {
if ($line["comments"] && $line["link"] != $line["comments"]) {
- $entry_comments = "<a target='_blank' href=\"".htmlspecialchars($line["comments"])."\">comments</a>";
+ $entry_comments = "<a class=\"postComments\" target='_blank' href=\"".htmlspecialchars($line["comments"])."\">".__("comments")."</a>";
}
}
<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>
<title>Tiny Tiny RSS - ".$line["title"]."</title>
<link rel=\"stylesheet\" type=\"text/css\" href=\"css/tt-rss.css\">
+ <link rel=\"shortcut icon\" type=\"image/png\" href=\"images/favicon.png\">
+ <link rel=\"icon\" type=\"image/png\" sizes=\"72x72\" href=\"images/favicon-72px.png\">
+
<script type=\"text/javascript\">
function openSelectedAttachment(elem) {
try {
$rv['content'] .= "<div class='postTitle'>" . $line["title"] . "$entry_author</div>";
}
- if ($zoom_mode)
+ if ($zoom_mode) {
+ $feed_title = "<a href=\"".htmlspecialchars($line["site_url"]).
+ "\" target=\"_blank\">".
+ htmlspecialchars($line["feed_title"])."</a>";
+
+ $rv['content'] .= "<div class=\"postFeedTitle\">$feed_title</div>";
+
$rv['content'] .= "<div class=\"postDate\">$parsed_updated</div>";
+ }
$tags_str = format_tags_string($line["tags"], $id);
$tags_str_full = join(", ", $line["tags"]);
$cat_id = (int)getFeedCategory($feed_id);
+ if ($cat_id == 0)
+ $null_cat_qpart = "cat_id IS NULL OR";
+ else
+ $null_cat_qpart = "";
+
$result = db_query("SELECT * FROM ttrss_filters2 WHERE
owner_uid = $owner_uid AND enabled = true ORDER BY order_id, title");
FROM ttrss_filters2_rules AS r,
ttrss_filter_types AS t
WHERE
- (cat_id IS NULL OR cat_id IN ($check_cats)) AND
+ ($null_cat_qpart (cat_id IS NULL AND cat_filter = false) OR cat_id IN ($check_cats)) AND
(feed_id IS NULL OR feed_id = '$feed_id') AND
filter_type = t.id AND filter_id = '$filter_id'");
$url = $line["content_url"];
$ctype = $line["content_type"];
+ $title = $line["title"];
if (!$ctype) $ctype = __("unknown type");
$entry["type"] = $ctype;
$entry["filename"] = $filename;
$entry["url"] = $url;
+ $entry["title"] = $title;
array_push($entries, $entry);
}
$rv .= "<p><a target=\"_blank\"
href=\"".htmlspecialchars($entry["url"])."\"
>" .htmlspecialchars($entry["url"]) . "</a></p>";
+ }
+ if ($entry['title']) {
+ $rv.= "<div class=\"enclosure_title\">${entry['title']}</div>";
}
}
}
"<option value=''>" . __('Attachments')."</option>";
foreach ($entries as $entry) {
- $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "</option>";
+ if ($entry["title"])
+ $title = "— " . truncate_string($entry["title"], 30);
+ else
+ $title = "";
+
+ $rv .= "<option value=\"".htmlspecialchars($entry["url"])."\">" . htmlspecialchars($entry["filename"]) . "$title</option>";
};
}
- function rewrite_urls($html) {
- libxml_use_internal_errors(true);
-
- $charset_hack = '<head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
- </head>';
-
- $doc = new DOMDocument();
- $doc->loadHTML($charset_hack . $html);
- $xpath = new DOMXPath($doc);
-
- $entries = $xpath->query('//*/text()');
-
- foreach ($entries as $entry) {
- if (strstr($entry->wholeText, "://") !== false) {
- $text = preg_replace("/((?<!=.)((http|https|ftp)+):\/\/[^ ,!]+)/i",
- "<a target=\"_blank\" href=\"\\1\">\\1</a>", $entry->wholeText);
-
- if ($text != $entry->wholeText) {
- $cdoc = new DOMDocument();
- $cdoc->loadHTML($charset_hack . $text);
-
-
- foreach ($cdoc->childNodes as $cnode) {
- $cnode = $doc->importNode($cnode, true);
-
- if ($cnode) {
- $entry->parentNode->insertBefore($cnode);
- }
- }
-
- $entry->parentNode->removeChild($entry);
-
- }
- }
- }
-
- $node = $doc->getElementsByTagName('body')->item(0);
-
- // http://tt-rss.org/forum/viewtopic.php?f=1&t=970
- if ($node)
- return $doc->saveXML($node);
- else
- return $html;
- }
-
function filter_to_sql($filter, $owner_uid) {
$query = array();
$qpart .= " AND $cat_qpart";
}
+ $qpart .= " AND feed_id IS NOT NULL";
+
array_push($query, "($qpart)");
}
curl_setopt($curl, CURLOPT_TIMEOUT, 60);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+ if (defined('_CURL_HTTP_PROXY')) {
+ curl_setopt($curl, CURLOPT_PROXY, _CURL_HTTP_PROXY);
+ }
+
if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
curl_setopt($curl, CURLOPT_SSLVERSION, 3);
}