Merge branch 'master' into patch-1

Conflicts:
	locale/de_DE/LC_MESSAGES/messages.po
This commit is contained in:
Art4 2015-02-19 23:17:43 +01:00
commit d9c042c4c5
94 changed files with 29504 additions and 26668 deletions

View file

@ -2,7 +2,7 @@
class API extends Handler {
const API_LEVEL = 8;
const API_LEVEL = 11;
const STATUS_OK = 0;
const STATUS_ERR = 1;
@ -200,6 +200,11 @@ class API extends Handler {
$include_nested = sql_bool_to_bool($_REQUEST["include_nested"]);
$sanitize_content = !isset($_REQUEST["sanitize"]) ||
sql_bool_to_bool($_REQUEST["sanitize"]);
$force_update = sql_bool_to_bool($_REQUEST["force_update"]);
$has_sandbox = sql_bool_to_bool($_REQUEST["has_sandbox"]);
$excerpt_length = (int)$this->dbh->escape_string($_REQUEST["excerpt_length"]);
$_SESSION['hasSandbox'] = $has_sandbox;
$override_order = false;
switch ($_REQUEST["order_by"]) {
@ -222,7 +227,7 @@ class API extends Handler {
$headlines = $this->api_get_headlines($feed_id, $limit, $offset,
$filter, $is_cat, $show_excerpt, $show_content, $view_mode, $override_order,
$include_attachments, $since_id, $search, $search_mode,
$include_nested, $sanitize_content);
$include_nested, $sanitize_content, $force_update, $excerpt_length);
$this->wrap(self::STATUS_OK, $headlines);
} else {
@ -632,7 +637,28 @@ class API extends Handler {
$filter, $is_cat, $show_excerpt, $show_content, $view_mode, $order,
$include_attachments, $since_id,
$search = "", $search_mode = "",
$include_nested = false, $sanitize_content = true) {
$include_nested = false, $sanitize_content = true, $force_update = false, $excerpt_length = 100) {
if ($force_update && $feed_id > 0 && is_numeric($feed_id)) {
// Update the feed if required with some basic flood control
$result = db_query(
"SELECT cache_images,".SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated
FROM ttrss_feeds WHERE id = '$feed_id'");
if (db_num_rows($result) != 0) {
$last_updated = strtotime(db_fetch_result($result, 0, "last_updated"));
$cache_images = sql_bool_to_bool(db_fetch_result($result, 0, "cache_images"));
if (!$cache_images && time() - $last_updated > 120) {
include "rssfuncs.php";
update_rss_feed($feed_id, true, true);
} else {
db_query("UPDATE ttrss_feeds SET last_updated = '1970-01-01', last_update_started = '1970-01-01'
WHERE id = '$feed_id'");
}
}
}
$qfh_ret = queryFeedHeadlines($feed_id, $limit,
$view_mode, $is_cat, $search, $search_mode,
@ -644,16 +670,31 @@ class API extends Handler {
$headlines = array();
while ($line = db_fetch_assoc($result)) {
$line["content_preview"] = truncate_string(strip_tags($line["content"]), 100);
$line["content_preview"] = truncate_string(strip_tags($line["content"]), $excerpt_length);
foreach (PluginHost::getInstance()->get_hooks(PluginHost::HOOK_QUERY_HEADLINES) as $p) {
$line = $p->hook_query_headlines($line, 100, true);
$line = $p->hook_query_headlines($line, $excerpt_length, true);
}
$is_updated = ($line["last_read"] == "" &&
($line["unread"] != "t" && $line["unread"] != "1"));
$tags = explode(",", $line["tag_cache"]);
$labels = json_decode($line["label_cache"], true);
$label_cache = $line["label_cache"];
$labels = false;
if ($label_cache) {
$label_cache = json_decode($label_cache, true);
if ($label_cache) {
if ($label_cache["no-labels"] == 1)
$labels = array();
else
$labels = $label_cache;
}
}
if (!is_array($labels)) $labels = get_article_labels($line["id"]);
//if (!$tags) $tags = get_article_tags($line["id"]);
//if (!$labels) $labels = get_article_labels($line["id"]);

View file

@ -220,52 +220,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>";
}
}
?>

View file

@ -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) {

View file

@ -115,6 +115,7 @@ class FeedParser {
$this->type = $this::FEED_RSS;
break;
case "feed":
case "atom:feed":
$this->type = $this::FEED_ATOM;
break;
default:

View file

@ -39,11 +39,10 @@ class Handler_Public extends Handler {
//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,
$date_sort_field, $offset, $owner_uid,
false, 0, false, true, false, false, $start_ts);
false, 0, true, true, false, false, $start_ts);
$result = $qfh_ret[0];
@ -64,7 +63,7 @@ class Handler_Public extends Handler {
$qfh_ret = queryFeedHeadlines($feed,
$limit, $view_mode, $is_cat, $search, $search_mode,
$date_sort_field, $offset, $owner_uid,
false, 0, false, true, false, false, $start_ts);
false, 0, true, true, false, false, $start_ts);
$result = $qfh_ret[0];

View file

@ -491,7 +491,9 @@ class Opml extends Handler_Protected {
if (is_file($tmp_file)) {
$doc = new DOMDocument();
libxml_disable_entity_loader(false);
$doc->load($tmp_file);
libxml_disable_entity_loader(true);
unlink($tmp_file);
} else if (!$doc) {
print_error(__('Error: unable to find moved OPML file.'));

View file

@ -495,7 +495,7 @@ class Pref_Feeds extends Handler_Protected {
$feed_id = $this->dbh->escape_string($_REQUEST["feed_id"]);
if (is_file($icon_file) && $feed_id) {
if (filesize($icon_file) < 20000) {
if (filesize($icon_file) < 65535) {
$result = $this->dbh->query("SELECT id FROM ttrss_feeds
WHERE id = '$feed_id' AND owner_uid = ". $_SESSION["uid"]);
@ -738,9 +738,9 @@ class Pref_Feeds extends Handler_Protected {
<input type=\"hidden\" name=\"op\" value=\"pref-feeds\">
<input type=\"hidden\" name=\"feed_id\" value=\"$feed_id\">
<input type=\"hidden\" name=\"method\" value=\"uploadicon\">
<button dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\"
<button class=\"small\" dojoType=\"dijit.form.Button\" onclick=\"return uploadFeedIcon();\"
type=\"submit\">".__('Replace')."</button>
<button dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\"
<button class=\"small\" dojoType=\"dijit.form.Button\" onclick=\"return removeFeedIcon($feed_id);\"
type=\"submit\">".__('Remove')."</button>
</form>";

View file

@ -571,7 +571,8 @@ class Pref_Prefs extends Handler_Protected {
} else if ($pref_name == "USER_CSS_THEME") {
$themes = array_map("basename", glob("themes/*.css"));
$themes = array_filter(array_map("basename", glob("themes/*.css")),
"theme_valid");
print_select($pref_name, $value, $themes,
'dojoType="dijit.form.Select"');

View file

@ -177,9 +177,9 @@
// *** Other settings (less important) ***
// ***************************************
define('CHECK_FOR_NEW_VERSION', true);
// Check for new versions of tt-rss automatically.
define('CHECK_FOR_UPDATES', true);
// Check for updates automatically if running Git version
define('DETECT_ARTICLE_LANGUAGE', false);
// Detect article language when updating feeds, presently this is only
// used for hyphenation. This may increase amount of CPU time used by
@ -193,7 +193,7 @@
// if you experience weird errors and tt-rss failing to start, blank pages
// after login, or content encoding errors, disable it.
define('PLUGINS', 'auth_internal, note, updater');
define('PLUGINS', 'auth_internal, note');
// Comma-separated list of plugins to load automatically for all users.
// System plugins have to be specified here. Please enable at least one
// authentication plugin here (auth_*).

View file

@ -93,6 +93,10 @@
background : white;
}
.claro #feedTree.dijitTree .dijitTreeRowSelected {
box-shadow : -1px 0px 2px -1px rgba(0,0,0,0.1);
}
.claro .dijitTree .dijitTreeRowHover {
background : #f0f0f0;
border-color : #ddd;
@ -346,6 +350,10 @@ button[disabled],
line-height : 20px;
}
.claro .dijitButton.small .dijitButtonText {
font-size : 11px;
}
.claro .dijitMenu {
border-color : #ccc;
}

View file

@ -7,6 +7,11 @@ body#ttrssMain, body#ttrssPrefs, body#ttrssLogin, body {
font-size: 14px;
}
body#ttrssMain {
overflow : hidden;
max-height : 100%;
}
div.postReply {
padding : 0px;
}
@ -138,18 +143,30 @@ a:hover {
position : absolute;
}
#notify.visible {
transform: translate(0, -35px);
-webkit-transform: translate(0, -35px);
-o-transform: translate(0, -35px);
-moz-transform: translate(0, -35px);
}
#notify {
bottom : 10px;
right : 20px;
border-width : 1px;
bottom : -35px;
right : 0px;
height : 20px;
left : 0px;
border-width : 1px 0px 0px 0px;
border-style : solid;
position : absolute;
position : fixed;
font-size : 12px;
z-index : 99;
max-width : 200px;
min-width : 100px;
padding : 5px;
-width : 200px;
box-shadow : 0px -2px 2px rgba(0,0,0,0.1);
transition: all 0.5s ease-in-out;
-webkit-transition: all 0.5s ease-in-out;
-moz-transition: all 0.5s ease-in-out;
-o-transition: all 0.5s ease-in-out;
}
#notify img {
@ -176,17 +193,17 @@ a:hover {
background-color : #fff7d5;
}
.notify.progress {
.notify.notify_progress {
border-color : #d7c47a;
background-color : #fff7d5;
}
.notify.info {
.notify.notify_info {
border-color : #88b0f0;
background-color : #ecf4ff;
}
.notify.error {
.notify.notify_error {
background-color : #ffcccc;
border-color : #ff0000;
}
@ -324,6 +341,10 @@ div.prefHelp {
color : #555;
}
.small {
font-size : 11px;
}
#main-toolbar > * {
white-space : nowrap;
display : table-cell;
@ -796,7 +817,7 @@ div.fatalError textarea {
#feeds-holder {
padding : 0px;
border-width : 0px 1px 0px 0px;
border-width : 0px 0px 0px 0px;
border-style : solid;
border-color : #ddd;
overflow : hidden;

View file

@ -1,6 +1,6 @@
<?php
define('EXPECTED_CONFIG_VERSION', 26);
define('SCHEMA_VERSION', 126);
define('SCHEMA_VERSION', 127);
define('LABEL_BASE_INDEX', -1024);
define('PLUGIN_FEED_BASE_INDEX', -128);
@ -14,6 +14,8 @@
$fetch_curl_used = false;
$suppress_debugging = false;
libxml_disable_entity_loader(true);
mb_internal_encoding("UTF-8");
date_default_timezone_set('UTC');
if (defined('E_DEPRECATED')) {
@ -357,6 +359,9 @@
$url = ltrim($url, ' ');
$url = str_replace(' ', '%20', $url);
if (strpos($url, "//") === 0)
$url = 'http:' . $url;
if (!defined('NO_CURL') && function_exists('curl_init')) {
$fetch_curl_used = true;
@ -403,10 +408,6 @@
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_query);
}
if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
curl_setopt($ch, CURLOPT_SSLVERSION, 3);
}
if ($login && $pass)
curl_setopt($ch, CURLOPT_USERPWD, "$login:$pass");
@ -804,10 +805,6 @@
function initialize_user($uid) {
db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
values ('$uid', 'Tiny Tiny RSS: New Releases',
'http://tt-rss.org/releases.rss')");
db_query("insert into ttrss_feeds (owner_uid,title,feed_url)
values ('$uid', 'Tiny Tiny RSS: Forum',
'http://tt-rss.org/forum/rss.php')");

View file

@ -17,7 +17,10 @@
$params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
$params["bw_limit"] = (int) $_SESSION["bw_limit"];
$params["label_base_index"] = (int) LABEL_BASE_INDEX;
$params["theme"] = get_pref("USER_CSS_THEME", false, false);
$theme = get_pref( "USER_CSS_THEME", false, false);
$params["theme"] = theme_valid("$theme") ? $theme : "";
$params["plugins"] = implode(", ", PluginHost::getInstance()->get_plugin_names());
$params["php_platform"] = PHP_OS;
@ -200,6 +203,26 @@
return array($prefixes, $hotkeys);
}
function check_for_update() {
if (defined("GIT_VERSION_TIMESTAMP")) {
$content = @fetch_file_contents("http://tt-rss.org/version.json");
if ($content) {
$content = json_decode($content, true);
if ($content && isset($content["changeset"])) {
if ((int)GIT_VERSION_TIMESTAMP < (int)$content["changeset"]["timestamp"] &&
GIT_VERSION_HEAD != $content["changeset"]["id"]) {
return $content["changeset"]["id"];
}
}
}
}
return "";
}
function make_runtime_info() {
$data = array();
@ -218,6 +241,15 @@
$data['dep_ts'] = calculate_dep_timestamp();
$data['reload_on_ts_change'] = !defined('_NO_RELOAD_ON_TS_CHANGE');
if (CHECK_FOR_UPDATES && $_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
$update_result = @check_for_update();
$data["update_result"] = $update_result;
$_SESSION["last_version_check"] = time();
}
if (file_exists(LOCK_DIRECTORY . "/update_daemon.lock")) {
$data['daemon_is_running'] = (int) file_is_locked("update_daemon.lock");
@ -245,15 +277,6 @@
}
}
if ($_SESSION["last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
$new_version_details = @check_for_update();
$data['new_version_available'] = (int) ($new_version_details != false);
$_SESSION["last_version_check"] = time();
$_SESSION["version_data"] = $new_version_details;
}
return $data;
}
@ -826,6 +849,21 @@
}
function iframe_whitelisted($entry) {
$whitelist = array("youtube.com", "youtu.be", "vimeo.com");
@$src = parse_url($entry->getAttribute("src"), PHP_URL_HOST);
if ($src) {
foreach ($whitelist as $w) {
if ($src == $w || $src == "www.$w")
return true;
}
}
return false;
}
function sanitize($str, $force_remove_images = false, $owner = false, $site_url = false, $highlight_words = false, $article_id = false) {
if (!$owner) $owner = $_SESSION["uid"];
@ -894,8 +932,15 @@
$entries = $xpath->query('//iframe');
foreach ($entries as $entry) {
$entry->setAttribute('sandbox', 'allow-scripts');
if (!iframe_whitelisted($entry)) {
$entry->setAttribute('sandbox', 'allow-scripts');
} else {
if ($_SERVER['HTTPS'] == "on") {
$entry->setAttribute("src",
str_replace("http://", "https://",
$entry->getAttribute("src")));
}
}
}
$allowed_elements = array('a', 'address', 'audio', 'article', 'aside',
@ -994,25 +1039,6 @@
return $doc;
}
function check_for_update() {
if (CHECK_FOR_NEW_VERSION && $_SESSION['access_level'] >= 10) {
$version_url = "http://tt-rss.org/version.php?ver=" . VERSION .
"&iid=" . sha1(SELF_URL_PATH);
$version_data = @fetch_file_contents($version_url);
if ($version_data) {
$version_data = json_decode($version_data, true);
if ($version_data && $version_data['version']) {
if (version_compare(VERSION_STATIC, $version_data['version']) == -1) {
return $version_data;
}
}
}
}
return false;
}
function catchupArticlesById($ids, $cmode, $owner_uid = false) {
if (!$owner_uid) $owner_uid = $_SESSION["uid"];
@ -1958,8 +1984,8 @@
}
function getLastArticleId() {
$result = db_query("SELECT MAX(ref_id) AS id FROM ttrss_user_entries
WHERE owner_uid = " . $_SESSION["uid"]);
$result = db_query("SELECT ref_id AS id FROM ttrss_user_entries
WHERE owner_uid = " . $_SESSION["uid"] . " ORDER BY ref_id DESC LIMIT 1");
if (db_num_rows($result) == 1) {
return db_fetch_result($result, 0, "id");
@ -2243,10 +2269,6 @@
curl_setopt($curl, CURLOPT_PROXY, _CURL_HTTP_PROXY);
}
if ((OPENSSL_VERSION_NUMBER >= 0x0090808f) && (OPENSSL_VERSION_NUMBER < 0x10000000)) {
curl_setopt($curl, CURLOPT_SSLVERSION, 3);
}
$html = curl_exec($curl);
$status = curl_getinfo($curl);
@ -2404,4 +2426,21 @@
return LABEL_BASE_INDEX - 1 + abs($feed);
}
function theme_valid($file) {
if ($file == "default.css" || $file == "night.css") return true; // needed for array_filter
$file = "themes/" . basename($file);
if (file_exists($file) && is_readable($file)) {
$fh = fopen($file, "r");
if ($fh) {
$header = fgets($fh);
fclose($fh);
return strpos($header, "supports-version:" . VERSION_STATIC) !== FALSE;
}
}
return false;
}
?>

View file

@ -481,7 +481,7 @@
if (!$registered_title || $registered_title == "[Unknown]") {
$feed_title = db_escape_string($rss->get_title());
$feed_title = db_escape_string(mb_substr($rss->get_title(), 0, 199));
if ($feed_title) {
_debug("registering title: $feed_title", $debug_enabled);
@ -683,6 +683,7 @@
"link" => $entry_link,
"tags" => $entry_tags,
"author" => $entry_author,
"force_catchup" => false, // ugly hack for the time being
"language" => $entry_language, // read only
"feed" => array("id" => $feed,
"fetch_url" => $fetch_url,
@ -707,7 +708,11 @@
db_query("UPDATE ttrss_entries SET date_updated = NOW()
WHERE id = '$base_entry_id'");
continue;
// if we allow duplicate posts, we have to continue to
// create the user entries for this feed
if (!get_pref("ALLOW_DUPLICATE_POSTS", $owner_uid, false)) {
continue;
}
}
_debug("hash differs, applying plugin filters:", $debug_enabled);
@ -733,6 +738,9 @@
$entry_author = db_escape_string($article["author"]);
$entry_link = db_escape_string($article["link"]);
$entry_content = $article["content"]; // escaped below
$entry_force_catchup = $article["force_catchup"];
_debug("force catchup: $entry_force_catchup");
if ($cache_images && is_writable(CACHE_DIR . '/images'))
cache_images($entry_content, $site_url, $debug_enabled);
@ -857,7 +865,7 @@
_debug("user record not found, creating...", $debug_enabled);
if ($score >= -500 && !find_article_filter($article_filters, 'catchup')) {
if ($score >= -500 && !find_article_filter($article_filters, 'catchup') && !$entry_force_catchup) {
$unread = 'true';
$last_read_qpart = 'NULL';
} else {
@ -879,7 +887,7 @@
// N-grams
if (DB_TYPE == "pgsql" and defined('_NGRAM_TITLE_DUPLICATE_THRESHOLD')) {
/* if (DB_TYPE == "pgsql" and defined('_NGRAM_TITLE_DUPLICATE_THRESHOLD')) {
$result = db_query("SELECT COUNT(*) AS similar FROM
ttrss_entries,ttrss_user_entries
@ -894,7 +902,7 @@
if ($ngram_similar > 0) {
$unread = 'false';
}
}
} */
$last_marked = ($marked == 'true') ? 'NOW()' : 'NULL';
$last_published = ($published == 'true') ? 'NOW()' : 'NULL';

View file

@ -1,3 +1,3 @@
<?php # This file has been generated at: Fri Sep 27 13:42:37 MSK 2013
<?php # This file has been generated at: Tue Feb 3 14:45:46 MSK 2015
define('GENERATED_CONFIG_CHECK', 26);
$requred_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'MYSQL_CHARSET', 'SELF_URL_PATH', 'FEED_CRYPT_KEY', 'SINGLE_USER_MODE', 'SIMPLE_UPDATE_MODE', 'PHP_EXECUTABLE', 'LOCK_DIRECTORY', 'CACHE_DIR', 'ICONS_DIR', 'ICONS_URL', 'AUTH_AUTO_CREATE', 'AUTH_AUTO_LOGIN', 'FORCE_ARTICLE_PURGE', 'PUBSUBHUBBUB_HUB', 'PUBSUBHUBBUB_ENABLED', 'ENABLE_REGISTRATION', 'REG_NOTIFY_ADDRESS', 'REG_MAX_USERS', 'SESSION_COOKIE_LIFETIME', 'SESSION_CHECK_ADDRESS', 'SMTP_FROM_NAME', 'SMTP_FROM_ADDRESS', 'DIGEST_SUBJECT', 'SMTP_SERVER', 'SMTP_LOGIN', 'SMTP_PASSWORD', 'SMTP_SECURE', 'CHECK_FOR_NEW_VERSION', 'DETECT_ARTICLE_LANGUAGE', 'ENABLE_GZIP_OUTPUT', 'PLUGINS', 'LOG_DESTINATION', 'CONFIG_VERSION'); ?>
$requred_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'MYSQL_CHARSET', 'SELF_URL_PATH', 'FEED_CRYPT_KEY', 'SINGLE_USER_MODE', 'SIMPLE_UPDATE_MODE', 'PHP_EXECUTABLE', 'LOCK_DIRECTORY', 'CACHE_DIR', 'ICONS_DIR', 'ICONS_URL', 'AUTH_AUTO_CREATE', 'AUTH_AUTO_LOGIN', 'FORCE_ARTICLE_PURGE', 'PUBSUBHUBBUB_HUB', 'PUBSUBHUBBUB_ENABLED', 'SPHINX_SERVER', 'SPHINX_INDEX', 'ENABLE_REGISTRATION', 'REG_NOTIFY_ADDRESS', 'REG_MAX_USERS', 'SESSION_COOKIE_LIFETIME', 'SESSION_CHECK_ADDRESS', 'SMTP_FROM_NAME', 'SMTP_FROM_ADDRESS', 'DIGEST_SUBJECT', 'SMTP_SERVER', 'SMTP_LOGIN', 'SMTP_PASSWORD', 'SMTP_SECURE', 'CHECK_FOR_UPDATES', 'DETECT_ARTICLE_LANGUAGE', 'ENABLE_GZIP_OUTPUT', 'PLUGINS', 'LOG_DESTINATION', 'CONFIG_VERSION'); ?>

View file

@ -39,7 +39,7 @@
function validate_session() {
if (SINGLE_USER_MODE) return true;
if (VERSION_STATIC != $_SESSION["version"]) return false;
//if (VERSION_STATIC != $_SESSION["version"]) return false;
$check_ip = $_SESSION['ip_address'];
@ -62,17 +62,17 @@
return false;
}
if ($_SESSION["ref_schema_version"] != session_get_schema_version(true)) {
if (isset($_SESSION["ref_schema_version"]) && $_SESSION["ref_schema_version"] != session_get_schema_version(true)) {
$_SESSION["login_error_msg"] =
__("Session failed to validate (schema version changed)");
return false;
}
if (sha1($_SERVER['HTTP_USER_AGENT']) != $_SESSION["user_agent"]) {
/* if (sha1($_SERVER['HTTP_USER_AGENT']) != $_SESSION["user_agent"]) {
$_SESSION["login_error_msg"] =
__("Session failed to validate (user agent changed)");
return false;
}
} */
if ($_SESSION["uid"]) {
$result = Db::get()->query(

View file

@ -1,5 +1,5 @@
<?php
define('VERSION_STATIC', '1.13');
define('VERSION_STATIC', '1.15.3');
function get_version() {
date_default_timezone_set('UTC');
@ -8,6 +8,10 @@
if (is_dir("$root_dir/.git") && file_exists("$root_dir/.git/refs/heads/master")) {
$suffix = substr(trim(file_get_contents("$root_dir/.git/refs/heads/master")), 0, 7);
$timestamp = filemtime("$root_dir/.git/refs/heads/master");
define("GIT_VERSION_HEAD", $suffix);
define("GIT_VERSION_TIMESTAMP", $timestamp);
return VERSION_STATIC . ".$suffix";
} else {

View file

@ -65,7 +65,7 @@
<?php if ($_SESSION["uid"]) {
$theme = get_pref( "USER_CSS_THEME", $_SESSION["uid"], false);
if ($theme && file_exists("themes/$theme")) {
if ($theme && theme_valid("$theme")) {
echo stylesheet_tag("themes/$theme");
} else {
echo stylesheet_tag("themes/default.css");
@ -138,7 +138,7 @@
</div>
</div>
<div id="notify" class="notify" style="display : none"></div>
<div id="notify" class="notify"></div>
<div id="cmdline" style="display : none"></div>
<div id="headlines-tmp" style="display : none"></div>
@ -221,13 +221,6 @@
src="images/error.png" />
</button>
<button id="newVersionIcon" dojoType="dijit.form.Button" style="display : none">
<img onclick="newVersionDlg()"
src="images/new_version.png"
title="<?php echo __('New version of Tiny Tiny RSS is available!') ?>" />
</button>
<div dojoType="dijit.form.DropDownButton">
<span><?php echo __('Actions...') ?></span>
<div dojoType="dijit.Menu" style="display: none">
@ -259,13 +252,17 @@
<?php } ?>
</div>
</div>
<button id="updatesIcon" dojoType="dijit.form.Button" style="display : none">
<img src="images/new_version.png" title="<?php echo __('Updates are available from Git.') ?>"/>
</button>
</div>
</div> <!-- toolbar -->
</div> <!-- toolbar pane -->
<div id="headlines-wrap-inner" dojoType="dijit.layout.BorderContainer" region="center">
<div id="floatingTitle" style="display : none"></div>
<div id="floatingTitle" style="visibility : hidden"></div>
<div id="headlines-frame" dojoType="dijit.layout.ContentPane"
onscroll="headlines_scroll_handler(this)" region="center">

View file

@ -270,7 +270,7 @@
<fieldset>
<label>Password</label>
<input required name="DB_PASS" size="20" type="password" value="<?php echo $DB_PASS ?>"/>
<input name="DB_PASS" size="20" type="password" value="<?php echo $DB_PASS ?>"/>
</fieldset>
<fieldset>

View file

@ -182,11 +182,6 @@ function param_unescape(arg) {
return unescape(arg);
}
function hide_notify() {
Element.hide('notify');
}
function notify_real(msg, no_hide, n_type) {
var n = $("notify");
@ -198,13 +193,11 @@ function notify_real(msg, no_hide, n_type) {
}
if (msg == "") {
if (Element.visible(n)) {
notify_hide_timerid = window.setTimeout("hide_notify()", 0);
if (n.hasClassName("visible")) {
notify_hide_timerid = window.setTimeout(function() {
n.removeClassName("visible") }, 0);
}
return;
} else {
Element.show(n);
new Effect.Highlight(n);
}
/* types:
@ -218,30 +211,40 @@ function notify_real(msg, no_hide, n_type) {
msg = "<span class=\"msg\"> " + __(msg) + "</span>";
if (n_type == 1) {
n.className = "notify";
} else if (n_type == 2) {
n.className = "notify progress";
if (n_type == 2) {
msg = "<span><img src='images/indicator_white.gif'></span>" + msg;
no_hide = true;
} else if (n_type == 3) {
n.className = "notify error";
msg = "<span><img src='images/alert.png'></span>" + msg;
} else if (n_type == 4) {
n.className = "notify info";
msg = "<span><img src='images/information.png'></span>" + msg;
}
msg += " <span><img src=\"images/cross.png\" class=\"close\" title=\"" +
__("Click to close") + "\" onclick=\"notify('')\"></span>";
// msg = "<img src='images/live_com_loading.gif'> " + msg;
n.innerHTML = msg;
if (!no_hide) {
notify_hide_timerid = window.setTimeout("hide_notify()", 5*1000);
}
window.setTimeout(function() {
// goddamnit firefox
if (n_type == 2) {
n.className = "notify notify_progress visible";
} else if (n_type == 3) {
n.className = "notify notify_error visible";
msg = "<span><img src='images/alert.png'></span>" + msg;
} else if (n_type == 4) {
n.className = "notify notify_info visible";
} else {
n.className = "notify visible";
}
if (!no_hide) {
notify_hide_timerid = window.setTimeout(function() {
n.removeClassName("visible") }, 5*1000);
}
}, 10);
}
function notify(msg, no_hide) {

View file

@ -302,21 +302,27 @@ function init() {
hotkey_actions["collapse_article"] = function() {
var id = getActiveArticleId();
var elem = $("CICD-"+id);
if(elem.visible()) {
cdmCollapseArticle(null, id);
}
else {
cdmExpandArticle(id);
if (elem) {
if (elem.visible()) {
cdmCollapseArticle(null, id);
}
else {
cdmExpandArticle(id);
}
}
};
hotkey_actions["toggle_expand"] = function() {
var id = getActiveArticleId();
var elem = $("CICD-"+id);
if(elem.visible()) {
cdmCollapseArticle(null, id, false);
}
else {
cdmExpandArticle(id);
if (elem) {
if (elem.visible()) {
cdmCollapseArticle(null, id, false);
}
else {
cdmExpandArticle(id);
}
}
};
hotkey_actions["search_dialog"] = function() {
@ -750,15 +756,6 @@ function parse_runtime_info(data) {
// console.log("RI: " + k + " => " + v);
if (k == "new_version_available") {
if (v == "1") {
Element.show(dijit.byId("newVersionIcon").domNode);
} else {
Element.hide(dijit.byId("newVersionIcon").domNode);
}
return;
}
if (k == "dep_ts" && parseInt(getInitParam("dep_ts")) > 0) {
if (parseInt(getInitParam("dep_ts")) < parseInt(v) && getInitParam("reload_on_ts_change")) {
window.location.reload();
@ -770,6 +767,16 @@ function parse_runtime_info(data) {
return;
}
if (k == "update_result") {
var updatesIcon = dijit.byId("updatesIcon").domNode;
if (v) {
Element.show(updatesIcon);
} else {
Element.hide(updatesIcon);
}
}
if (k == "daemon_stamp_ok" && v != 1) {
notify_error("<span onclick=\"javascript:explainError(3)\">Update daemon is not updating feeds.</span>", true);
return;
@ -962,27 +969,6 @@ function reverseHeadlineOrder() {
}
}
function newVersionDlg() {
try {
var query = "backend.php?op=dlg&method=newVersion";
if (dijit.byId("newVersionDlg"))
dijit.byId("newVersionDlg").destroyRecursive();
dialog = new dijit.Dialog({
id: "newVersionDlg",
title: __("New version available!"),
style: "width: 600px",
href: query,
});
dialog.show();
} catch (e) {
exception_error("newVersionDlg", e);
}
}
function handle_rpc_json(transport, scheduled_call) {
try {
var reply = JSON.parse(transport.responseText);

View file

@ -56,7 +56,7 @@ function headlines_callback2(transport, offset, background, infscroll_req) {
if (infscroll_req == false) {
$("headlines-frame").scrollTop = 0;
Element.hide("floatingTitle");
$("floatingTitle").style.visibility = "hidden";
$("floatingTitle").setAttribute("rowid", 0);
$("floatingTitle").innerHTML = "";
}
@ -1532,7 +1532,7 @@ function cdmCollapseArticle(event, id, unmark) {
if (row.offsetTop < $("headlines-frame").scrollTop)
scrollToRowId(row.id);
Element.hide("floatingTitle");
$("floatingTitle").style.visibility = "hidden";
$("floatingTitle").setAttribute("rowid", false);
}
@ -2357,7 +2357,7 @@ function scrollToRowId(id) {
var row = $(id);
if (row)
$("headlines-frame").scrollTop = row.offsetTop;
$("headlines-frame").scrollTop = row.offsetTop - 4;
} catch (e) {
exception_error("scrollToRowId", e);
@ -2402,11 +2402,12 @@ function updateFloatingTitle(unread_only) {
PluginHost.run(PluginHost.HOOK_FLOATING_TITLE, child);
}
if (child.offsetTop < hf.scrollTop - header.offsetHeight &&
child.offsetTop + child.offsetHeight - hf.scrollTop > header.offsetHeight)
Element.show("floatingTitle");
$("floatingTitle").style.marginRight = hf.offsetWidth - child.offsetWidth + "px";
if (header.offsetTop + header.offsetHeight < hf.scrollTop + $("floatingTitle").offsetHeight - 5 &&
child.offsetTop + child.offsetHeight >= hf.scrollTop + $("floatingTitle").offsetHeight - 5)
$("floatingTitle").style.visibility = "visible";
else
Element.hide("floatingTitle");
$("floatingTitle").style.visibility = "hidden";
return;

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -6,23 +6,33 @@ class Af_Comics_Dilbert extends Af_ComicFilter {
}
function process(&$article) {
$owner_uid = $article["owner_uid"];
if (strpos($article["link"], "dilbert.com") !== FALSE) {
$res = fetch_file_contents($article["link"], false, false, false,
false, false, 0,
"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)");
global $fetch_last_error_content;
if (!$res && $fetch_last_error_content)
$res = $fetch_last_error_content;
if (strpos($article["guid"], "dilbert.com") !== FALSE) {
$doc = new DOMDocument();
@$doc->loadHTML(fetch_file_contents($article["link"]));
@$doc->loadHTML($res);
$basenode = false;
if ($doc) {
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//img[@src])'); // we might also check for img[@class='strip'] I guess...
$basenode = $xpath->query('//img[contains(@class, "img-comic")]')->item(0);
/* $entries = $xpath->query('(//img[@src])'); // we might also check for img[@class='strip'] I guess...
$matches = array();
foreach ($entries as $entry) {
if (preg_match("/dyn\/str_strip\/.*zoom\.gif$/", $entry->getAttribute("src"), $matches)) {
if (preg_match("/dyn\/str_strip\/.*strip\.gif$/", $entry->getAttribute("src"), $matches)) {
$entry->setAttribute("src",
rewrite_relative_url("http://dilbert.com/",
@ -31,7 +41,7 @@ class Af_Comics_Dilbert extends Af_ComicFilter {
$basenode = $entry;
break;
}
}
} */
if ($basenode) {
$article["content"] = $doc->saveXML($basenode);

View file

@ -17,18 +17,7 @@ class Af_Comics_Explosm extends Af_ComicFilter {
if ($doc) {
$xpath = new DOMXPath($doc);
$entries = $xpath->query('(//img[@src])'); // we might also check for img[@class='strip'] I guess...
$matches = array();
foreach ($entries as $entry) {
if (preg_match("/(http:\/\/.*\/db\/files\/Comics\/.*)/i", $entry->getAttribute("src"), $matches)) {
$basenode = $entry;
break;
}
}
$basenode = $xpath->query('(//img[@id="main-comic"])')->item(0);
if ($basenode) {
$article["content"] = $doc->saveXML($basenode);

View file

@ -56,7 +56,9 @@ class Af_Comics_Pa extends Af_ComicFilter {
if ($header->parentNode) { $header->parentNode->removeChild($header); }
$avatar = $xpath->query('(//div[@class="avatar"]//img)')->item(0);
$basenode->insertBefore($avatar, $basenode->firstChild);
if ($basenode)
$basenode->insertBefore($avatar, $basenode->firstChild);
$uninteresting = $xpath->query('(//div[@class="avatar"])');
foreach ($uninteresting as $i) {

View file

@ -0,0 +1,32 @@
<?php
class Af_Comics_Tfd extends Af_ComicFilter {
function supported() {
return array("Toothpaste For Dinner");
}
function process(&$article) {
$owner_uid = $article["owner_uid"];
if (strpos($article["link"], "toothpastefordinner.com") !== FALSE) {
$doc = new DOMDocument();
@$doc->loadHTML(fetch_file_contents($article["link"]));
$basenode = false;
if ($doc) {
$xpath = new DOMXPath($doc);
$basenode = $xpath->query('//img[@class="comic"]')->item(0);
if ($basenode) {
$article["content"] = $doc->saveXML($basenode);
return true;
}
}
}
return false;
}
}
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

View file

@ -0,0 +1,25 @@
function showTrgmRelated(id) {
try {
var query = "backend.php?op=pluginhandler&plugin=af_psql_trgm&method=showrelated&param=" + param_escape(id);
if (dijit.byId("trgmRelatedDlg"))
dijit.byId("trgmRelatedDlg").destroyRecursive();
dialog = new dijit.Dialog({
id: "trgmRelatedDlg",
title: __("Related articles"),
style: "width: 600px",
execute: function() {
},
href: query,
});
dialog.show();
} catch (e) {
exception_error("showTrgmRelated", e);
}
}

View file

@ -0,0 +1,280 @@
<?php
class Af_Psql_Trgm extends Plugin {
private $host;
function about() {
return array(1.0,
"Marks similar articles as read (requires pg_trgm)",
"fox");
}
function save() {
$similarity = (float) db_escape_string($_POST["similarity"]);
$min_title_length = (int) db_escape_string($_POST["min_title_length"]);
if ($similarity < 0) $similarity = 0;
if ($similarity > 1) $similarity = 1;
if ($min_title_length < 0) $min_title_length = 0;
$similarity = sprintf("%.2f", $similarity);
$this->host->set($this, "similarity", $similarity);
$this->host->set($this, "min_title_length", $min_title_length);
echo T_sprintf("Data saved (%s)", $similarity);
}
function init($host) {
$this->host = $host;
$host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
$host->add_hook($host::HOOK_PREFS_TAB, $this);
$host->add_hook($host::HOOK_PREFS_EDIT_FEED, $this);
$host->add_hook($host::HOOK_PREFS_SAVE_FEED, $this);
$host->add_hook($host::HOOK_ARTICLE_BUTTON, $this);
}
function get_js() {
return file_get_contents(__DIR__ . "/init.js");
}
function showrelated() {
$id = (int) db_escape_string($_REQUEST['param']);
$owner_uid = $_SESSION["uid"];
$result = db_query("SELECT title FROM ttrss_entries, ttrss_user_entries
WHERE ref_id = id AND id = $id AND owner_uid = $owner_uid");
$title = db_fetch_result($result, 0, "title");
print "<h2>$title</h2>";
$title = db_escape_string($title);
$result = db_query("SELECT ttrss_entries.id AS id,
feed_id,
ttrss_entries.title AS title,
updated, link,
ttrss_feeds.title AS feed_title,
SIMILARITY(ttrss_entries.title, '$title') AS sm
FROM
ttrss_entries, ttrss_user_entries LEFT JOIN ttrss_feeds ON (ttrss_feeds.id = feed_id)
WHERE
ttrss_entries.id = ref_id AND
ttrss_user_entries.owner_uid = $owner_uid AND
ttrss_entries.id != $id AND
score >= 0 AND
date_entered >= NOW() - INTERVAL '2 weeks'
ORDER BY
sm DESC, date_entered DESC
LIMIT 10");
print "<ul class=\"browseFeedList\" style=\"border-width : 1px\">";
while ($line = db_fetch_assoc($result)) {
print "<li>";
print "<div class='insensitive small' style='margin-left : 20px; float : right'>" .
smart_date_time(strtotime($line["updated"]))
. "</div>";
print "<img src='images/score_high.png' title='".sprintf("%.2f", $line['sm'])."'
style='vertical-align : middle'>";
$article_link = htmlspecialchars($line["link"]);
print " <a target=\"_blank\" href=\"$article_link\">".
$line["title"]."</a>";
print " (<a href=\"#\" onclick=\"viewfeed(".$line["feed_id"].")\">".
htmlspecialchars($line["feed_title"])."</a>)";
print "</li>";
}
print "</ul>";
print "<div style='text-align : center'>";
print "<button dojoType=\"dijit.form.Button\" onclick=\"dijit.byId('trgmRelatedDlg').hide()\">".__('Close this window')."</button>";
print "</div>";
}
function hook_article_button($line) {
return "<img src=\"plugins/af_psql_trgm/button.png\"
style=\"cursor : pointer\" style=\"cursor : pointer\"
onclick=\"showTrgmRelated(".$line["id"].")\"
class='tagsPic' title='".__('Show related articles')."'>";
}
function hook_prefs_tab($args) {
if ($args != "prefFeeds") return;
print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Mark similar articles as read')."\">";
if (DB_TYPE != "pgsql") {
print_error("Database type not supported.");
}
$result = db_query("select 'similarity'::regproc");
if (db_num_rows($result) == 0) {
print_error("pg_trgm extension not found.");
}
$similarity = $this->host->get($this, "similarity");
$min_title_length = $this->host->get($this, "min_title_length");
if (!$similarity) $similarity = '0.75';
if (!$min_title_length) $min_title_length = '32';
print "<form dojoType=\"dijit.form.Form\">";
print "<script type=\"dojo/method\" event=\"onSubmit\" args=\"evt\">
evt.preventDefault();
if (this.validate()) {
console.log(dojo.objectToQuery(this.getValues()));
new Ajax.Request('backend.php', {
parameters: dojo.objectToQuery(this.getValues()),
onComplete: function(transport) {
notify_info(transport.responseText);
}
});
//this.reset();
}
</script>";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"op\" value=\"pluginhandler\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"method\" value=\"save\">";
print "<input dojoType=\"dijit.form.TextBox\" style=\"display : none\" name=\"plugin\" value=\"af_psql_trgm\">";
print_notice("PostgreSQL trigram extension returns string similarity as a floating point number (0-1). Setting it too low might produce false positives, zero disables checking.");
print "<br/>";
print_notice("Enable the plugin for specific feeds in the feed editor.");
print "<h3>" . __("Global settings") . "</h3>";
print "<table>";
print "<tr><td width=\"40%\">".__("Minimum similarity:")."</td>";
print "<td>
<input dojoType=\"dijit.form.ValidationTextBox\"
placeholder=\"0.75\"
required=\"1\" name=\"similarity\" value=\"$similarity\"></td></tr>";
print "<tr><td width=\"40%\">".__("Minimum title length:")."</td>";
print "<td>
<input dojoType=\"dijit.form.ValidationTextBox\"
placeholder=\"32\"
required=\"1\" name=\"min_title_length\" value=\"$min_title_length\"></td></tr>";
print "</table>";
print "<p><button dojoType=\"dijit.form.Button\" type=\"submit\">".
__("Save")."</button>";
print "</form>";
$enabled_feeds = $this->host->get($this, "enabled_feeds");
if (!array($enabled_feeds)) $enabled_feeds = array();
if (count($enabled_feeds) > 0) {
print "<h3>" . __("Currently enabled for (click to edit):") . "</h3>";
print "<ul class=\"browseFeedList\" style=\"border-width : 1px\">";
foreach ($enabled_feeds as $f) {
print "<li>" .
"<img src='images/pub_set.png'
style='vertical-align : middle'> <a href='#'
onclick='editFeed($f)'>".
getFeedTitle($f) . "</a></li>";
}
print "</ul>";
}
print "</div>";
}
function hook_prefs_edit_feed($feed_id) {
print "<div class=\"dlgSec\">".__("Similarity (pg_trgm)")."</div>";
print "<div class=\"dlgSecCont\">";
$enabled_feeds = $this->host->get($this, "enabled_feeds");
if (!array($enabled_feeds)) $enabled_feeds = array();
$key = array_search($feed_id, $enabled_feeds);
$checked = $key !== FALSE ? "checked" : "";
print "<hr/><input dojoType=\"dijit.form.CheckBox\" type=\"checkbox\" id=\"trgm_similarity_enabled\"
name=\"trgm_similarity_enabled\"
$checked>&nbsp;<label for=\"trgm_similarity_enabled\">".__('Mark similar articles as read')."</label>";
print "</div>";
}
function hook_prefs_save_feed($feed_id) {
$enabled_feeds = $this->host->get($this, "enabled_feeds");
if (!is_array($enabled_feeds)) $enabled_feeds = array();
$enable = checkbox_to_sql_bool($_POST["trgm_similarity_enabled"]) == 'true';
$key = array_search($feed_id, $enabled_feeds);
if ($enable) {
if ($key === FALSE) {
array_push($enabled_feeds, $feed_id);
}
} else {
if ($key !== FALSE) {
unset($enabled_feeds[$key]);
}
}
$this->host->set($this, "enabled_feeds", $enabled_feeds);
}
function hook_article_filter($article) {
if (DB_TYPE != "pgsql") return $article;
$result = db_query("select 'similarity'::regproc");
if (db_num_rows($result) == 0) return $article;
$enabled_feeds = $this->host->get($this, "enabled_feeds");
$key = array_search($article["feed"]["id"], $enabled_feeds);
if ($key === FALSE) return $article;
$similarity = (float) $this->host->get($this, "similarity");
if ($similarity < 0.01) return $article;
$min_title_length = (int) $this->host->get($this, "min_length");
if (mb_strlen($article["title"]) < $min_title_length) return $article;
$owner_uid = $article["owner_uid"];
$feed_id = $article["feed"]["id"];
$title_escaped = db_escape_string($article["title"]);
$result = db_query("SELECT MAX(SIMILARITY(title, '$title_escaped')) AS ms
FROM ttrss_entries, ttrss_user_entries WHERE ref_id = id AND
date_entered >= NOW() - interval '1 day' AND
owner_uid = $owner_uid");
$similarity_result = db_fetch_result($result, 0, "ms");
//_debug("similarity result: $similarity_result");
if ($similarity_result >= $similarity) {
$article["force_catchup"] = true;
}
return $article;
}
function api_version() {
return 2;
}
}
?>

View file

@ -42,7 +42,7 @@ class Af_RedditImgur extends Plugin {
// links to imgur pages
$matches = array();
if (preg_match("/^http:\/\/imgur.com\/([^\.\/]+$)/", $entry->getAttribute("href"), $matches)) {
if (preg_match("/^https?:\/\/imgur.com\/([^\.\/]+$)/", $entry->getAttribute("href"), $matches)) {
$token = $matches[1];
@ -77,7 +77,7 @@ class Af_RedditImgur extends Plugin {
}
// linked albums, ffs
if (preg_match("/^http:\/\/imgur.com\/(a|album)\/[^\.]+$/", $entry->getAttribute("href"), $matches)) {
if (preg_match("/^https?:\/\/imgur.com\/(a|album)\/[^\.]+$/", $entry->getAttribute("href"), $matches)) {
$album_content = fetch_file_contents($entry->getAttribute("href"),
false, false, false, false, 10);
@ -88,11 +88,11 @@ class Af_RedditImgur extends Plugin {
if ($adoc) {
$axpath = new DOMXPath($adoc);
$aentries = $axpath->query("//div[@class='image']//a[@href and @class='zoom']");
$aentries = $axpath->query("//meta[@property='og:image']");
foreach ($aentries as $aentry) {
$img = $doc->createElement('img');
$img->setAttribute("src", $aentry->getAttribute("href"));
$img->setAttribute("src", $aentry->getAttribute("content"));
$entry->parentNode->insertBefore($doc->createElement('br'), $entry);
$br = $doc->createElement('br');

View file

@ -0,0 +1,79 @@
<?php
class Af_Tumblr_1280 extends Plugin {
private $host;
function about() {
return array(1.0,
"Replace Tumblr pictures with largest size if available",
"fox");
}
function init($host) {
$this->host = $host;
if (function_exists("curl_init")) {
$host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
}
}
function hook_article_filter($article) {
$owner_uid = $article["owner_uid"];
$charset_hack = '<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>';
$doc = new DOMDocument();
$doc->loadHTML($charset_hack . $article["content"]);
$found = false;
if ($doc) {
$xpath = new DOMXpath($doc);
$images = $xpath->query('(//img[contains(@src, \'media.tumblr.com\')])');
foreach ($images as $img) {
$src = $img->getAttribute("src");
$test_src = preg_replace("/_\d{3}.(jpg|gif|png)/", "_1280.$1", $src);
if ($src != $test_src) {
$ch = curl_init($test_src);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,
!ini_get("safe_mode") && !ini_get("open_basedir"));
curl_setopt($ch, CURLOPT_USERAGENT, SELF_USER_AGENT);
@$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($result && $http_code == 200) {
$img->setAttribute("src", $test_src);
$found = true;
}
}
}
if ($found) {
$doc->removeChild($doc->firstChild); //remove doctype
$article["content"] = $doc->saveHTML();
}
}
return $article;
}
function api_version() {
return 2;
}
}
?>

View file

@ -0,0 +1,88 @@
<?php
class Af_Zz_ImgSetSizes extends Plugin {
private $host;
function about() {
return array(1.0,
"Set width/height attributes for images in articles (requires CURL and GD)",
"fox");
}
function init($host) {
$this->host = $host;
if (function_exists("curl_init") && function_exists("getimagesize")) {
$host->add_hook($host::HOOK_ARTICLE_FILTER, $this);
}
}
function hook_article_filter($article) {
$owner_uid = $article["owner_uid"];
$charset_hack = '<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>';
$doc = new DOMDocument();
$doc->loadHTML($charset_hack . $article["content"]);
$found = false;
if ($doc) {
$xpath = new DOMXpath($doc);
$images = $xpath->query('(//img[@src])');
foreach ($images as $img) {
$src = $img->getAttribute("src");
$ch = curl_init($src);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
curl_setopt($ch, CURLOPT_RANGE, "0-32768");
@$result = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($result && ($http_code == 200 || $http_code == 206)) {
$filename = tempnam(sys_get_temp_dir(), "ttsizecheck");
if ($filename) {
$fh = fopen($filename, "w");
if ($fh) {
fwrite($fh, $result);
fclose($fh);
@$info = getimagesize($filename);
if ($info && $info[0] > 0 && $info[1] > 0) {
$img->setAttribute("width", $info[0]);
$img->setAttribute("height", $info[1]);
$found = true;
}
unlink($filename);
}
}
}
}
if ($found) {
$doc->removeChild($doc->firstChild); //remove doctype
$article["content"] = $doc->saveHTML();
}
}
return $article;
}
function api_version() {
return 2;
}
}
?>

View file

@ -189,6 +189,8 @@ class Import_Export extends Plugin implements IHandler {
$num_processed = 0;
$num_feeds_created = 0;
libxml_disable_entity_loader(false);
$doc = @DOMDocument::load($filename);
if (!$doc) {
@ -206,6 +208,8 @@ class Import_Export extends Plugin implements IHandler {
$doc = DOMDocument::loadXML($data);
}
libxml_disable_entity_loader(true);
if ($doc) {
$xpath = new DOMXpath($doc);

View file

@ -4,7 +4,7 @@ class No_Iframes extends Plugin {
function about() {
return array(1.0,
"Remove embedded iframes",
"Remove embedded iframes (unless whitelisted)",
"fox");
}
@ -16,7 +16,13 @@ class No_Iframes extends Plugin {
function hook_sanitize($doc, $site_url, $allowed_elements, $disallowed_attributes) {
$allowed_elements = array_diff($allowed_elements, array("iframe"));
$xpath = new DOMXpath($doc);
$entries = $xpath->query('//iframe');
foreach ($entries as $entry) {
if (!iframe_whitelisted($entry))
$entry->parentNode->removeChild($entry);
}
return array($doc, $allowed_elements, $disallowed_attributes);
}

View file

@ -1,395 +0,0 @@
<?php
class Updater extends Plugin {
private $host;
function about() {
return array(1.0,
"Updates tt-rss installation to latest version.",
"fox",
true);
}
function init($host) {
$this->host = $host;
$host->add_hook($host::HOOK_PREFS_TAB, $this);
$host->add_command("update-self",
"update tt-rss installation to latest version",
$this);
}
function update_self_step($step, $params, $force = false) {
// __FILE__ is in plugins/updater so we need to go one level up
$work_dir = dirname(dirname(dirname(__FILE__)));
$parent_dir = dirname($work_dir);
// Set PATH to run "which"
putenv('PATH="$PATH:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin"');
$log = array();
if (!is_array($params)) $params = array();
$stop = false;
if (!chdir($work_dir)) {
array_push($log, "Unable to change to work directory: $work_dir");
$stop = true;
}
if (!$stop) {
switch ($step) {
case 0:
array_push($log, "Work directory: $work_dir");
if (!is_writable($work_dir) || !is_writable("$parent_dir")) {
$user = posix_getpwuid(posix_geteuid());
$user = $user["name"];
array_push($log, "Both tt-rss and parent directories should be writable as current user ($user).");
$stop = true; break;
}
if (!file_exists("$work_dir/config.php") || !file_exists("$work_dir/include/sanity_check.php")) {
array_push($log, "Work directory $work_dir doesn't look like tt-rss installation.");
$stop = true; break;
}
if (!is_writable(sys_get_temp_dir())) {
array_push($log, "System temporary directory should be writable as current user.");
$stop = true; break;
}
// bah, also humbug
putenv("PATH=" . getenv("PATH") . PATH_SEPARATOR . "/bin" .
PATH_SEPARATOR . "/usr/bin");
array_push($log, "Checking for tar...");
$system_rc = 0;
system("which tar >/dev/null", $system_rc);
if ($system_rc != 0) {
array_push($log, "Could not run tar executable (RC=$system_rc).");
$stop = true; break;
}
array_push($log, "Checking for gunzip...");
$system_rc = 0;
system("which gunzip >/dev/null", $system_rc);
if ($system_rc != 0) {
array_push($log, "Could not run gunzip executable (RC=$system_rc).");
$stop = true; break;
}
array_push($log, "Checking for latest version...");
$version_info = json_decode(fetch_file_contents("http://tt-rss.org/version.php"),
true);
if (!is_array($version_info)) {
array_push($log, "Unable to fetch version information.");
$stop = true; break;
}
$target_version = $version_info["version"];
$target_dir = "$parent_dir/Tiny-Tiny-RSS-$target_version";
array_push($log, "Target version: $target_version");
$params["target_version"] = $target_version;
if (version_compare(VERSION, $target_version) != -1 && !$force) {
array_push($log, "Your Tiny Tiny RSS installation is up to date.");
$stop = true; break;
}
if (file_exists($target_dir)) {
array_push($log, "Target directory $target_dir already exists.");
$stop = true; break;
}
break;
case 1:
$target_version = $params["target_version"];
/* array_push($log, "Downloading checksums...");
$md5sum_data = fetch_file_contents("http://tt-rss.org/download/md5sum.txt");
if (!$md5sum_data) {
array_push($log, "Could not download checksums.");
$stop = true; break;
}
$md5sum_data = explode("\n", $md5sum_data);
foreach ($md5sum_data as $line) {
$pair = explode(" ", $line);
if ($pair[1] == "tt-rss-$target_version.tar.gz") {
$target_md5sum = $pair[0];
break;
}
}
if (!$target_md5sum) {
array_push($log, "Unable to locate checksum for target version.");
$stop = true; break;
}
$params["target_md5sum"] = $target_md5sum; */
array_push($log, "Proceeding to download...");
break;
case 2:
$target_version = $params["target_version"];
// $target_md5sum = $params["target_md5sum"];
array_push($log, "Downloading distribution tarball...");
$tarball_url = "https://github.com/gothfox/Tiny-Tiny-RSS/archive/$target_version.tar.gz";
$data = fetch_file_contents($tarball_url);
if (!$data) {
array_push($log, "Could not download distribution tarball ($tarball_url).");
$stop = true; break;
}
/* array_push($log, "Verifying tarball checksum...");
$test_md5sum = md5($data);
if ($test_md5sum != $target_md5sum) {
array_push($log, "Downloaded checksum doesn't match (got $test_md5sum, expected $target_md5sum).");
$stop = true; break;
} */
$tmp_file = tempnam(sys_get_temp_dir(), 'tt-rss');
array_push($log, "Saving download to $tmp_file");
if (!file_put_contents($tmp_file, $data)) {
array_push($log, "Unable to save download.");
$stop = true; break;
}
$params["tmp_file"] = $tmp_file;
break;
case 3:
$tmp_file = $params["tmp_file"];
$target_version = $params["target_version"];
if (!chdir($parent_dir)) {
array_push($log, "Unable to change into parent directory.");
$stop = true; break;
}
array_push($log, "Extracting tarball...");
system("tar zxf $tmp_file", $system_rc);
if ($system_rc != 0) {
array_push($log, "Error while extracting tarball (RC=$system_rc).");
$stop = true; break;
}
$target_dir = "$parent_dir/Tiny-Tiny-RSS-$target_version";
if (!is_dir($target_dir)) {
array_push($log, "Target directory ($target_dir) not found.");
$stop = true; break;
}
$old_dir = tmpdirname($parent_dir, "tt-rss-old");
array_push($log, "Renaming tt-rss directory to ".basename($old_dir));
if (!rename($work_dir, $old_dir)) {
array_push($log, "Unable to rename tt-rss directory.");
$stop = true; break;
}
array_push($log, "Renaming target directory...");
if (!rename($target_dir, $work_dir)) {
array_push($log, "Unable to rename target directory.");
$stop = true; break;
}
if (!chdir($work_dir)) {
array_push($log, "Unable to change to work directory: $work_dir");
$stop = true; break;
}
array_push($log, "Copying config.php...");
if (!copy("$old_dir/config.php", "$work_dir/config.php")) {
array_push($log, "Unable to copy config.php to $work_dir.");
$stop = true; break;
}
array_push($log, "Cleaning up...");
unlink($tmp_file);
array_push($log, "Fixing permissions...");
$directories = array(
CACHE_DIR,
CACHE_DIR . "/export",
CACHE_DIR . "/images",
CACHE_DIR . "/js",
CACHE_DIR . "/simplepie",
CACHE_DIR . "/upload",
ICONS_DIR,
LOCK_DIRECTORY);
foreach ($directories as $dir) {
array_push($log, "-> $dir");
chmod($dir, 0777);
}
if (ICONS_DIR == "feed-icons") {
array_push($log, "Migrating feed icons...");
$icons = glob("$old_dir/feed-icons/*.ico");
$icons_copied = 0;
foreach ($icons as $icon) {
$icon = basename($icon);
if (copy("$old_dir/feed-icons/$icon", "$work_dir/feed-icons/$icon")) {
++$icons_copied;
}
}
array_push($log, "Done; $icons_copied files copied");
} else {
array_push($log, "Not migrating feed icons, ICONS_DIR modified.");
}
array_push($log, "Upgrade completed.");
array_push($log, "Your old tt-rss directory is saved at $old_dir. ".
"Please migrate locally modified files (if any) and remove it.");
array_push($log, "You might need to re-enter current directory in shell to see new files.");
$stop = true;
break;
default:
$stop = true;
}
}
return array("step" => $step, "stop" => $stop, "params" => $params, "log" => $log);
}
function update_self_cli($force = false) {
$step = 0;
$stop = false;
$params = array();
while (!$stop) {
$rc = $this->update_self_step($step, $params, $force);
$params = $rc['params'];
$stop = $rc['stop'];
foreach ($rc['log'] as $line) {
_debug($line);
}
++$step;
}
}
function update_self($args) {
_debug("READ THE FOLLOWING BEFORE CONTINUING!");
_debug("* It is suggested to backup your tt-rss directory first.");
_debug("* Your database will not be modified.");
_debug("* Your current tt-rss installation directory will not be modified. It will be renamed and left in the parent directory. You will be able to migrate all your customized files after update finishes.");
_debug("Type 'yes' to continue.");
$input = read_stdin();
if ($input != 'yes' && $input != 'force')
exit;
$this->update_self_cli($input == 'force');
}
function get_prefs_js() {
return file_get_contents(dirname(__FILE__) . "/updater.js");
}
function hook_prefs_tab($args) {
if ($args != "prefPrefs") return;
if (($_SESSION["access_level"] >= 10 || SINGLE_USER_MODE) && CHECK_FOR_NEW_VERSION) {
print "<div dojoType=\"dijit.layout.AccordionPane\" title=\"".__('Update Tiny Tiny RSS')."\">";
if ($_SESSION["pref_last_version_check"] + 86400 + rand(-1000, 1000) < time()) {
$_SESSION["version_data"] = @check_for_update();
$_SESSION["pref_last_version_check"] = time();
}
if (is_array($_SESSION["version_data"])) {
$version = $_SESSION["version_data"]["version"];
$version_id = $_SESSION["version_data"]["version_id"];
print_notice(T_sprintf("New version of Tiny Tiny RSS is available (%s).", "<b>$version</b>"));
$details = "http://tt-rss.org/redmine/versions/$version_id";
print "<p><button onclick=\"window.open('$details')\" dojoType=\"dijit.form.Button\">".__("See the release notes")."</button>";
print " <button dojoType=\"dijit.form.Button\" onclick=\"return updateSelf()\">".
__('Update Tiny Tiny RSS')."</button></p>";
} else {
print_notice(__("Your Tiny Tiny RSS installation is up to date."));
print "<br/> <button dojoType=\"dijit.form.Button\" onclick=\"return updateSelf()\">".
__('Force update')."</button></p>";
}
print "</div>"; #pane
}
}
function updateSelf() {
print_warning(__("Do not close this dialog until updating is finished."));
print "<form style='display : block' name='self_update_form' id='self_update_form'>";
print "<style type='text/css'>
li.notice { font-style : italic; color : red; }
</style>";
print "<ul class='selfUpdateList' id='self_update_log'>";
print "<li class='notice'>" .__("It is suggested to backup your tt-rss directory first.") . "</li>";
print "<li class='notice'>" . __("Your database will not be modified.") . "</li>";
print "<li class='notice'>" . __("Your current tt-rss installation directory will not be modified. It will be renamed and left in the parent directory. You will be able to migrate all your customized files after update finishes.") . "</li>";
print "<li>" . __("Ready to update.") . "</li>";
print "</ul>";
print "<div class='dlgButtons'>";
print "<button id=\"self_update_start_btn\" dojoType=\"dijit.form.Button\" onclick=\"return dijit.byId('updateSelfDlg').start()\" >".
__("Start update")."</button>";
print "<button id=\"self_update_stop_btn\" onclick=\"return dijit.byId('updateSelfDlg').close()\" dojoType=\"dijit.form.Button\">".
__("Close this window")."</button>";
print "</div>";
print "</form>";
}
function performUpdate() {
$step = (int) $_REQUEST["step"];
$params = json_decode($_REQUEST["params"], true);
$force = (bool) $_REQUEST["force"];
if (($_SESSION["access_level"] >= 10 || SINGLE_USER_MODE) && CHECK_FOR_NEW_VERSION) {
print json_encode($this->update_self_step($step, $params, $force));
}
}
function api_version() {
return 2;
}
}
?>

View file

@ -1,69 +0,0 @@
function updateSelf() {
try {
var query = "backend.php?op=pluginhandler&plugin=updater&method=updateSelf";
if (dijit.byId("updateSelfDlg"))
dijit.byId("updateSelfDlg").destroyRecursive();
var dialog = new dijit.Dialog({
id: "updateSelfDlg",
title: __("Update Tiny Tiny RSS"),
style: "width: 600px",
closable: false,
performUpdate: function(step) {
dijit.byId("self_update_start_btn").attr("disabled", true);
dijit.byId("self_update_stop_btn").attr("disabled", true);
notify_progress("Loading, please wait...", true);
new Ajax.Request("backend.php", {
parameters: "op=pluginhandler&plugin=updater&method=performUpdate&step=" + step +
"&params=" + param_escape(JSON.stringify(dialog.attr("update-params"))),
onComplete: function(transport) {
try {
rv = JSON.parse(transport.responseText);
if (rv) {
notify('');
rv['log'].each(function(line) {
$("self_update_log").innerHTML += "<li>" + line + "</li>";
});
dialog.attr("update-params", rv['params']);
if (!rv['stop']) {
window.setTimeout("dijit.byId('updateSelfDlg').performUpdate("+(step+1)+")", 500);
} else {
dijit.byId("self_update_stop_btn").attr("disabled", false);
}
} else {
console.log(transport.responseText);
notify_error("Received invalid data from server.");
}
dialog.attr("updated", true);
} catch (e) {
exception_error("updateSelf/inner", e);
}
} });
},
close: function() {
if (dialog.attr("updated")) {
window.location.reload();
} else {
dialog.hide();
}
},
start: function() {
if (prompt(__("Backup your tt-rss directory before continuing. Please type 'yes' to continue.")) == 'yes') {
dialog.performUpdate(0);
}
},
href: query});
dialog.show();
} catch (e) {
exception_error("batchSubscribe", e);
}
}

View file

@ -41,7 +41,7 @@
<?php if ($_SESSION["uid"]) {
$theme = get_pref( "USER_CSS_THEME", $_SESSION["uid"], false);
if ($theme && file_exists("themes/$theme")) {
if ($theme && theme_valid("$theme")) {
echo stylesheet_tag("themes/$theme");
} else {
echo stylesheet_tag("themes/default.css");
@ -94,7 +94,7 @@
<body id="ttrssPrefs" class="claro">
<div id="notify" class="notify" style="display : none"></div>
<div id="notify" class="notify"></div>
<div id="cmdline" style="display : none"></div>
<div id="overlay">

View file

@ -133,9 +133,7 @@ create table ttrss_feeds (id integer not null auto_increment primary key,
view_settings varchar(250) not null default '',
pubsub_state integer not null default 0,
favicon_last_checked datetime default null,
index(owner_uid),
foreign key (owner_uid) references ttrss_users(id) ON DELETE CASCADE,
index(cat_id),
foreign key (cat_id) references ttrss_feed_categories(id) ON DELETE SET NULL,
index(parent_feed),
foreign key (parent_feed) references ttrss_feeds(id) ON DELETE SET NULL) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
@ -167,7 +165,6 @@ create table ttrss_entries (id integer not null primary key auto_increment,
author varchar(250) not null default '') ENGINE=InnoDB DEFAULT CHARSET=UTF8;
create index ttrss_entries_date_entered_index on ttrss_entries(date_entered);
create index ttrss_entries_guid_index on ttrss_entries(guid);
create index ttrss_entries_updated_idx on ttrss_entries(updated);
create table ttrss_user_entries (
@ -187,13 +184,10 @@ create table ttrss_user_entries (
last_marked datetime,
last_published datetime,
unread bool not null default 1,
index (ref_id),
foreign key (ref_id) references ttrss_entries(id) ON DELETE CASCADE,
index (feed_id),
foreign key (feed_id) references ttrss_feeds(id) ON DELETE CASCADE,
index (orig_feed_id),
foreign key (orig_feed_id) references ttrss_archived_feeds(id) ON DELETE SET NULL,
index (owner_uid),
foreign key (owner_uid) references ttrss_users(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
create index ttrss_user_entries_owner_uid_index on ttrss_user_entries(owner_uid);
@ -301,7 +295,7 @@ create table ttrss_tags (id integer primary key auto_increment,
create table ttrss_version (schema_version int not null) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
insert into ttrss_version values (126);
insert into ttrss_version values (127);
create table ttrss_enclosures (id integer primary key auto_increment,
content_url text not null,
@ -311,7 +305,6 @@ create table ttrss_enclosures (id integer primary key auto_increment,
duration text not null,
width integer not null default 0,
height integer not null default 0,
index (post_id),
foreign key (post_id) references ttrss_entries(id) ON DELETE cascade) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
create index ttrss_enclosures_post_id_idx on ttrss_enclosures(post_id);
@ -347,8 +340,6 @@ create table ttrss_prefs (pref_name varchar(250) not null primary key,
index(section_id),
foreign key (section_id) references ttrss_prefs_sections(id)) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
create index ttrss_prefs_pref_name_idx on ttrss_prefs(pref_name);
insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('PURGE_OLD_DAYS', 3, '60', 1);
insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DEFAULT_UPDATE_INTERVAL', 3, '30', 1);
insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DEFAULT_ARTICLE_LIMIT', 3, '30', 2);
@ -418,18 +409,15 @@ create table ttrss_user_prefs (
profile integer,
index (profile),
foreign key (profile) references ttrss_settings_profiles(id) ON DELETE CASCADE,
index (owner_uid),
foreign key (owner_uid) references ttrss_users(id) ON DELETE CASCADE,
index (pref_name),
foreign key (pref_name) references ttrss_prefs(pref_name) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
create index ttrss_user_prefs_owner_uid_index on ttrss_user_prefs(owner_uid);
create index ttrss_user_prefs_pref_name_idx on ttrss_user_prefs(pref_name);
create table ttrss_sessions (id varchar(250) unique not null primary key,
create table ttrss_sessions (id varchar(250) not null primary key,
data text,
expire integer not null,
index (id),
index (expire)) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
create table ttrss_feedbrowser_cache (

View file

@ -148,7 +148,6 @@ create table ttrss_entries (id serial not null primary key,
lang varchar(2),
author varchar(250) not null default '');
create index ttrss_entries_guid_index on ttrss_entries(guid);
-- create index ttrss_entries_title_index on ttrss_entries(title);
create index ttrss_entries_date_entered_index on ttrss_entries(date_entered);
create index ttrss_entries_updated_idx on ttrss_entries(updated);
@ -261,7 +260,7 @@ create index ttrss_tags_post_int_id_idx on ttrss_tags(post_int_id);
create table ttrss_version (schema_version int not null);
insert into ttrss_version values (126);
insert into ttrss_version values (127);
create table ttrss_enclosures (id serial not null primary key,
content_url text not null,
@ -300,8 +299,6 @@ create table ttrss_prefs (pref_name varchar(250) not null primary key,
access_level integer not null default 0,
def_value text not null);
create index ttrss_prefs_pref_name_idx on ttrss_prefs(pref_name);
insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('PURGE_OLD_DAYS', 3, '60', 1);
insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DEFAULT_UPDATE_INTERVAL', 3, '30', 1);
insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DEFAULT_ARTICLE_LIMIT', 3, '30', 2);
@ -374,7 +371,7 @@ create index ttrss_user_prefs_owner_uid_index on ttrss_user_prefs(owner_uid);
create index ttrss_user_prefs_pref_name_idx on ttrss_user_prefs(pref_name);
-- create index ttrss_user_prefs_value_index on ttrss_user_prefs(value);
create table ttrss_sessions (id varchar(250) unique not null primary key,
create table ttrss_sessions (id varchar(250) not null primary key,
data text,
expire integer not null);

View file

@ -0,0 +1,18 @@
BEGIN;
ALTER TABLE ttrss_enclosures DROP INDEX post_id;
ALTER TABLE ttrss_entries DROP INDEX ttrss_entries_guid_index;
ALTER TABLE ttrss_feeds DROP INDEX owner_uid;
ALTER TABLE ttrss_feeds DROP INDEX cat_id;
ALTER TABLE ttrss_prefs DROP INDEX ttrss_prefs_pref_name_idx;
ALTER TABLE ttrss_sessions DROP INDEX id_2;
ALTER TABLE ttrss_sessions DROP INDEX id;
ALTER TABLE ttrss_user_entries DROP INDEX ref_id;
ALTER TABLE ttrss_user_entries DROP INDEX owner_uid;
ALTER TABLE ttrss_user_entries DROP INDEX feed_id;
ALTER TABLE ttrss_user_prefs DROP INDEX pref_name;
ALTER TABLE ttrss_user_prefs DROP INDEX owner_uid;
UPDATE ttrss_version SET schema_version = 127;
COMMIT;

View file

@ -0,0 +1,8 @@
BEGIN;
DROP INDEX ttrss_entries_guid_index;
DROP INDEX ttrss_prefs_pref_name_idx;
UPDATE ttrss_version SET schema_version = 127;
COMMIT;

View file

@ -3,12 +3,17 @@
body#ttrssMain #feeds-holder {
background : #222;
border-color : #666;
border-left-width : 1px;
}
body#ttrssMain #headlines-frame {
border-color : #ddd;
}
body#ttrssMain div.whiteBox {
border-color : #666;
}
body#ttrssMain #content-insert {
background : #333;
}
@ -132,6 +137,7 @@ body#ttrssMain #feedTree img,
body#ttrssMain .postContent img {
filter: grayscale(1);
-webkit-filter: grayscale(1);
filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); // firefox lol
}
body#ttrssMain .hl img.hlScorePic {

View file

@ -5,6 +5,8 @@ xgettext -kT_js_decl -kT_sprintf -k_ngettext:1,2 -kT_ngettext:1,2 -k__ -L PHP -o
xgettext --from-code utf-8 -k__ -knotify_info -knotify_progress -kngettext -L Perl -j -o $TEMPLATE js/*.js `find plugins -iname '*.js'`
xgettext --from-code utf-8 -k__ -knotify_info -knotify_progress -kngettext -L Java -j -o $TEMPLATE js/*.js `find plugins -iname '*.js'`
update_lang() {
if [ -f $1.po ]; then
msgmerge --no-wrap --width 1 -U $1.po $TEMPLATE