Commit 40adc06e authored by Sebastian Reichel's avatar Sebastian Reichel

Imported Upstream version 15.7+git20151123+dfsg

parent 5f6d7942
<?xml version="1.0" encoding="UTF-8"?>
<buildpath>
<buildpathentry kind="src" path=""/>
<buildpathentry kind="con" path="org.eclipse.php.core.LANGUAGE"/>
</buildpath>
Thumbs.db
/deploy.exclude
/deploy.sh
/messages.mo
*~
*.DS_Store
#*
.idea/*
plugins.local/*
themes.local/*
config.php
feed-icons/*
cache/*/*
lock/*
tags
plugins/fever
cache/htmlpurifier/*/*ser
lib/htmlpurifier/library/HTMLPurifier/DefinitionCache/Serializer/*/*ser
web.config
AddType image/svg+xml svg
AddType image/svg+xml svgz
# PROTECT all htaccess files
<Files ~ "^[\._]ht">
Order Allow,Deny
Deny from all
Satisfy All
</Files>
# PROTECT config.php
<files config.php>
Order Allow,Deny
Deny from all
Satisfy all
</files>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>tt-rss</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.wst.validation.validationbuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.dltk.core.scriptbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.php.core.PHPNature</nature>
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
</natures>
</projectDescription>
......@@ -4,7 +4,7 @@ Tiny Tiny RSS
Web-based news feed aggregator, designed to allow you to read news from
any location, while feeling as close to a real desktop application as possible.
http://tt-rss.org (http://mirror.tt-rss.org)
http://tt-rss.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
......@@ -23,19 +23,3 @@ Copyright (c) 2005 Andrew Dolgov (unless explicitly stated otherwise).
Uses Silk icons by Mark James: http://www.famfamfam.com/lab/icons/silk/
## Requirements
* Compatible web browser (http://tt-rss.org/wiki/CompatibleBrowsers)
* Web server, for example Apache
* PHP (with support for mbstring functions)
* PostgreSQL (tested on 8.3) or MySQL (InnoDB and version 4.1+ required)
## Installation Notes
http://tt-rss.org/wiki/InstallationNotes
## See also
* FAQ: http://tt-rss.org/wiki/FrequentlyAskedQuestions
* Forum: http://tt-rss.org/forum
* Wiki: http://tt-rss.org/wiki/WikiStart
......@@ -53,6 +53,8 @@
@session_start();
}
startup_gettext();
if (!init_plugins()) return;
$method = strtolower($_REQUEST["op"]);
......
......@@ -63,7 +63,7 @@
if ($_SESSION["uid"]) {
if (!validate_session()) {
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 6)));
print error_json(6);
return;
}
load_user_plugins( $_SESSION["uid"]);
......@@ -81,21 +81,21 @@
$update_intervals = array(
0 => __("Default interval"),
-1 => __("Disable updates"),
15 => __("Each 15 minutes"),
30 => __("Each 30 minutes"),
15 => __("15 minutes"),
30 => __("30 minutes"),
60 => __("Hourly"),
240 => __("Each 4 hours"),
720 => __("Each 12 hours"),
240 => __("4 hours"),
720 => __("12 hours"),
1440 => __("Daily"),
10080 => __("Weekly"));
$update_intervals_nodefault = array(
-1 => __("Disable updates"),
15 => __("Each 15 minutes"),
30 => __("Each 30 minutes"),
15 => __("15 minutes"),
30 => __("30 minutes"),
60 => __("Hourly"),
240 => __("Each 4 hours"),
720 => __("Each 12 hours"),
240 => __("4 hours"),
720 => __("12 hours"),
1440 => __("Daily"),
10080 => __("Weekly"));
......@@ -104,13 +104,6 @@
5 => __("Power User"),
10 => __("Administrator"));
#$error = sanity_check();
#if ($error['code'] != 0 && $op != "logout") {
# print json_encode(array("error" => $error));
# return;
#}
$op = str_replace("-", "_", $op);
$override = PluginHost::getInstance()->lookup_handler($op, $method);
......@@ -137,18 +130,18 @@
return;
} else {
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 6)));
print error_json(6);
return;
}
} else {
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 6)));
print error_json(6);
return;
}
}
}
header("Content-Type: text/json");
print json_encode(array("error" => array("code" => 7)));
print error_json(13);
?>
This diff is collapsed.
......@@ -41,12 +41,12 @@ class Article extends Handler_Protected {
} else if ($mode == "zoom") {
array_push($articles, format_article($id, true, true));
} else if ($mode == "raw") {
if ($_REQUEST['html']) {
if (isset($_REQUEST['html'])) {
header("Content-Type: text/html");
print '<link rel="stylesheet" type="text/css" href="css/tt-rss.css"/>';
}
$article = format_article($id, false);
$article = format_article($id, false, isset($_REQUEST["zoom"]));
print $article['content'];
return;
}
......@@ -190,7 +190,7 @@ class Article extends Handler_Protected {
print "<table width='100%'><tr><td>";
print "<textarea dojoType=\"dijit.form.SimpleTextarea\" rows='4'
style='font-size : 12px; width : 100%' id=\"tags_str\"
style='font-size : 12px; width : 98%' id=\"tags_str\"
name='tags_str'>$tags_str</textarea>
<div class=\"autocomplete\" id=\"tags_choices\"
style=\"display:none\"></div>";
......@@ -215,6 +215,18 @@ class Article extends Handler_Protected {
score = '$score' WHERE ref_id IN ($ids) AND owner_uid = " . $_SESSION["uid"]);
print json_encode(array("id" => $ids,
"score" => (int)$score,
"score_pic" => get_score_pic($score)));
}
function getScore() {
$id = $this->dbh->escape_string($_REQUEST['id']);
$result = $this->dbh->query("SELECT score FROM ttrss_user_entries WHERE ref_id = $id AND owner_uid = " . $_SESSION["uid"]);
$score = $this->dbh->fetch_result($result, 0, "score");
print json_encode(array("id" => $id,
"score" => (int)$score,
"score_pic" => get_score_pic($score)));
}
......
......@@ -88,6 +88,7 @@ class Db_PDO implements IDb {
$this->query("set client_encoding = 'UTF-8'");
$this->query("set datestyle = 'ISO, european'");
$this->query("set TIME ZONE 0");
return;
case "mysql":
$this->query("SET time_zone = '+0:0'");
return;
......
......@@ -78,6 +78,7 @@ class Db_Pgsql implements IDb {
pg_set_client_encoding("UNICODE");
$this->query("set datestyle = 'ISO, european'");
$this->query("set TIME ZONE 0");
$this->query("set cpu_tuple_cost = 0.5");
return true;
}
......
<?php
class Dlg extends Handler_Protected {
private $param;
private $params;
function before($method) {
function before($method) {
if (parent::before($method)) {
header("Content-Type: text/html"); # required for iframe
......@@ -143,7 +144,7 @@ class Dlg extends Handler_Protected {
$key_escaped = str_replace("'", "\\'", $key);
echo "<a href=\"javascript:viewfeed('$key_escaped') \" style=\"font-size: " .
echo "<a href=\"javascript:viewfeed({feed:'$key_escaped'}) \" style=\"font-size: " .
$size . "px\" title=\"$value articles tagged with " .
$key . '">' . $key . '</a> ';
}
......@@ -160,37 +161,6 @@ class Dlg extends Handler_Protected {
}
function printTagSelect() {
print __("Match:"). "&nbsp;" .
"<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\" type=\"radio\" checked value=\"any\" name=\"tag_mode\" id=\"tag_mode_any\">";
print "<label for=\"tag_mode_any\">".__("Any")."</label>";
print "&nbsp;";
print "<input class=\"noborder\" dojoType=\"dijit.form.RadioButton\" type=\"radio\" value=\"all\" name=\"tag_mode\" id=\"tag_mode_all\">";
print "<label for=\"tag_mode_all\">".__("All tags.")."</input>";
print "<select id=\"all_tags\" name=\"all_tags\" title=\"" . __('Which Tags?') . "\" multiple=\"multiple\" size=\"10\" style=\"width : 100%\">";
$result = $this->dbh->query("SELECT DISTINCT tag_name FROM ttrss_tags WHERE owner_uid = ".$_SESSION['uid']."
AND LENGTH(tag_name) <= 30 ORDER BY tag_name ASC");
while ($row = $this->dbh->fetch_assoc($result)) {
$tmp = htmlspecialchars($row["tag_name"]);
print "<option value=\"$tmp\">$tmp</option>";
}
print "</select>";
print "<div align='right'>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"viewfeed(get_all_tags($('all_tags')),
get_radio_checked($('tag_mode')));\">" . __('Display entries') . "</button>";
print "&nbsp;";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return closeInfoBox()\">" .
__('Close this window') . "</button>";
print "</div>";
}
function generatedFeed() {
$this->params = explode(":", $this->param, 3);
......@@ -220,52 +190,5 @@ class Dlg extends Handler_Protected {
//return;
}
function newVersion() {
$version_data = check_for_update();
$version = $version_data['version'];
$id = $version_data['version_id'];
if ($version && $id) {
print "<div class='tagCloudContainer'>";
print T_sprintf("New version of Tiny Tiny RSS is available (%s).",
"<b>$version</b>");
print "</div>";
$details = "http://tt-rss.org/redmine/versions/$id";
$download = "http://tt-rss.org/#Download";
print "<p align='center'>".__("You can update using built-in updater in the Preferences or by using update.php")."</p>";
print "<div style='text-align : center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return window.open('$details')\">".__("See the release notes")."</button>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return window.open('$download')\">".__("Download")."</button>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return dijit.byId('newVersionDlg').hide()\">".
__('Close this window')."</button>";
} else {
print "<div class='tagCloudContainer'>";
print "<p align='center'>".__("Error receiving version information or no new version available.")."</p>";
print "</div>";
print "<div style='text-align : center'>";
print "<button dojoType=\"dijit.form.Button\"
onclick=\"return dijit.byId('newVersionDlg').hide()\">".
__('Close this window')."</button>";
print "</div>";
}
print "</div>";
}
}
?>
......@@ -51,6 +51,14 @@ class FeedItem_RSS extends FeedItem_Common {
}
function get_title() {
$title = $this->xpath->query("title", $this->elem)->item(0);
if ($title) {
return trim($title->nodeValue);
}
// if the document has a default namespace then querying for
// title would fail because of reasons so let's try the old way
$title = $this->elem->getElementsByTagName("title")->item(0);
if ($title) {
......
......@@ -115,6 +115,7 @@ class FeedParser {
$this->type = $this::FEED_RSS;
break;
case "feed":
case "atom:feed":
$this->type = $this::FEED_ATOM;
break;
default:
......@@ -140,9 +141,14 @@ class FeedParser {
$link = $xpath->query("//atom:feed/atom:link[not(@rel)]")->item(0);
if (!$link)
$link = $xpath->query("//atom:feed/atom:link[@rel='alternate']")->item(0);
if (!$link)
$link = $xpath->query("//atom03:feed/atom03:link[not(@rel)]")->item(0);
if (!$link)
$link = $xpath->query("//atom03:feed/atom03:link[@rel='alternate']")->item(0);
if ($link && $link->hasAttributes()) {
$this->link = $link->getAttribute("href");
......
This diff is collapsed.
......@@ -2,7 +2,7 @@
class Handler_Public extends Handler {
private function generate_syndicated_feed($owner_uid, $feed, $is_cat,
$limit, $offset, $search, $search_mode,
$limit, $offset, $search,
$view_mode = false, $format = 'atom', $order = false, $orig_guid = false, $start_ts = false) {
require_once "lib/MiniTemplator.class.php";
......@@ -37,12 +37,31 @@ class Handler_Public extends Handler {
break;
}
//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, $override_strategy = false, $override_vfeed = false, $start_ts = false) {
$qfh_ret = queryFeedHeadlines($feed,
1, $view_mode, $is_cat, $search, $search_mode,
/*$qfh_ret = queryFeedHeadlines($feed,
1, $view_mode, $is_cat, $search, false,
$date_sort_field, $offset, $owner_uid,
false, 0, true, true, false, false, $start_ts);
false, 0, true, true, false, false, $start_ts);*/
//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, $override_strategy = false, $override_vfeed = false, $start_ts = false, $check_top_id = false) {
$params = array(
"owner_uid" => $owner_uid,
"feed" => $feed,
"limit" => 1,
"view_mode" => $view_mode,
"cat_view" => $is_cat,
"search" => $search,
"override_order" => $date_sort_field,
"include_children" => true,
"ignore_vfeed_group" => true,
"offset" => $offset,
"start_ts" => $start_ts
);
$qfh_ret = queryFeedHeadlines($params);
$result = $qfh_ret[0];
......@@ -60,11 +79,26 @@ class Handler_Public extends Handler {
header("Last-Modified: $last_modified", true);
}
$qfh_ret = queryFeedHeadlines($feed,
$limit, $view_mode, $is_cat, $search, $search_mode,
/*$qfh_ret = queryFeedHeadlines($feed,
$limit, $view_mode, $is_cat, $search, false,
$date_sort_field, $offset, $owner_uid,
false, 0, true, true, false, false, $start_ts);
false, 0, true, true, false, false, $start_ts);*/
$params = array(
"owner_uid" => $owner_uid,
"feed" => $feed,
"limit" => $limit,
"view_mode" => $view_mode,
"cat_view" => $is_cat,
"search" => $search,
"override_order" => $date_sort_field,
"include_children" => true,
"ignore_vfeed_group" => true,
"offset" => $offset,
"start_ts" => $start_ts
);
$qfh_ret = queryFeedHeadlines($params);
$result = $qfh_ret[0];
$feed_title = htmlspecialchars($qfh_ret[1]);
......@@ -101,8 +135,7 @@ class Handler_Public extends Handler {
$tpl->setVariable('ARTICLE_ID',
htmlspecialchars($orig_guid ? $line['link'] :
get_self_url_prefix() .
"/public.php?url=" . urlencode($line['link'])), true);
$this->make_article_tag_uri($line['id'], $line['date_entered'])), true);
$tpl->setVariable('ARTICLE_LINK', htmlspecialchars($line['link']), true);
$tpl->setVariable('ARTICLE_TITLE', htmlspecialchars($line['title']), true);
$tpl->setVariable('ARTICLE_EXCERPT', $line["content_preview"], true);
......@@ -125,7 +158,7 @@ class Handler_Public extends Handler {
$tpl->setVariable('ARTICLE_AUTHOR', htmlspecialchars($line['author']), true);
$tpl->setVariable('ARTICLE_SOURCE_LINK', htmlspecialchars($line['site_url']), true);
$tpl->setVariable('ARTICLE_SOURCE_LINK', htmlspecialchars($line['site_url'] ? $line["site_url"] : get_self_url_prefix()), true);
$tpl->setVariable('ARTICLE_SOURCE_TITLE', htmlspecialchars($line['feed_title'] ? $line['feed_title'] : $feed_title), true);
$tags = get_article_tags($line["id"], $owner_uid);
......@@ -140,7 +173,7 @@ class Handler_Public extends Handler {
foreach ($enclosures as $e) {
$type = htmlspecialchars($e['content_type']);
$url = htmlspecialchars($e['content_url']);
$length = $e['duration'];
$length = $e['duration'] ? $e['duration'] : 1;
$tpl->setVariable('ARTICLE_ENCLOSURE_URL', $url, true);
$tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', $type, true);
......@@ -375,7 +408,6 @@ class Handler_Public extends Handler {
$offset = (int)$this->dbh->escape_string($_REQUEST["offset"]);
$search = $this->dbh->escape_string($_REQUEST["q"]);
$search_mode = $this->dbh->escape_string($_REQUEST["smode"]);
$view_mode = $this->dbh->escape_string($_REQUEST["view-mode"]);
$order = $this->dbh->escape_string($_REQUEST["order"]);
$start_ts = $this->dbh->escape_string($_REQUEST["ts"]);
......@@ -401,7 +433,7 @@ class Handler_Public extends Handler {
if ($owner_id) {
$this->generate_syndicated_feed($owner_id, $feed, $is_cat, $limit,
$offset, $search, $search_mode, $view_mode, $format, $order, $orig_guid, $start_ts);
$offset, $search, $view_mode, $format, $order, $orig_guid, $start_ts);
} else {
header('HTTP/1.1 403 Forbidden');
}
......@@ -500,7 +532,7 @@ class Handler_Public extends Handler {
</div>
<button type="submit"><?php echo __('Share') ?></button>
<button onclick="return window.close()"><?php echo __('Cancel') ?></button>
</div>
</td>
</form>
</td></tr></table>
......@@ -707,7 +739,7 @@ class Handler_Public extends Handler {
function index() {
header("Content-Type: text/plain");
print json_encode(array("error" => array("code" => 7)));
print error_json(13);
}
function forgotpass() {
......@@ -972,10 +1004,10 @@ class Handler_Public extends Handler {
print "<h2>Database update required</h2>";
print "<h3>";
printf("Your Tiny Tiny RSS database needs update to the latest version: %d to %d.",
$updater->getSchemaVersion(), SCHEMA_VERSION);
print "</h3>";
print_notice("<h4>".
sprintf("Your Tiny Tiny RSS database needs update to the latest version: %d to %d.",
$updater->getSchemaVersion(), SCHEMA_VERSION).
"</h4>");
print_warning("Please backup your database before proceeding.");
......@@ -1002,5 +1034,42 @@ class Handler_Public extends Handler {
<?php
}
function cached_image() {
@$hash = basename($_GET['hash']);
if ($hash) {
$filename = CACHE_DIR . '/images/' . $hash . '.png';
if (file_exists($filename)) {
/* See if we can use X-Sendfile */
$xsendfile = false;
if (function_exists('apache_get_modules') &&
array_search('mod_xsendfile', apache_get_modules()))
$xsendfile = true;
if ($xsendfile) {
header("X-Sendfile: $filename");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
} else {
header("Content-type: image/png");
$stamp = gmdate("D, d M Y H:i:s", filemtime($filename)). " GMT";
header("Last-Modified: $stamp", true);
readfile($filename);
}
} else {
header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
echo "File not found.";
}
}
}
private function make_article_tag_uri($id, $timestamp) {
$timestamp = date("Y-m-d", strtotime($timestamp));
return "tag:" . parse_url(get_self_url_prefix(), PHP_URL_HOST) . ",$timestamp:/$id";
}
}
?>
......@@ -11,10 +11,10 @@ class PluginHandler extends Handler_Protected {
if (method_exists($plugin, $method)) {
$plugin->$method();
} else {
print json_encode(array("error" => "METHOD_NOT_FOUND"));
print error_json(13);
}
} else {
print json_encode(array("error" => "PLUGIN_NOT_FOUND"));
print error_json(14);
}
}
}
......
......@@ -8,6 +8,7 @@ class PluginHost {
private $storage = array();
private $feeds = array();
private $api_methods = array();
private $plugin_actions = array();
private $owner_uid;
private $debug;
private $last_registered;
......@@ -15,13 +16,16 @@ class PluginHost {
const API_VERSION = 2;
// Hooks marked with *1 are run in global context and available
// to plugins loaded in config.php only
const HOOK_ARTICLE_BUTTON = 1;
const HOOK_ARTICLE_FILTER = 2;
const HOOK_PREFS_TAB = 3;
const HOOK_PREFS_TAB_SECTION = 4;
const HOOK_PREFS_TABS = 5;
const HOOK_FEED_PARSED = 6;
const HOOK_UPDATE_TASK = 7;
const HOOK_UPDATE_TASK = 7; // *1
const HOOK_AUTH_USER = 8;
const HOOK_HOTKEY_MAP = 9;
const HOOK_RENDER_ARTICLE = 10;
......@@ -38,11 +42,13 @@ class PluginHost {
const HOOK_PREFS_SAVE_FEED = 21;
const HOOK_FETCH_FEED = 22;
const HOOK_QUERY_HEADLINES = 23;
const HOOK_HOUSE_KEEPING = 24;
const HOOK_HOUSE_KEEPING = 24; // *1
const HOOK_SEARCH = 25;
const HOOK_FORMAT_ENCLOSURES = 26;
const HOOK_SUBSCRIBE_FEED = 27;
const HOOK_HEADLINES_BEFORE = 28;
const HOOK_RENDER_ENCLOSURE = 29;
const HOOK_ARTICLE_FILTER_ACTION = 30;
const KIND_ALL = 1;
const KIND_SYSTEM = 2;
......@@ -94,7 +100,7 @@ class PluginHost {
}
function get_plugin($name) {
return $this->plugins[$name];
return $this->plugins[strtolower($name)];
}
function run_hooks($type, $method, $args) {
......@@ -127,12 +133,18 @@ class PluginHost {
return array();
}
}
function load_all($kind, $owner_uid = false) {
$plugins = array_map("basename", glob("plugins/*"));
$this->load(join(",", $plugins), $kind, $owner_uid);
function load_all($kind, $owner_uid = false, $skip_init = false) {
$plugins = array_merge(glob("plugins/*"), glob("plugins.local/*"));
$plugins = array_filter($plugins, "is_dir");
$plugins = array_map("basename", $plugins);
asort($plugins);
$this->load(join(",", $plugins), $kind, $owner_uid, $skip_init);