Commit 41bd4a98 authored by Sebastian Reichel's avatar Sebastian Reichel

Imported Upstream version 1.10+dfsg

parent aaaceadf
......@@ -21,9 +21,6 @@
<div class="rss">
<img class="feedicon" src="images/pub_set.svg" style="width : 64px; height : 64px;"
alt="feed icon"/>
<h1><xsl:value-of select="atom:title"/></h1>
<p class="description">This feed has been exported from
......
......@@ -430,7 +430,7 @@ class API extends Handler {
}
array_push($rv, array(
"id" => (int)$line['id'],
"id" => (int)label_to_feed_id($line['id']),
"caption" => $line['caption'],
"fg_color" => $line['fg_color'],
"bg_color" => $line['bg_color'],
......@@ -669,7 +669,7 @@ class API extends Handler {
$headline_row['attachments'] = get_article_enclosures(
$line['id']);
if (!$show_excerpt)
if ($show_excerpt)
$headline_row["excerpt"] = $line["content_preview"];
if ($show_content) {
......@@ -678,7 +678,7 @@ class API extends Handler {
$headline_row["content"] = sanitize(
$line["content"],
sql_bool_to_bool($line['hide_images']),
false, $line["site_url"]);
false, $line["site_url"], false, $line["id"]);
} else {
$headline_row["content"] = $line["content"];
}
......
......@@ -26,9 +26,12 @@ class Db_Mysql implements IDb {
}
function query($query, $die_on_error = true) {
$result = mysql_query($query, $this->link);
$result = @mysql_query($query, $this->link);
if (!$result) {
user_error("Query $query failed: " . ($this->link ? mysql_error($this->link) : "No connection"),
$error = @mysql_error($this->link);
@mysql_query("ROLLBACK", $this->link);
user_error("Query $query failed: " . ($this->link ? $error : "No connection"),
$die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
return $result;
......
......@@ -24,9 +24,12 @@ class Db_Mysqli implements IDb {
}
function query($query, $die_on_error = true) {
$result = mysqli_query($this->link, $query);
$result = @mysqli_query($this->link, $query);
if (!$result) {
user_error("Query $query failed: " . ($this->link ? mysqli_error($this->link) : "No connection"),
$error = @mysqli_error($this->link);
@mysqli_query($this->link, "ROLLBACK");
user_error("Query $query failed: " . ($this->link ? $error : "No connection"),
$die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
......
......@@ -35,11 +35,14 @@ class Db_Pgsql implements IDb {
}
function query($query, $die_on_error = true) {
$result = pg_query($query);
$result = @pg_query($this->link, $query);
if (!$result) {
$error = @pg_last_error($this->link);
@pg_query($this->link, "ROLLBACK");
$query = htmlspecialchars($query); // just in case
user_error("Query $query failed: " . ($this->link ? pg_last_error($this->link) : "No connection"),
user_error("Query $query failed: " . ($this->link ? $error : "No connection"),
$die_on_error ? E_USER_ERROR : E_USER_WARNING);
}
return $result;
......
......@@ -3,5 +3,6 @@ class FeedEnclosure {
public $link;
public $type;
public $length;
public $title;
}
?>
......@@ -42,7 +42,11 @@ class FeedItem_Atom extends FeedItem_Common {
|| $link->getAttribute("rel") == "standout")) {
$base = $this->xpath->evaluate("string(ancestor-or-self::*[@xml:base][1]/@xml:base)", $link);
return rewrite_relative_url($base, $link->getAttribute("href"));
if ($base)
return rewrite_relative_url($base, $link->getAttribute("href"));
else
return $link->getAttribute("href");
}
}
}
......@@ -133,7 +137,7 @@ class FeedItem_Atom extends FeedItem_Common {
}
}
$enclosures = $this->xpath->query("media:content", $this->elem);
$enclosures = $this->xpath->query("media:content | media:group/media:content", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
......@@ -142,6 +146,9 @@ class FeedItem_Atom extends FeedItem_Common {
$enc->link = $enclosure->getAttribute("url");
$enc->length = $enclosure->getAttribute("length");
$desc = $this->xpath->query("media:description", $enclosure)->item(0);
if ($desc) $enc->title = strip_tags($desc->nodeValue);
array_push($encs, $enc);
}
......
......@@ -112,7 +112,7 @@ class FeedItem_RSS extends FeedItem_Common {
array_push($encs, $enc);
}
$enclosures = $this->xpath->query("media:content", $this->elem);
$enclosures = $this->xpath->query("media:content | media:group/media:content", $this->elem);
foreach ($enclosures as $enclosure) {
$enc = new FeedEnclosure();
......@@ -121,6 +121,9 @@ class FeedItem_RSS extends FeedItem_Common {
$enc->link = $enclosure->getAttribute("url");
$enc->length = $enclosure->getAttribute("length");
$desc = $this->xpath->query("media:description", $enclosure)->item(0);
if ($desc) $enc->title = strip_tags($desc->nodeValue);
array_push($encs, $enc);
}
......
......@@ -18,36 +18,46 @@ class FeedParser {
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
mb_substitute_character("none");
$error = libxml_get_last_error();
// libxml compiled without iconv?
if ($error && $error->code == 32) {
if ($error && ($error->code == 32 || $error->code == 9)) {
if (preg_match('/^(<\?xml[\t\n\r ].*?encoding=["\'])(.+?)(["\'].*?\?>)/s', $data, $matches) === 1) {
libxml_clear_errors();
$enc = $matches[2];
$data = iconv($enc, 'UTF-8//IGNORE', $data);
$data = mb_convert_encoding($data, 'UTF-8', $enc);
$data = preg_replace('/^<\?xml[\t\n\r ].*?\?>/s', $matches[1] . "UTF-8" . $matches[3] , $data);
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
$error = libxml_get_last_error();
// apparently not all UTF-8 characters are valid for XML
$data = preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $data);
if ($data) {
libxml_clear_errors();
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
$error = libxml_get_last_error();
}
}
}
// some terrible invalid unicode entity?
if ($error && $error->code == 9) {
libxml_clear_errors();
$data = mb_convert_encoding($data, 'UTF-8', 'UTF-8');
// we might want to try guessing input encoding here too
$data = iconv("UTF-8", "UTF-8//IGNORE", $data);
if ($data) {
libxml_clear_errors();
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
$this->doc = new DOMDocument();
$this->doc->loadXML($data);
$error = libxml_get_last_error();
$error = libxml_get_last_error();
}
}
$this->error = $this->format_error($error);
......
......@@ -255,6 +255,7 @@ class Feeds extends Handler_Protected {
$last_error = $qfh_ret[3];
$last_updated = strpos($qfh_ret[4], '1970-') === FALSE ?
make_local_datetime($qfh_ret[4], false) : __("Never");
$highlight_words = $qfh_ret[5];
$vgroup_last_feed = $vgr_last_feed;
......@@ -509,7 +510,7 @@ class Feeds extends Handler_Protected {
$tags = false;
$line["content"] = sanitize($line["content"],
sql_bool_to_bool($line['hide_images']), false, $entry_site_url);
sql_bool_to_bool($line['hide_images']), false, $entry_site_url, $highlight_words, $line["id"]);
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_RENDER_ARTICLE_CDM) as $p) {
$line = $p->hook_render_article_cdm($line);
......@@ -560,6 +561,13 @@ class Feeds extends Handler_Protected {
$reply['content'] .= "</div>";
if ($highlight_words && count($highlight_words > 0)) {
foreach ($highlight_words as $word) {
$line["title"] = preg_replace("/(\Q$word\E)/i",
"<span class=\"highlight\">$1</span>", $line["title"]);
}
}
$reply['content'] .= "<span id=\"RTITLE-$id\"
onclick=\"return cdmClicked(event, $id);\"
class=\"titleWrap $hlc_suffix\">
......@@ -621,7 +629,9 @@ class Feeds extends Handler_Protected {
}
$reply['content'] .= "</div>";
$reply['content'] .= "<div class=\"cdmContentInner\">";
if (!$line['lang']) $line['lang'] = 'en';
$reply['content'] .= "<div class=\"cdmContentInner\" lang=\"".$line['lang']."\">";
if ($line["orig_feed_id"]) {
......@@ -691,10 +701,13 @@ class Feeds extends Handler_Protected {
} 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>";
}
}
......@@ -1134,7 +1147,7 @@ class Feeds extends Handler_Protected {
if (!SPHINX_ENABLED) {
print "<div style=\"float : left\">
<a class=\"visibleLink\" target=\"_blank\" href=\"http://tt-rss.org/wiki/SearchSyntax\">Search syntax</a>
<a class=\"visibleLink\" target=\"_blank\" href=\"http://tt-rss.org/wiki/SearchSyntax\">".__("Search syntax")."</a>
</div>";
}
......
......@@ -85,8 +85,9 @@ class Handler_Public extends Handler {
}
$tpl->setVariable('SELF_URL', htmlspecialchars(get_self_url_prefix()), true);
$line["content_preview"] = truncate_string(strip_tags($line["content_preview"]), 100, '...');
while ($line = $this->dbh->fetch_assoc($result)) {
$line["content_preview"] = truncate_string(strip_tags($line["content_preview"]), 100, '...');
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
$line = $p->hook_query_headlines($line);
}
......@@ -390,14 +391,18 @@ class Handler_Public extends Handler {
}
}
function updateTask() {
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", $op);
}
function housekeepingTask() {
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING, "hook_house_keeping", $op);
}
function globalUpdateFeeds() {
include "rssfuncs.php";
// Update all feeds needing a update.
update_daemon_common(0, true, false);
housekeeping_common(false);
RPC::updaterandomfeed_real($this->dbh);
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_UPDATE_TASK, "hook_update_task", $op);
}
function sharepopup() {
......
......@@ -97,7 +97,7 @@ class Opml extends Handler_Protected {
$html_url_qpart = "";
}
$out .= "<outline text=\"$title\" xmlUrl=\"$url\" $html_url_qpart/>\n";
$out .= "<outline type=\"rss\" text=\"$title\" xmlUrl=\"$url\" $html_url_qpart/>\n";
}
if ($cat_title) $out .= "</outline>\n";
......
......@@ -38,6 +38,7 @@ class PluginHost {
const HOOK_PREFS_SAVE_FEED = 21;
const HOOK_FETCH_FEED = 22;
const HOOK_QUERY_HEADLINES = 23;
const HOOK_HOUSE_KEEPING = 24;
const KIND_ALL = 1;
const KIND_SYSTEM = 2;
......
......@@ -875,6 +875,11 @@ class Pref_Filters extends Handler_Protected {
print "<div class=\"dlgButtons\">";
print "<div style=\"float : left\">
<a class=\"visibleLink\" target=\"_blank\" href=\"http://tt-rss.org/wiki/ContentFilters\">".__("Wiki: Filters")."</a>
</div>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('filterNewRuleDlg').execute()\">".
($rule ? __("Save rule") : __('Add rule'))."</button> ";
......
......@@ -476,7 +476,8 @@ class RPC extends Handler_Protected {
print json_encode(array("wide" => $wide));
}
function updaterandomfeed() {
static function updaterandomfeed_real($dbh) {
// Test if the feed need a update (update interval exceded).
if (DB_TYPE == "pgsql") {
$update_limit_qpart = "AND ((
......@@ -507,16 +508,24 @@ class RPC extends Handler_Protected {
$random_qpart = sql_random_function();
// we could be invoked from public.php with no active session
if ($_SESSION["uid"]) {
$owner_check_qpart = "AND ttrss_feeds.owner_uid = '".$_SESSION["uid"]."'";
} else {
$owner_check_qpart = "";
}
// We search for feed needing update.
$result = $this->dbh->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id
$result = $dbh->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id
FROM
ttrss_feeds, ttrss_users, ttrss_user_prefs
WHERE
ttrss_feeds.owner_uid = ttrss_users.id
AND ttrss_users.id = ttrss_user_prefs.owner_uid
AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL'
AND ttrss_feeds.owner_uid = ".$_SESSION["uid"]."
$update_limit_qpart $updstart_thresh_qpart
$owner_check_qpart
$update_limit_qpart
$updstart_thresh_qpart
ORDER BY $random_qpart LIMIT 30");
$feed_id = -1;
......@@ -527,7 +536,7 @@ class RPC extends Handler_Protected {
$tstart = time();
while ($line = $this->dbh->fetch_assoc($result)) {
while ($line = $dbh->fetch_assoc($result)) {
$feed_id = $line["id"];
if (time() - $tstart < ini_get("max_execution_time") * 0.7) {
......@@ -551,6 +560,10 @@ class RPC extends Handler_Protected {
}
function updaterandomfeed() {
RPC::updaterandomfeed_real($this->dbh);
}
private function markArticlesById($ids, $cmode) {
$tmp_ids = array();
......
......@@ -182,7 +182,7 @@ div.cdmFeedTitle {
border-width : 0px 0px 1px 0px;
border-style : solid;
padding : 5px 3px 5px 5px;
background : url("images/toolbar.png") bottom left;
background : url("../images/toolbar.png") bottom left;
background-repeat : repeat-x;
}
......@@ -223,6 +223,9 @@ div.cdm .hlFeed a {
div.cdmContentInner p {
max-width : 650px;
text-align : justify;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
div.cdmContentInner iframe {
......
......@@ -382,7 +382,7 @@ span.hlLabelRef {
div.postHeader div.postDate {
text-align : right;
color : #555;
color : #909090;
float : right;
}
......@@ -995,6 +995,9 @@ span.collapseBtn {
div.postContent p {
max-width : 650px;
text-align : justify;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
div.postContent iframe {
......@@ -1011,13 +1014,46 @@ body#ttrssZoom {
margin-left : auto;
margin-right : auto;
padding : 20px;
max-width : 800px;
max-width : 670px;
background : #f9fbff;
}
body#ttrssZoom div.postHeader div.postFeedTitle {
float : left;
text-align : right;
padding-left : 0px;
font-size : 10px;
}
body#ttrssZoom div.postHeader a.postComments {
text-align : right;
padding-left : 0px;
font-size : 10px;
}
body#ttrssZoom div.postHeader div.postDate {
float : none;
text-align : right;
padding-left : 0px;
color : #777;
font-size : 10px;
}
body#ttrssZoom div.postHeader div.postTags {
color : #777;
font-size : 10px;
}
body#ttrssZoom div.postHeader div.postTitle {
white-space : normal;
}
body#ttrssZoom div.postContent p {
max-width : 650px;
text-align : justify;
-webkit-hyphens: auto;
-moz-hyphens: auto;
hyphens: auto;
}
body#ttrssZoom div.postHeader {
......@@ -1108,7 +1144,6 @@ body#ttrssMain #feedTree {
background : #f0f0f0;
color : #999;
border-color : #f0f0f0;
text-shadow : none;
}
#feedTree .counterNode {
......@@ -1128,7 +1163,6 @@ body#ttrssMain #feedTree {
margin-top : 3px;
min-width : 23px;
height : 14px;
text-shadow : 1px 1px rgba(0,0,0,0.2);
}
#feedTree .dijitTreeRow {
......@@ -1154,3 +1188,13 @@ body#ttrssPrefs hr {
position : relative;
top : -2px;
}
span.highlight {
background-color : #ffff00;
color : #cc90cc;
}
div.enclosure_title {
}
......@@ -120,7 +120,6 @@ div.rss h1 {
border-color : gray;
border-style : dotted;
color : gray;
margin-right : 90px;
}
div.rss h2 {
......@@ -160,7 +159,7 @@ div.rss hr {
body#sharepopup {
background-color : white;
background-image : url("images/toolbar.png");
background-image : url("../images/toolbar.png");
background-repeat : repeat-x;
background-position : bottom;
margin : 10px;
......
......@@ -80,7 +80,7 @@
$class = ($feedctr % 2) ? "even" : "odd";
if ($line['articles_archived'] > 0) {
$archived = sprintf(ngettext("%d archived article", "%d archived articles", $line['articles_archived']), $line['articles_archived']);
$archived = sprintf(_ngettext("%d archived article", "%d archived articles", $line['articles_archived']), $line['articles_archived']);
$archived = "&nbsp;<span class='subscribers'>($archived)</span>";
} else {
$archived = '';
......
This diff is collapsed.
......@@ -42,7 +42,8 @@
ORDER BY caption");
while ($line = db_fetch_assoc($result)) {
$rk = array($line["label_id"], $line["caption"], $line["fg_color"],
$rk = array(label_to_feed_id($line["label_id"]),
$line["caption"], $line["fg_color"],
$line["bg_color"]);
array_push($rv, $rk);
}
......
......@@ -175,6 +175,7 @@
while ($tline = db_fetch_assoc($tmp_result)) {
if($debug) _debug(" => " . $tline["last_updated"] . ", " . $tline["id"] . " " . $tline["owner_uid"]);
update_rss_feed($tline["id"], true);
_debug_suppress(false);
++$nf;
}
}
......@@ -194,6 +195,7 @@
$debug_enabled = defined('DAEMON_EXTENDED_DEBUG') || $_REQUEST['xdebug'];
_debug_suppress(!$debug_enabled);
_debug("start", $debug_enabled);
$result = db_query("SELECT id,update_interval,auth_login,
......@@ -354,6 +356,11 @@
$rss->init();
}
require_once "lib/languagedetect/LanguageDetect.php";
$lang = new Text_LanguageDetect();
$lang->setNameMode(2);
// print_r($rss);
$feed = db_escape_string($feed);
......@@ -565,6 +572,17 @@
print "\n";
}
$entry_language = $lang->detect($entry_title . " " . $entry_content, 1);
if (count($entry_language) > 0) {
$entry_language = array_keys($entry_language);
$entry_language = db_escape_string(substr($entry_language[0], 0, 2));
_debug("detected language: $entry_language", $debug_enabled);
} else{
$entry_language = "";
}
$entry_comments = $item->get_comments_url();
$entry_author = $item->get_author();
......@@ -628,7 +646,11 @@
"tags" => $entry_tags,
"plugin_data" => $entry_plugin_data,
"author" => $entry_author,
"stored" => $stored_article);
"stored" => $stored_article,
"feed" => array("id" => $feed,
"fetch_url" => $fetch_url,
"site_url" => $site_url)
);
foreach ($pluginhost->get_hooks(PluginHost::HOOK_ARTICLE_FILTER) as $plugin) {
$article = $plugin->hook_article_filter($article);
......@@ -677,6 +699,7 @@
comments,
num_comments,
plugin_data,
lang,
author)
VALUES
('$entry_title',
......@@ -691,6 +714,7 @@
'$entry_comments',
'$num_comments',
'$entry_plugin_data',
'$entry_language',
'$entry_author')");
$article_labels = array();
......@@ -936,7 +960,7 @@
if (is_array($encs)) {
foreach ($encs as $e) {
$e_item = array(
$e->link, $e->type, $e->length);
$e->link, $e->type, $e->length, $e->title);
array_push($enclosures, $e_item);
}
}
......@@ -948,10 +972,14 @@
db_query("BEGIN");
// debugging
// db_query("DELETE FROM ttrss_enclosures WHERE post_id = '$entry_ref_id'");
foreach ($enclosures as $enc) {
$enc_url = db_escape_string($enc[0]);
$enc_type = db_escape_string($enc[1]);
$enc_dur = db_escape_string($enc[2]);
$enc_title = db_escape_string($enc[3]);
$result = db_query("SELECT id FROM ttrss_enclosures
WHERE content_url = '$enc_url' AND post_id = '$entry_ref_id'");
......@@ -959,7 +987,7 @@
if (db_num_rows($result) == 0) {
db_query("INSERT INTO ttrss_enclosures
(content_url, content_type, title, duration, post_id) VALUES
('$enc_url', '$enc_type', '', '$enc_dur', '$entry_ref_id')");
('$enc_url', '$enc_type', '$enc_title', '$enc_dur', '$entry_ref_id')");
}
}
......@@ -1116,16 +1144,15 @@
}
}
if (file_exists($local_filename)) {
/* if (file_exists($local_filename)) {
$entry->setAttribute('src', SELF_URL_PATH . '/image.php?url=' .
base64_encode($src));
}
} */
}
}
$node = $doc->getElementsByTagName('body')->item(0);
return $doc->saveXML($node);
//$node = $doc->getElementsByTagName('body')->item(0);
//return $doc->saveXML($node);
}
function expire_error_log($debug) {
......@@ -1363,5 +1390,8 @@
$rc = cleanup_tags( 14, 50000);
_debug("Cleaned $rc cached tags.");
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_HOUSE_KEEPING, "hook_house_keeping", "");
}
?>
......@@ -138,10 +138,6 @@
array_push($errors, "PHP support for ctype functions are required by HTMLPurifier.");
}
if (!function_exists("iconv")) {
array_push($errors, "PHP support for iconv is required to handle multiple charsets.");
}
/* if (ini_get("safe_mode")) {
array_push($errors, "PHP safe mode setting is not supported.");
} */
......
<?php
define('VERSION_STATIC', '1.9');
define('VERSION_STATIC', '1.10');
function get_version() {
date_default_timezone_set('UTC');
......
......@@ -98,7 +98,7 @@
<script type="text/javascript">
require({cache:{}});
<?php
require 'lib/jshrink/Minifier.php';
require_once 'lib/jshrink/Minifier.php';
print get_minified_js(array("tt-rss",