summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/launch.json23
-rw-r--r--api/index.php2
-rwxr-xr-xclasses/api.php21
-rwxr-xr-xclasses/article.php2
-rw-r--r--classes/config.php27
-rw-r--r--classes/db/prefs.php167
-rw-r--r--classes/dbupdater.php3
-rw-r--r--classes/digest.php10
-rwxr-xr-xclasses/feeds.php74
-rwxr-xr-xclasses/handler/public.php23
-rwxr-xr-xclasses/logger.php27
-rw-r--r--classes/logger/adapter.php4
-rwxr-xr-xclasses/logger/sql.php12
-rw-r--r--classes/logger/stdout.php6
-rw-r--r--classes/logger/syslog.php6
-rw-r--r--classes/mailer.php2
-rw-r--r--classes/opml.php2
-rwxr-xr-xclasses/pref/feeds.php24
-rwxr-xr-xclasses/pref/filters.php2
-rw-r--r--classes/pref/prefs.php268
-rw-r--r--classes/pref/system.php2
-rw-r--r--classes/prefs.php407
-rwxr-xr-xclasses/rpc.php119
-rwxr-xr-xclasses/rssutils.php121
-rw-r--r--classes/sanitizer.php2
-rw-r--r--classes/timehelper.php12
-rw-r--r--classes/userhelper.php24
-rw-r--r--include/controls_compat.php2
-rw-r--r--include/errorhandler.php12
-rw-r--r--include/functions.php82
-rw-r--r--include/sessions.php20
-rw-r--r--index.php4
-rw-r--r--js/App.js16
-rwxr-xr-xjs/Headlines.js12
-rwxr-xr-xjs/common.js16
-rw-r--r--plugins/auth_remote/init.php2
-rw-r--r--plugins/close_button/init.php2
-rw-r--r--prefs.php4
-rw-r--r--schema/ttrss_schema_mysql.sql83
-rw-r--r--schema/ttrss_schema_pgsql.sql86
-rw-r--r--schema/versions/mysql/141.sql9
-rw-r--r--schema/versions/pgsql/141.sql11
-rwxr-xr-xupdate.php6
-rwxr-xr-xupdate_daemon2.php4
44 files changed, 933 insertions, 830 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 0e10b4b23..b3911320d 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -2,6 +2,15 @@
"version": "0.2.0",
"configurations": [
{
+ "name": "Listen for XDebug",
+ "type": "php",
+ "request": "launch",
+ "pathMappings": {
+ "/var/www/html/tt-rss": "${workspaceRoot}",
+ },
+ "port": 9000
+ },
+ {
"name": "Launch Chrome",
"request": "launch",
"type": "chrome",
@@ -10,14 +19,6 @@
},
"urlFilter": "*/tt-rss/*",
"runtimeExecutable": "chrome.exe",
- },
- {
- "name": "Listen for XDebug",
- "type": "php",
- "request": "launch",
- "pathMappings": {
- "/var/www/html/tt-rss": "${workspaceRoot}",
- },
- "port": 9000
- }]
-} \ No newline at end of file
+ }
+ ]
+}
diff --git a/api/index.php b/api/index.php
index d85a1103c..430082f16 100644
--- a/api/index.php
+++ b/api/index.php
@@ -23,7 +23,7 @@
if (!empty($_REQUEST["sid"])) {
session_id($_REQUEST["sid"]);
- @session_start();
+ session_start();
}
startup_gettext();
diff --git a/classes/api.php b/classes/api.php
index 18f9c83b5..a0ee773c1 100755
--- a/classes/api.php
+++ b/classes/api.php
@@ -36,7 +36,7 @@ class API extends Handler {
return false;
}
- if (!empty($_SESSION["uid"]) && $method != "logout" && !get_pref('ENABLE_API_ACCESS')) {
+ if (!empty($_SESSION["uid"]) && $method != "logout" && !get_pref(Prefs::ENABLE_API_ACCESS)) {
$this->_wrap(self::STATUS_ERR, array("error" => self::E_API_DISABLED));
return false;
}
@@ -59,25 +59,24 @@ class API extends Handler {
}
function login() {
- @session_destroy();
- @session_start();
+
+ if (session_status() == PHP_SESSION_ACTIVE) {
+ session_destroy();
+ }
+
+ session_start();
$login = clean($_REQUEST["user"]);
$password = clean($_REQUEST["password"]);
- $password_base64 = base64_decode(clean($_REQUEST["password"]));
if (Config::get(Config::SINGLE_USER_MODE)) $login = "admin";
if ($uid = UserHelper::find_user_by_login($login)) {
- if (get_pref("ENABLE_API_ACCESS", $uid)) {
- if (UserHelper::authenticate($login, $password, false, Auth_Base::AUTH_SERVICE_API)) { // try login with normal password
+ if (get_pref(Prefs::ENABLE_API_ACCESS, $uid)) {
+ if (UserHelper::authenticate($login, $password, false, Auth_Base::AUTH_SERVICE_API)) {
$this->_wrap(self::STATUS_OK, array("session_id" => session_id(),
"api_level" => self::API_LEVEL));
- } else if (UserHelper::authenticate($login, $password_base64, false, Auth_Base::AUTH_SERVICE_API)) { // else try with base64_decoded password
- $this->_wrap(self::STATUS_OK, array("session_id" => session_id(),
- "api_level" => self::API_LEVEL));
- } else { // else we are not logged in
- user_error("Failed login attempt for $login from " . UserHelper::get_user_ip(), E_USER_WARNING);
+ } else {
$this->_wrap(self::STATUS_ERR, array("error" => self::E_LOGIN_ERROR));
}
} else {
diff --git a/classes/article.php b/classes/article.php
index 6baf8f068..d8ae97257 100755
--- a/classes/article.php
+++ b/classes/article.php
@@ -349,7 +349,7 @@ class Article extends Handler_Protected {
$rv['can_inline'] = isset($_SESSION["uid"]) &&
empty($_SESSION["bw_limit"]) &&
- !get_pref("STRIP_IMAGES") &&
+ !get_pref(Prefs::STRIP_IMAGES) &&
($always_display_enclosures || !preg_match("/<img/i", $article_content));
$rv['inline_text_only'] = $hide_images && $rv['can_inline'];
diff --git a/classes/config.php b/classes/config.php
index effbb78ad..ee1d3cb4a 100644
--- a/classes/config.php
+++ b/classes/config.php
@@ -107,14 +107,19 @@ class Config {
private static $instance;
private $params = [];
+ private $schema_version = null;
- public static function get_instance() {
+ public static function get_instance() : Config {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
+ private function __clone() {
+ //
+ }
+
function __construct() {
$ref = new ReflectionClass(get_class($this));
@@ -124,12 +129,26 @@ class Config {
list ($defval, $deftype) = $this::_DEFAULTS[$const];
- $this->params[$cvalue] = [ $this->cast_to(!empty($override) ? $override : $defval, $deftype), $deftype ];
+ $this->params[$cvalue] = [ self::cast_to(!empty($override) ? $override : $defval, $deftype), $deftype ];
}
}
}
- private function cast_to(string $value, int $type_hint) {
+ static function get_schema_version(bool $nocache = false) {
+ return self::get_instance()->_schema_version($nocache);
+ }
+
+ function _schema_version(bool $nocache = false) {
+ if (empty($this->schema_version) || $nocache) {
+ $row = Db::pdo()->query("SELECT schema_version FROM ttrss_version")->fetch();
+
+ $this->schema_version = (int) $row["schema_version"];
+ }
+
+ return $this->schema_version;
+ }
+
+ static function cast_to(string $value, int $type_hint) {
switch ($type_hint) {
case self::T_BOOL:
return sql_bool_to_bool($value);
@@ -149,7 +168,7 @@ class Config {
private function _add(string $param, string $default, int $type_hint) {
$override = getenv($this::_ENVVAR_PREFIX . $param);
- $this->params[$param] = [ $this->cast_to(!empty($override) ? $override : $default, $type_hint), $type_hint ];
+ $this->params[$param] = [ self::cast_to(!empty($override) ? $override : $default, $type_hint), $type_hint ];
}
static function add(string $param, string $default, int $type_hint = Config::T_STRING) {
diff --git a/classes/db/prefs.php b/classes/db/prefs.php
index 44581dbcb..821216622 100644
--- a/classes/db/prefs.php
+++ b/classes/db/prefs.php
@@ -1,173 +1,12 @@
<?php
class Db_Prefs {
- private $pdo;
- private static $instance;
- private $cache;
-
- function __construct() {
- $this->pdo = Db::pdo();
- $this->cache = [];
- $this->cache_prefs();
- }
-
- private function __clone() {
- //
- }
-
- public static function get() {
- if (self::$instance == null)
- self::$instance = new self();
-
- return self::$instance;
- }
-
- private function cache_prefs() {
- if (!empty($_SESSION["uid"])) {
- $profile = $_SESSION["profile"] ?? false;
-
- if (!is_numeric($profile) || !$profile || get_schema_version() < 63) $profile = null;
-
- $sth = $this->pdo->prepare("SELECT up.pref_name, pt.type_name, up.value
- FROM ttrss_user_prefs up
- JOIN ttrss_prefs p ON (up.pref_name = p.pref_name)
- JOIN ttrss_prefs_types pt ON (p.type_id = pt.id)
- WHERE
- up.pref_name NOT LIKE '_MOBILE%' AND
- (profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND
- owner_uid = :uid");
-
- $sth->execute([":profile" => $profile, ":uid" => $_SESSION["uid"]]);
-
- while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
- $pref_name = $row["pref_name"];
-
- $this->cache[$pref_name] = [
- "type" => $row["type_name"],
- "value" => $row["value"]
- ];
- }
- }
- }
+ // this class is a stub for the time being (to be removed)
function read($pref_name, $user_id = false, $die_on_error = false) {
-
- if (!$user_id) {
- $user_id = $_SESSION["uid"];
- $profile = $_SESSION["profile"] ?? false;
- } else {
- $profile = false;
- }
-
- if ($user_id == ($_SESSION['uid'] ?? false) && isset($this->cache[$pref_name])) {
- $tuple = $this->cache[$pref_name];
- return $this->convert($tuple["value"], $tuple["type"]);
- }
-
- if (!is_numeric($profile) || !$profile || get_schema_version() < 63) $profile = null;
-
- $sth = $this->pdo->prepare("SELECT up.pref_name, pt.type_name, up.value
- FROM ttrss_user_prefs up
- JOIN ttrss_prefs p ON (up.pref_name = p.pref_name)
- JOIN ttrss_prefs_types pt ON (p.type_id = pt.id)
- WHERE
- up.pref_name = :pref_name AND
- (profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND
- owner_uid = :uid");
-
- $sth->execute([":uid" => $user_id, ":profile" => $profile, ":pref_name" => $pref_name]);
-
- if ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
- $value = $row["value"];
- $type_name = $row["type_name"];
-
- if ($user_id == ($_SESSION["uid"] ?? false)) {
- $this->cache[$pref_name] = [
- "type" => $row["type_name"],
- "value" => $row["value"]
- ];
- }
-
- return $this->convert($value, $type_name);
-
- } else if ($die_on_error) {
- user_error("Failed retrieving preference $pref_name for user $user_id", E_USER_ERROR);
- } else {
- user_error("Failed retrieving preference $pref_name for user $user_id", E_USER_WARNING);
- }
-
- return null;
- }
-
- function convert($value, $type_name) {
- if ($type_name == "bool") {
- return $value == "true";
- } else if ($type_name == "integer") {
- return (int)$value;
- } else {
- return $value;
- }
+ return get_pref($pref_name, $user_id);
}
function write($pref_name, $value, $user_id = false, $strip_tags = true) {
- if ($strip_tags) $value = strip_tags($value);
-
- if (!$user_id) {
- $user_id = $_SESSION["uid"];
- @$profile = $_SESSION["profile"] ?? false;
- } else {
- $profile = null;
- }
-
- if (!is_numeric($profile) || !$profile || get_schema_version() < 63) $profile = null;
-
- $type_name = "";
- $current_value = "";
-
- if (isset($this->cache[$pref_name])) {
- $type_name = $this->cache[$pref_name]["type"];
- $current_value = $this->cache[$pref_name]["value"];
- }
-
- if (!$type_name) {
- $sth = $this->pdo->prepare("SELECT type_name
- FROM ttrss_prefs,ttrss_prefs_types
- WHERE pref_name = ? AND type_id = ttrss_prefs_types.id");
- $sth->execute([$pref_name]);
-
- if ($row = $sth->fetch())
- $type_name = $row["type_name"];
-
- } else if ($current_value == $value) {
- return;
- }
-
- if ($type_name) {
- if ($type_name == "bool") {
- if ($value == "1" || $value == "true") {
- $value = "true";
- } else {
- $value = "false";
- }
- } else if ($type_name == "integer") {
- $value = (int)$value;
- }
-
- if ($pref_name == 'USER_TIMEZONE' && $value == '') {
- $value = 'UTC';
- }
-
- $sth = $this->pdo->prepare("UPDATE ttrss_user_prefs SET
- value = :value WHERE pref_name = :pref_name
- AND (profile = :profile OR (:profile IS NULL AND profile IS NULL))
- AND owner_uid = :uid");
-
- $sth->execute([":pref_name" => $pref_name, ":value" => $value, ":uid" => $user_id, ":profile" => $profile]);
-
- if ($user_id == $_SESSION["uid"]) {
- $this->cache[$pref_name]["type"] = $type_name;
- $this->cache[$pref_name]["value"] = $value;
- }
- }
+ return set_pref($pref_name, $value, $user_id, $strip_tags);
}
-
}
diff --git a/classes/dbupdater.php b/classes/dbupdater.php
index e923c7fcb..d1df31b40 100644
--- a/classes/dbupdater.php
+++ b/classes/dbupdater.php
@@ -12,8 +12,7 @@ class DbUpdater {
}
function get_schema_version() {
- $row = $this->pdo->query("SELECT schema_version FROM ttrss_version")->fetch();
- return (int) $row['schema_version'];
+ return Config::get_schema_version(true);
}
function is_update_required() {
diff --git a/classes/digest.php b/classes/digest.php
index a6a0c47de..26ca5221f 100644
--- a/classes/digest.php
+++ b/classes/digest.php
@@ -21,8 +21,8 @@ class Digest
while ($line = $res->fetch()) {
- if (@get_pref('DIGEST_ENABLE', $line['id'], false)) {
- $preferred_ts = strtotime(get_pref('DIGEST_PREFERRED_TIME', $line['id'], '00:00'));
+ if (get_pref(Prefs::DIGEST_ENABLE, $line['id'])) {
+ $preferred_ts = strtotime(get_pref(Prefs::DIGEST_PREFERRED_TIME, $line['id']));
// try to send digests within 2 hours of preferred time
if ($preferred_ts && time() >= $preferred_ts &&
@@ -31,7 +31,7 @@ class Digest
Debug::log("Sending digest for UID:" . $line['id'] . " - " . $line["email"]);
- $do_catchup = get_pref('DIGEST_CATCHUP', $line['id'], false);
+ $do_catchup = get_pref(Prefs::DIGEST_CATCHUP, $line['id']);
global $tz_offset;
@@ -86,7 +86,7 @@ class Digest
$tpl->readTemplateFromFile("digest_template_html.txt");
$tpl_t->readTemplateFromFile("digest_template.txt");
- $user_tz_string = get_pref('USER_TIMEZONE', $user_id);
+ $user_tz_string = get_pref(Prefs::USER_TIMEZONE, $user_id);
$local_ts = TimeHelper::convert_timestamp(time(), 'UTC', $user_tz_string);
$tpl->setVariable('CUR_DATE', date('Y/m/d', $local_ts));
@@ -152,7 +152,7 @@ class Digest
$updated = TimeHelper::make_local_datetime($line['last_updated'], false,
$user_id);
- if (get_pref('ENABLE_FEED_CATS', $user_id)) {
+ if (get_pref(Prefs::ENABLE_FEED_CATS, $user_id)) {
$line['feed_title'] = $line['cat_title'] . " / " . $line['feed_title'];
}
diff --git a/classes/feeds.php b/classes/feeds.php
index ba2719f48..b1b19500f 100755
--- a/classes/feeds.php
+++ b/classes/feeds.php
@@ -108,7 +108,7 @@ class Feeds extends Handler_Protected {
$this->_mark_timestamp("db query");
- $vfeed_group_enabled = get_pref("VFEED_GROUP_BY_FEED") &&
+ $vfeed_group_enabled = get_pref(Prefs::VFEED_GROUP_BY_FEED) &&
!(in_array($feed, self::NEVER_GROUP_FEEDS) && !$cat_view);
$result = $qfh_ret[0]; // this could be either a PDO query result or a -1 if first id changed
@@ -167,7 +167,7 @@ class Feeds extends Handler_Protected {
++$headlines_count;
- if (!get_pref('SHOW_CONTENT_PREVIEW')) {
+ if (!get_pref(Prefs::SHOW_CONTENT_PREVIEW)) {
$line["content_preview"] = "";
} else {
$line["content_preview"] = "&mdash; " . truncate_string(strip_tags($line["content"]), 250);
@@ -208,15 +208,19 @@ class Feeds extends Handler_Protected {
if ($label_cache) {
if ($label_cache["no-labels"] ?? false == 1)
- $labels = array();
+ $labels = [];
else
$labels = $label_cache;
}
+
+ $line["labels"] = $labels;
+ } else {
+ $line["labels"] = [];
}
- if (!is_array($labels)) $labels = Article::_get_labels($id);
+ /*if (!is_array($labels)) $labels = Article::_get_labels($id);
- $line["labels"] = Article::_get_labels($id);
+ $line["labels"] = Article::_get_labels($id);*/
if (count($topmost_article_ids) < 3) {
array_push($topmost_article_ids, $id);
@@ -262,22 +266,26 @@ class Feeds extends Handler_Protected {
$this->_mark_timestamp(" note");
- if (!get_pref("CDM_EXPANDED")) {
+ if (!get_pref(Prefs::CDM_EXPANDED)) {
$line["cdm_excerpt"] = "<span class='collapse'>
<i class='material-icons' onclick='return Article.cdmUnsetActive(event)'
title=\"" . __("Collapse article") . "\">remove_circle</i></span>";
- if (get_pref('SHOW_CONTENT_PREVIEW')) {
+ if (get_pref(Prefs::SHOW_CONTENT_PREVIEW)) {
$line["cdm_excerpt"] .= "<span class='excerpt'>" . $line["content_preview"] . "</span>";
}
}
$this->_mark_timestamp(" pre-enclosures");
- $line["enclosures"] = Article::_format_enclosures($id,
- $line["always_display_enclosures"],
- $line["content"],
- $line["hide_images"]);
+ if ($line["num_enclosures"] > 0) {
+ $line["enclosures"] = Article::_format_enclosures($id,
+ $line["always_display_enclosures"],
+ $line["content"],
+ $line["hide_images"]);
+ } else {
+ $line["enclosures"] = [ 'formatted' => '', 'entries' => [] ];
+ }
$this->_mark_timestamp(" enclosures");
@@ -292,9 +300,11 @@ class Feeds extends Handler_Protected {
if ($line["tag_cache"])
$tags = explode(",", $line["tag_cache"]);
else
- $tags = false;
+ $tags = [];
- $line["tags"] = Article::_get_tags($line["id"], false, $line["tag_cache"]);
+ $line["tags"] = $tags;
+
+ //$line["tags"] = Article::_get_tags($line["id"], false, $line["tag_cache"]);
$this->_mark_timestamp(" tags");
@@ -324,7 +334,7 @@ class Feeds extends Handler_Protected {
/* we don't need those */
foreach (["date_entered", "guid", "last_published", "last_marked", "tag_cache", "favicon_avg_color",
- "uuid", "label_cache", "yyiw"] as $k)
+ "uuid", "label_cache", "yyiw", "num_enclosures"] as $k)
unset($line[$k]);
array_push($reply['content'], $line);
@@ -413,7 +423,7 @@ class Feeds extends Handler_Protected {
$feed = $_REQUEST["feed"];
$method = $_REQUEST["m"] ?? "";
- $view_mode = $_REQUEST["view_mode"];
+ $view_mode = $_REQUEST["view_mode"] ?? "";
$limit = 30;
$cat_view = $_REQUEST["cat"] == "true";
$next_unread_feed = $_REQUEST["nuf"] ?? 0;
@@ -459,8 +469,8 @@ class Feeds extends Handler_Protected {
return;
}
- set_pref("_DEFAULT_VIEW_MODE", $view_mode);
- set_pref("_DEFAULT_VIEW_ORDER_BY", $order_by);
+ set_pref(Prefs::_DEFAULT_VIEW_MODE, $view_mode);
+ set_pref(Prefs::_DEFAULT_VIEW_ORDER_BY, $order_by);
/* bump login timestamp if needed */
if (time() - $_SESSION["last_login_update"] > 3600) {
@@ -499,7 +509,7 @@ class Feeds extends Handler_Protected {
"disable_cache" => (bool) $disable_cache];
// this is parsed by handleRpcJson() on first viewfeed() to set cdm expanded, etc
- $reply['runtime-info'] = RPC::make_runtime_info();
+ $reply['runtime-info'] = RPC::_make_runtime_info();
print json_encode($reply);
}
@@ -573,7 +583,7 @@ class Feeds extends Handler_Protected {
"show_language" => Config::get(Config::DB_TYPE) == "pgsql",
"show_syntax_help" => count(PluginHost::getInstance()->get_hooks(PluginHost::HOOK_SEARCH)) == 0,
"all_languages" => Pref_Feeds::get_ts_languages(),
- "default_language" => get_pref('DEFAULT_SEARCH_LANGUAGE')
+ "default_language" => get_pref(Prefs::DEFAULT_SEARCH_LANGUAGE)
]);
}
@@ -799,7 +809,7 @@ class Feeds extends Handler_Protected {
if ($feed == -3) {
- $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE");
+ $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE);
if (Config::get(Config::DB_TYPE) == "pgsql") {
$match_part = "date_entered > NOW() - INTERVAL '$intl hour' ";
@@ -892,7 +902,7 @@ class Feeds extends Handler_Protected {
} else if ($n_feed == -3) {
$match_part = "unread = true AND score >= 0";
- $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
+ $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE, $owner_uid);
if (Config::get(Config::DB_TYPE) == "pgsql") {
$match_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
@@ -1478,7 +1488,7 @@ class Feeds extends Handler_Protected {
} else if ($feed == -3) { // fresh virtual feed
$query_strategy_part = "unread = true AND score >= 0";
- $intl = (int) get_pref("FRESH_ARTICLE_MAX_AGE", $owner_uid);
+ $intl = (int) get_pref(Prefs::FRESH_ARTICLE_MAX_AGE, $owner_uid);
if (Config::get(Config::DB_TYPE) == "pgsql") {
$query_strategy_part .= " AND date_entered > NOW() - INTERVAL '$intl hour' ";
@@ -1566,7 +1576,7 @@ class Feeds extends Handler_Protected {
if (is_numeric($feed)) {
// proper override_order applied above
- if ($vfeed_query_part && !$ignore_vfeed_group && get_pref('VFEED_GROUP_BY_FEED', $owner_uid)) {
+ if ($vfeed_query_part && !$ignore_vfeed_group && get_pref(Prefs::VFEED_GROUP_BY_FEED, $owner_uid)) {
if (!(in_array($feed, self::NEVER_GROUP_BY_DATE) && !$cat_view)) {
$yyiw_desc = $order_by == "date_reverse" ? "" : "desc";
@@ -1583,7 +1593,7 @@ class Feeds extends Handler_Protected {
}
if (!$allow_archived) {
- $from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id),ttrss_feeds";
+ $from_qpart = "${ext_tables_part}ttrss_entries LEFT JOIN ttrss_user_entries ON (ref_id = ttrss_entries.id), ttrss_feeds";
$feed_check_qpart = "ttrss_user_entries.feed_id = ttrss_feeds.id AND";
} else {
@@ -1611,6 +1621,11 @@ class Feeds extends Handler_Protected {
$distinct_qpart = "DISTINCT"; //fallback
}
+ // except for Labels category
+ if (get_pref(Prefs::HEADLINES_NO_DISTINCT) && !($feed == -2 && $cat_view)) {
+ $distinct_qpart = "";
+ }
+
if (!$search && !$skip_first_id_check) {
// if previous topmost article id changed that means our current pagination is no longer valid
$query = "SELECT
@@ -1675,7 +1690,8 @@ class Feeds extends Handler_Protected {
last_marked, last_published,
$vfeed_query_part
$content_query_part
- author,score
+ author,score,
+ (SELECT count(id) FROM ttrss_enclosures WHERE post_id = ttrss_entries.id) AS num_enclosures
FROM
$from_qpart
WHERE
@@ -1975,7 +1991,7 @@ class Feeds extends Handler_Protected {
$purge_unread = true;
$purge_interval = Config::get(Config::FORCE_ARTICLE_PURGE);
} else {
- $purge_unread = get_pref("PURGE_UNREAD_ARTICLES", $owner_uid, false);
+ $purge_unread = get_pref(Prefs::PURGE_UNREAD_ARTICLES, $owner_uid);
}
$purge_interval = (int) $purge_interval;
@@ -2038,7 +2054,7 @@ class Feeds extends Handler_Protected {
$owner_uid = $row["owner_uid"];
if ($purge_interval == 0)
- $purge_interval = get_pref('PURGE_OLD_DAYS', $owner_uid, false);
+ $purge_interval = get_pref(Prefs::PURGE_OLD_DAYS, $owner_uid);
return $purge_interval;
} else {
@@ -2058,7 +2074,7 @@ class Feeds extends Handler_Protected {
if ($search_language)
$search_language = $pdo->quote(mb_strtolower($search_language));
else
- $search_language = $pdo->quote(mb_strtolower(get_pref('DEFAULT_SEARCH_LANGUAGE', $owner_uid)));
+ $search_language = $pdo->quote(mb_strtolower(get_pref(Prefs::DEFAULT_SEARCH_LANGUAGE, $owner_uid)));
foreach ($keywords as $k) {
if (strpos($k, "-") === 0) {
@@ -2166,7 +2182,7 @@ class Feeds extends Handler_Protected {
default:
if (strpos($k, "@") === 0) {
- $user_tz_string = get_pref('USER_TIMEZONE', $_SESSION['uid']);
+ $user_tz_string = get_pref(Prefs::USER_TIMEZONE, $_SESSION['uid']);
$orig_ts = strtotime(substr($k, 1));
$k = date("Y-m-d", TimeHelper::convert_timestamp($orig_ts, $user_tz_string, 'UTC'));
diff --git a/classes/handler/public.php b/classes/handler/public.php
index 42be6f713..e4572382e 100755
--- a/classes/handler/public.php
+++ b/classes/handler/public.php
@@ -40,7 +40,7 @@ class Handler_Public extends Handler {
if (!$is_cat && is_numeric($feed) && $feed < PLUGIN_FEED_BASE_INDEX && $feed > LABEL_BASE_INDEX) {
- $user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
+ $user_plugins = get_pref(Prefs::_ENABLED_PLUGINS, $owner_uid);
$tmppluginhost = new PluginHost();
$tmppluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL);
@@ -354,20 +354,22 @@ class Handler_Public extends Handler {
$remember_me = clean($_POST["remember_me"] ?? false);
$safe_mode = checkbox_to_sql_bool(clean($_POST["safe_mode"] ?? false));
- if ($remember_me) {
- @session_set_cookie_params(Config::get(Config::SESSION_COOKIE_LIFETIME));
- } else {
- @session_set_cookie_params(0);
+ if (session_status() != PHP_SESSION_ACTIVE) {
+ if ($remember_me) {
+ session_set_cookie_params(Config::get(Config::SESSION_COOKIE_LIFETIME));
+ } else {
+ session_set_cookie_params(0);
+ }
}
if (UserHelper::authenticate($login, $password)) {
$_POST["password"] = "";
if (get_schema_version() >= 120) {
- $_SESSION["language"] = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
+ $_SESSION["language"] = get_pref(Prefs::USER_LANGUAGE, $_SESSION["uid"]);
}
- $_SESSION["ref_schema_version"] = get_schema_version(true);
+ $_SESSION["ref_schema_version"] = get_schema_version();
$_SESSION["bw_limit"] = !!clean($_POST["bw_limit"] ?? false);
$_SESSION["safe_mode"] = $safe_mode;
@@ -388,12 +390,11 @@ class Handler_Public extends Handler {
} else {
// start an empty session to deliver login error message
- @session_start();
+ if (session_status() != PHP_SESSION_ACTIVE)
+ session_start();
if (!isset($_SESSION["login_error_msg"]))
$_SESSION["login_error_msg"] = __("Incorrect username or password");
-
- user_error("Failed login attempt for $login from " . UserHelper::get_user_ip(), E_USER_WARNING);
}
$return = clean($_REQUEST['return']);
@@ -613,7 +614,7 @@ class Handler_Public extends Handler {
function dbupdate() {
startup_gettext();
- if (!Config::get(Config::SINGLE_USER_MODE) && $_SESSION["access_level"] < 10) {
+ if (!Config::get(Config::SINGLE_USER_MODE) && ($_SESSION["access_level"] ?? 0) < 10) {
$_SESSION["login_error_msg"] = __("Your access level is insufficient to run this script.");
$this->_render_login_form();
exit;
diff --git a/classes/logger.php b/classes/logger.php
index 6cc33314d..864b66743 100755
--- a/classes/logger.php
+++ b/classes/logger.php
@@ -3,7 +3,7 @@ class Logger {
private static $instance;
private $adapter;
- public static $errornames = array(
+ const ERROR_NAMES = [
1 => 'E_ERROR',
2 => 'E_WARNING',
4 => 'E_PARSE',
@@ -19,10 +19,14 @@ class Logger {
4096 => 'E_RECOVERABLE_ERROR',
8192 => 'E_DEPRECATED',
16384 => 'E_USER_DEPRECATED',
- 32767 => 'E_ALL');
+ 32767 => 'E_ALL'];
- function log_error($errno, $errstr, $file, $line, $context) {
- if ($errno == E_NOTICE) return false;
+ static function log_error(int $errno, string $errstr, string $file, int $line, $context) {
+ return self::get_instance()->_log_error($errno, $errstr, $file, $line, $context);
+ }
+
+ private function _log_error($errno, $errstr, $file, $line, $context) {
+ //if ($errno == E_NOTICE) return false;
if ($this->adapter)
return $this->adapter->log_error($errno, $errstr, $file, $line, $context);
@@ -30,7 +34,11 @@ class Logger {
return false;
}
- function log($errno, $errstr, $context = "") {
+ static function log(int $errno, string $errstr, $context = "") {
+ return self::get_instance()->_log($errno, $errstr, $context);
+ }
+
+ private function _log(int $errno, string $errstr, $context = "") {
if ($this->adapter)
return $this->adapter->log_error($errno, $errstr, '', 0, $context);
else
@@ -55,13 +63,20 @@ class Logger {
default:
$this->adapter = false;
}
+
+ if ($this->adapter && !implements_interface($this->adapter, "Logger_Adapter"))
+ user_error("Adapter for LOG_DESTINATION: " . Config::LOG_DESTINATION . " does not implement required interface.", E_USER_ERROR);
}
- public static function get() {
+ private static function get_instance() : Logger {
if (self::$instance == null)
self::$instance = new self();
return self::$instance;
}
+ static function get() : Logger {
+ user_error("Please don't use Logger::get(), call Logger::log(...) instead.", E_USER_DEPRECATED);
+ return self::get_instance();
+ }
}
diff --git a/classes/logger/adapter.php b/classes/logger/adapter.php
new file mode 100644
index 000000000..79f641441
--- /dev/null
+++ b/classes/logger/adapter.php
@@ -0,0 +1,4 @@
+<?php
+interface Logger_Adapter {
+ function log_error(int $errno, string $errstr, string $file, int $line, $context);
+} \ No newline at end of file
diff --git a/classes/logger/sql.php b/classes/logger/sql.php
index ad7fdecb2..d21934aa6 100755
--- a/classes/logger/sql.php
+++ b/classes/logger/sql.php
@@ -1,17 +1,15 @@
<?php
-class Logger_SQL {
+class Logger_SQL implements Logger_Adapter {
private $pdo;
- function log_error($errno, $errstr, $file, $line, $context) {
+ function log_error(int $errno, string $errstr, string $file, int $line, $context) {
// separate PDO connection object is used for logging
if (!$this->pdo) $this->pdo = Db::instance()->pdo_connect();
if ($this->pdo && get_schema_version() > 117) {
- $owner_uid = $_SESSION["uid"] ?? null;
-
// limit context length, DOMDocument dumps entire XML in here sometimes, which may be huge
$context = mb_substr($context, 0, 8192);
@@ -34,10 +32,14 @@ class Logger_SQL {
$errstr = UConverter::transcode($errstr, 'UTF-8', 'UTF-8');
$context = UConverter::transcode($context, 'UTF-8', 'UTF-8');
+ // can't use $_SESSION["uid"] ?? null because what if its, for example, false? or zero?
+ // this would cause a PDOException on insert below
+ $owner_uid = !empty($_SESSION["uid"]) ? $_SESSION["uid"] : null;
+
$sth = $this->pdo->prepare("INSERT INTO ttrss_error_log
(errno, errstr, filename, lineno, context, owner_uid, created_at) VALUES
(?, ?, ?, ?, ?, ?, NOW())");
- $sth->execute([$errno, $errstr, $file, $line, $context, $owner_uid]);
+ $sth->execute([$errno, $errstr, $file, (int)$line, $context, $owner_uid]);
return $sth->rowCount();
}
diff --git a/classes/logger/stdout.php b/classes/logger/stdout.php
index 4dac2e598..e906853ce 100644
--- a/classes/logger/stdout.php
+++ b/classes/logger/stdout.php
@@ -1,7 +1,7 @@
<?php
-class Logger_Stdout {
+class Logger_Stdout implements Logger_Adapter {
- function log_error($errno, $errstr, $file, $line, $context) {
+ function log_error(int $errno, string $errstr, string $file, int $line, $context) {
switch ($errno) {
case E_ERROR:
@@ -21,7 +21,7 @@ class Logger_Stdout {
$priority = LOG_INFO;
}
- $errname = Logger::$errornames[$errno] . " ($errno)";
+ $errname = Logger::ERROR_NAMES[$errno] . " ($errno)";
print "[EEE] $priority $errname ($file:$line) $errstr\n";
diff --git a/classes/logger/syslog.php b/classes/logger/syslog.php
index f1e151548..3ad9858f3 100644
--- a/classes/logger/syslog.php
+++ b/classes/logger/syslog.php
@@ -1,7 +1,7 @@
<?php
-class Logger_Syslog {
+class Logger_Syslog implements Logger_Adapter {
- function log_error($errno, $errstr, $file, $line, $context) {
+ function log_error(int $errno, string $errstr, string $file, int $line, $context) {
switch ($errno) {
case E_ERROR:
@@ -21,7 +21,7 @@ class Logger_Syslog {
$priority = LOG_INFO;
}
- $errname = Logger::$errornames[$errno] . " ($errno)";
+ $errname = Logger::ERROR_NAMES[$errno] . " ($errno)";
syslog($priority, "[tt-rss] $errname ($file:$line) $errstr");
diff --git a/classes/mailer.php b/classes/mailer.php
index 93f778210..a4270ba88 100644
--- a/classes/mailer.php
+++ b/classes/mailer.php
@@ -20,7 +20,7 @@ class Mailer {
$to_combined = $to_name ? "$to_name <$to_address>" : $to_address;
if (Config::get(Config::LOG_SENT_MAIL))
- Logger::get()->log(E_USER_NOTICE, "Sending mail from $from_combined to $to_combined [$subject]: $message");
+ Logger::log(E_USER_NOTICE, "Sending mail from $from_combined to $to_combined [$subject]: $message");
// HOOK_SEND_MAIL plugin instructions:
// 1. return 1 or true if mail is handled
diff --git a/classes/opml.php b/classes/opml.php
index cbc1269e3..6c7cab606 100644
--- a/classes/opml.php
+++ b/classes/opml.php
@@ -153,7 +153,7 @@ class OPML extends Handler_Protected {
if ($include_settings) {
$out .= "<outline text=\"tt-rss-prefs\" schema-version=\"".SCHEMA_VERSION."\">";
- $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs WHERE
+ $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs2 WHERE
profile IS NULL AND owner_uid = ? ORDER BY pref_name");
$sth->execute([$owner_uid]);
diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php
index 086c52697..3b4afab26 100755
--- a/classes/pref/feeds.php
+++ b/classes/pref/feeds.php
@@ -122,7 +122,7 @@ class Pref_Feeds extends Handler_Protected {
$root['param'] = 0;
$root['type'] = 'category';
- $enable_cats = get_pref('ENABLE_FEED_CATS');
+ $enable_cats = get_pref(Prefs::ENABLE_FEED_CATS);
if (clean($_REQUEST['mode'] ?? 0) == 2) {
@@ -171,7 +171,7 @@ class Pref_Feeds extends Handler_Protected {
ttrss_labels2 WHERE owner_uid = ? ORDER by caption");
$sth->execute([$_SESSION['uid']]);
- if (get_pref('ENABLE_FEED_CATS')) {
+ if (get_pref(Prefs::ENABLE_FEED_CATS)) {
$cat = $this->feedlist_init_cat(-2);
} else {
$cat['items'] = array();
@@ -527,11 +527,11 @@ class Pref_Feeds extends Handler_Protected {
$row["icon"] = Feeds::_get_icon($feed_id);
$local_update_intervals = $update_intervals;
- $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]);
+ $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref(Prefs::DEFAULT_UPDATE_INTERVAL)]);
if (Config::get(Config::FORCE_ARTICLE_PURGE) == 0) {
$local_purge_intervals = $purge_intervals;
- $default_purge_interval = get_pref("PURGE_OLD_DAYS");
+ $default_purge_interval = get_pref(Prefs::PURGE_OLD_DAYS);
if ($default_purge_interval > 0)
$local_purge_intervals[0] .= " " . T_nsprintf('(%d day)', '(%d days)', $default_purge_interval, $default_purge_interval);
@@ -546,7 +546,7 @@ class Pref_Feeds extends Handler_Protected {
print json_encode([
"feed" => $row,
"cats" => [
- "enabled" => get_pref('ENABLE_FEED_CATS'),
+ "enabled" => get_pref(Prefs::ENABLE_FEED_CATS),
"select" => \Controls\select_feeds_cats("cat_id", $row["cat_id"]),
],
"plugin_data" => $plugin_data,
@@ -557,7 +557,7 @@ class Pref_Feeds extends Handler_Protected {
],
"lang" => [
"enabled" => Config::get(Config::DB_TYPE) == "pgsql",
- "default" => get_pref('DEFAULT_SEARCH_LANGUAGE'),
+ "default" => get_pref(Prefs::DEFAULT_SEARCH_LANGUAGE),
"all" => $this::get_ts_languages(),
]
]);
@@ -576,10 +576,10 @@ class Pref_Feeds extends Handler_Protected {
$feed_ids = clean($_REQUEST["ids"]);
$local_update_intervals = $update_intervals;
- $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]);
+ $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref(Prefs::DEFAULT_UPDATE_INTERVAL)]);
$local_purge_intervals = $purge_intervals;
- $default_purge_interval = get_pref("PURGE_OLD_DAYS");
+ $default_purge_interval = get_pref(Prefs::PURGE_OLD_DAYS);
if ($default_purge_interval > 0)
$local_purge_intervals[0] .= " " . T_sprintf("(%d days)", $default_purge_interval);
@@ -604,7 +604,7 @@ class Pref_Feeds extends Handler_Protected {
<div dojoType="dijit.layout.TabContainer" style="height : 450px">
<div dojoType="dijit.layout.ContentPane" title="<?= __('General') ?>">
<section>
- <?php if (get_pref('ENABLE_FEED_CATS')) { ?>
+ <?php if (get_pref(Prefs::ENABLE_FEED_CATS)) { ?>
<fieldset>
<label><?= __('Place in category:') ?></label>
<?= \Controls\select_feeds_cats("cat_id", null, ['disabled' => '1']) ?>
@@ -830,7 +830,7 @@ class Pref_Feeds extends Handler_Protected {
break;
case "cat_id":
- if (get_pref('ENABLE_FEED_CATS')) {
+ if (get_pref(Prefs::ENABLE_FEED_CATS)) {
if ($cat_id) {
$qpart = "cat_id = " . $this->pdo->quote($cat_id);
} else {
@@ -946,7 +946,7 @@ class Pref_Feeds extends Handler_Protected {
</div>
</div>
- <?php if (get_pref('ENABLE_FEED_CATS')) { ?>
+ <?php if (get_pref(Prefs::ENABLE_FEED_CATS)) { ?>
<div dojoType="fox.form.DropDownButton">
<span><?= __('Categories') ?></span>
<div dojoType="dijit.Menu" style="display: none">
@@ -1237,7 +1237,7 @@ class Pref_Feeds extends Handler_Protected {
function batchSubscribe() {
print json_encode([
- "enable_cats" => (int)get_pref('ENABLE_FEED_CATS'),
+ "enable_cats" => (int)get_pref(Prefs::ENABLE_FEED_CATS),
"cat_select" => \Controls\select_feeds_cats("cat")
]);
}
diff --git a/classes/pref/filters.php b/classes/pref/filters.php
index fda4a6513..a6ea9f982 100755
--- a/classes/pref/filters.php
+++ b/classes/pref/filters.php
@@ -848,7 +848,7 @@ class Pref_Filters extends Handler_Protected {
}
}
- if (get_pref('ENABLE_FEED_CATS')) {
+ if (get_pref(Prefs::ENABLE_FEED_CATS)) {
if (!$root_id) $root_id = null;
diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php
index 0d0dcadbc..ba63d76b3 100644
--- a/classes/pref/prefs.php
+++ b/classes/pref/prefs.php
@@ -6,7 +6,6 @@ class Pref_Prefs extends Handler_Protected {
private $pref_item_map = [];
private $pref_help_bottom = [];
private $pref_blacklist = [];
- private $profile_blacklist = [];
function csrf_ignore($method) {
$csrf_ignored = array("index", "updateself", "otpqrcode");
@@ -19,106 +18,99 @@ class Pref_Prefs extends Handler_Protected {
$this->pref_item_map = [
__('General') => [
- 'USER_LANGUAGE',
- 'USER_TIMEZONE',
+ Prefs::USER_LANGUAGE,
+ Prefs::USER_TIMEZONE,
'BLOCK_SEPARATOR',
- 'USER_CSS_THEME',
+ Prefs::USER_CSS_THEME,
'BLOCK_SEPARATOR',
- 'ENABLE_API_ACCESS',
+ Prefs::ENABLE_API_ACCESS,
],
__('Feeds') => [
- 'DEFAULT_UPDATE_INTERVAL',
- 'FRESH_ARTICLE_MAX_AGE',
- 'DEFAULT_SEARCH_LANGUAGE',
+ Prefs::DEFAULT_UPDATE_INTERVAL,
+ Prefs::FRESH_ARTICLE_MAX_AGE,
+ Prefs::DEFAULT_SEARCH_LANGUAGE,
'BLOCK_SEPARATOR',
- 'ENABLE_FEED_CATS',
+ Prefs::ENABLE_FEED_CATS,
'BLOCK_SEPARATOR',
- 'CONFIRM_FEED_CATCHUP',
- 'ON_CATCHUP_SHOW_NEXT_FEED',
+ Prefs::CONFIRM_FEED_CATCHUP,
+ Prefs::ON_CATCHUP_SHOW_NEXT_FEED,
'BLOCK_SEPARATOR',
- 'HIDE_READ_FEEDS',
- 'HIDE_READ_SHOWS_SPECIAL',
+ Prefs::HIDE_READ_FEEDS,
+ Prefs::HIDE_READ_SHOWS_SPECIAL,
],
__('Articles') => [
- 'PURGE_OLD_DAYS',
- 'PURGE_UNREAD_ARTICLES',
+ Prefs::PURGE_OLD_DAYS,
+ Prefs::PURGE_UNREAD_ARTICLES,
'BLOCK_SEPARATOR',
- 'COMBINED_DISPLAY_MODE',
- 'CDM_EXPANDED',
+ Prefs::COMBINED_DISPLAY_MODE,
+ Prefs::CDM_EXPANDED,
'BLOCK_SEPARATOR',
- 'CDM_AUTO_CATCHUP',
- 'VFEED_GROUP_BY_FEED',
+ Prefs::CDM_AUTO_CATCHUP,
+ Prefs::VFEED_GROUP_BY_FEED,
'BLOCK_SEPARATOR',
- 'SHOW_CONTENT_PREVIEW',
- 'STRIP_IMAGES',
+ Prefs::SHOW_CONTENT_PREVIEW,
+ Prefs::STRIP_IMAGES,
],
__('Digest') => [
- 'DIGEST_ENABLE',
- 'DIGEST_CATCHUP',
- 'DIGEST_PREFERRED_TIME',
+ Prefs::DIGEST_ENABLE,
+ Prefs::DIGEST_CATCHUP,
+ Prefs::DIGEST_PREFERRED_TIME,
],
__('Advanced') => [
- 'BLACKLISTED_TAGS',
+ Prefs::BLACKLISTED_TAGS,
'BLOCK_SEPARATOR',
- 'LONG_DATE_FORMAT',
- 'SHORT_DATE_FORMAT',
+ Prefs::LONG_DATE_FORMAT,
+ Prefs::SHORT_DATE_FORMAT,
'BLOCK_SEPARATOR',
- 'SSL_CERT_SERIAL',
- ]
+ Prefs::SSL_CERT_SERIAL,
+ 'BLOCK_SEPARATOR',
+ Prefs::HEADLINES_NO_DISTINCT,
+ ],
+ __('Debugging') => [
+ Prefs::DEBUG_HEADLINE_IDS,
+ ],
];
$this->pref_help_bottom = [
- "BLACKLISTED_TAGS" => __("Never apply these tags automatically (comma-separated list)."),
+ Prefs::BLACKLISTED_TAGS => __("Never apply these tags automatically (comma-separated list)."),
];
$this->pref_help = [
- "ALLOW_DUPLICATE_POSTS" => array(__("Allow duplicate articles"), ""),
- "BLACKLISTED_TAGS" => array(__("Blacklisted tags"), ""),
- "DEFAULT_SEARCH_LANGUAGE" => array(__("Default language"), __("Used for full-text search")),
- "CDM_AUTO_CATCHUP" => array(__("Mark read on scroll"), __("Mark articles as read as you scroll past them")),
- "CDM_EXPANDED" => array(__("Always expand articles")),
- "COMBINED_DISPLAY_MODE" => array(__("Combined mode"), __("Show flat list of articles instead of separate panels")),
- "CONFIRM_FEED_CATCHUP" => array(__("Confirm marking feeds as read")),
- "DEFAULT_ARTICLE_LIMIT" => array(__("Amount of articles to display at once")),
- "DEFAULT_UPDATE_INTERVAL" => array(__("Default update interval")),
- "DIGEST_CATCHUP" => array(__("Mark sent articles as read")),
- "DIGEST_ENABLE" => array(__("Enable digest"), __("Send daily digest of new (and unread) headlines to your e-mail address")),
- "DIGEST_PREFERRED_TIME" => array(__("Try to send around this time"), __("Time in UTC")),
- "ENABLE_API_ACCESS" => array(__("Enable API"), __("Allows accessing this account through the API")),
- "ENABLE_FEED_CATS" => array(__("Enable categories")),
- "FEEDS_SORT_BY_UNREAD" => array(__("Sort feeds by unread articles count"), ""),
- "FRESH_ARTICLE_MAX_AGE" => array(__("Maximum age of fresh articles"), "<strong>" . __("hours") . "</strong>"),
- "HIDE_READ_FEEDS" => array(__("Hide read feeds")),
- "HIDE_READ_SHOWS_SPECIAL" => array(__("Always show special feeds"), __("While hiding read feeds")),
- "LONG_DATE_FORMAT" => array(__("Long date format"), __("Syntax is identical to PHP <a href='http://php.net/manual/function.date.php'>date()</a> function.")),
- "ON_CATCHUP_SHOW_NEXT_FEED" => array(__("Automatically show next feed"), __("After marking one as read")),
- "PURGE_OLD_DAYS" => array(__("Purge articles older than"), __("<strong>days</strong> (0 disables)")),
- "PURGE_UNREAD_ARTICLES" => array(__("Purge unread articles")),
- "REVERSE_HEADLINES" => array(__("Reverse headline order (oldest first)")),
- "SHORT_DATE_FORMAT" => array(__("Short date format")),
- "SHOW_CONTENT_PREVIEW" => array(__("Show content preview in headlines")),
- "SORT_HEADLINES_BY_FEED_DATE" => array(__("Sort headlines by feed date"), __("Use feed-specified date to sort headlines instead of local import date.")),
- "SSL_CERT_SERIAL" => array(__("SSL client certificate")),
- "STRIP_IMAGES" => array(__("Do not embed media")),
- "STRIP_UNSAFE_TAGS" => array(__("Strip unsafe tags from articles"), __("Strip all but most common HTML tags when reading articles.")),
- "USER_STYLESHEET" => array(__("Customize stylesheet")),
- "USER_TIMEZONE" => array(__("Time zone")),
- "VFEED_GROUP_BY_FEED" => array(__("Group by feed"), __("Group multiple-feed output by originating feed")),
- "USER_LANGUAGE" => array(__("Language")),
- "USER_CSS_THEME" => array(__("Theme"))
+ Prefs::BLACKLISTED_TAGS => array(__("Blacklisted tags"), ""),
+ Prefs::DEFAULT_SEARCH_LANGUAGE => array(__("Default language"), __("Used for full-text search")),
+ Prefs::CDM_AUTO_CATCHUP => array(__("Mark read on scroll"), __("Mark articles as read as you scroll past them")),
+ Prefs::CDM_EXPANDED => array(__("Always expand articles")),
+ Prefs::COMBINED_DISPLAY_MODE => array(__("Combined mode"), __("Show flat list of articles instead of separate panels")),
+ Prefs::CONFIRM_FEED_CATCHUP => array(__("Confirm marking feeds as read")),
+ Prefs::DEFAULT_UPDATE_INTERVAL => array(__("Default update interval")),
+ Prefs::DIGEST_CATCHUP => array(__("Mark sent articles as read")),
+ Prefs::DIGEST_ENABLE => array(__("Enable digest"), __("Send daily digest of new (and unread) headlines to your e-mail address")),
+ Prefs::DIGEST_PREFERRED_TIME => array(__("Try to send around this time"), __("Time in UTC")),
+ Prefs::ENABLE_API_ACCESS => array(__("Enable API"), __("Allows accessing this account through the API")),
+ Prefs::ENABLE_FEED_CATS => array(__("Enable categories")),
+ Prefs::FRESH_ARTICLE_MAX_AGE => array(__("Maximum age of fresh articles"), "<strong>" . __("hours") . "</strong>"),
+ Prefs::HIDE_READ_FEEDS => array(__("Hide read feeds")),
+ Prefs::HIDE_READ_SHOWS_SPECIAL => array(__("Always show special feeds"), __("While hiding read feeds")),
+ Prefs::LONG_DATE_FORMAT => array(__("Long date format"), __("Syntax is identical to PHP <a href='http://php.net/manual/function.date.php'>date()</a> function.")),
+ Prefs::ON_CATCHUP_SHOW_NEXT_FEED => array(__("Automatically show next feed"), __("After marking one as read")),
+ Prefs::PURGE_OLD_DAYS => array(__("Purge articles older than"), __("<strong>days</strong> (0 disables)")),
+ Prefs::PURGE_UNREAD_ARTICLES => array(__("Purge unread articles")),
+ Prefs::SHORT_DATE_FORMAT => array(__("Short date format")),
+ Prefs::SHOW_CONTENT_PREVIEW => array(__("Show content preview in headlines")),
+ Prefs::SSL_CERT_SERIAL => array(__("SSL client certificate")),
+ Prefs::STRIP_IMAGES => array(__("Do not embed media")),
+ Prefs::USER_TIMEZONE => array(__("Time zone")),
+ Prefs::VFEED_GROUP_BY_FEED => array(__("Group by feed"), __("Group multiple-feed output by originating feed")),
+ Prefs::USER_LANGUAGE => array(__("Language")),
+ Prefs::USER_CSS_THEME => array(__("Theme")),
+ Prefs::HEADLINES_NO_DISTINCT => array(__("Don't enforce DISTINCT headlines"), __("May produce duplicate entries")),
+ Prefs::DEBUG_HEADLINE_IDS => array(__("Show article and feed IDs"), __("In the headlines buffer")),
];
- $this->pref_blacklist = ["ALLOW_DUPLICATE_POSTS", "STRIP_UNSAFE_TAGS", "REVERSE_HEADLINES",
- "SORT_HEADLINES_BY_FEED_DATE", "DEFAULT_ARTICLE_LIMIT",
- "FEEDS_SORT_BY_UNREAD", "USER_STYLESHEET"];
-
- /* "FEEDS_SORT_BY_UNREAD", "HIDE_READ_FEEDS", "REVERSE_HEADLINES" */
-
- $this->profile_blacklist = ["ALLOW_DUPLICATE_POSTS", "PURGE_OLD_DAYS",
- "PURGE_UNREAD_ARTICLES", "DIGEST_ENABLE", "DIGEST_CATCHUP",
- "BLACKLISTED_TAGS", "ENABLE_API_ACCESS", "UPDATE_POST_ON_CHECKSUM_CHANGE",
- "DEFAULT_UPDATE_INTERVAL", "USER_TIMEZONE", "SORT_HEADLINES_BY_FEED_DATE",
- "SSL_CERT_SERIAL", "DIGEST_PREFERRED_TIME"];
+ // hidden in the main prefs UI (use to hide things that have description set above)
+ $this->pref_blacklist = [
+ //
+ ];
}
function changepassword() {
@@ -181,31 +173,33 @@ class Pref_Prefs extends Handler_Protected {
$value = $_POST[$pref_name];
switch ($pref_name) {
- case 'DIGEST_PREFERRED_TIME':
- if (get_pref('DIGEST_PREFERRED_TIME') != $value) {
+ case Prefs::DIGEST_PREFERRED_TIME:
+ if (get_pref(Prefs::DIGEST_PREFERRED_TIME) != $value) {
$sth = $this->pdo->prepare("UPDATE ttrss_users SET
- last_digest_sent = NULL WHERE id = ?");
+ last_digest_sent = NULL WHERE id = ?");
$sth->execute([$_SESSION['uid']]);
}
break;
- case 'USER_LANGUAGE':
+ case Prefs::USER_LANGUAGE:
if (!$need_reload) $need_reload = $_SESSION["language"] != $value;
break;
- case 'USER_CSS_THEME':
+ case Prefs::USER_CSS_THEME:
if (!$need_reload) $need_reload = get_pref($pref_name) != $value;
break;
- case 'BLACKLISTED_TAGS':
+ case Prefs::BLACKLISTED_TAGS:
$cats = FeedItem_Common::normalize_categories(explode(",", $value));
asort($cats);
$value = implode(", ", $cats);
break;
}
- set_pref($pref_name, $value);
+ if (Prefs::is_valid($pref_name)) {
+ Prefs::set($pref_name, $value, $_SESSION["uid"], $_SESSION["profile"] ?? null);
+ }
}
if ($need_reload) {
@@ -260,17 +254,9 @@ class Pref_Prefs extends Handler_Protected {
}
function resetconfig() {
+ Prefs::reset($_SESSION["uid"], $_SESSION["profile"]);
- $_SESSION["prefs_op_result"] = "reset-to-defaults";
-
- $sth = $this->pdo->prepare("DELETE FROM ttrss_user_prefs
- WHERE (profile = :profile OR (:profile IS NULL AND profile IS NULL))
- AND owner_uid = :uid");
- $sth->execute([":profile" => $_SESSION['profile'], ":uid" => $_SESSION['uid']]);
-
- $this->_init_user_prefs($_SESSION["uid"], $_SESSION["profile"]);
-
- echo __("Your preferences are now set to default values.");
+ print "PREFS_NEED_RELOAD";
}
private function index_auth_personal() {
@@ -568,35 +554,18 @@ class Pref_Prefs extends Handler_Protected {
if ($profile) {
print_notice(__("Some preferences are only available in default profile."));
- $this->_init_user_prefs($_SESSION["uid"], $profile);
- } else {
- $this->_init_user_prefs($_SESSION["uid"]);
}
$prefs_available = [];
-
- $sth = $this->pdo->prepare("SELECT DISTINCT
- ttrss_user_prefs.pref_name,value,type_name,
- ttrss_prefs_sections.order_id,
- def_value,section_id
- FROM ttrss_prefs,ttrss_prefs_types,ttrss_prefs_sections,ttrss_user_prefs
- WHERE type_id = ttrss_prefs_types.id AND
- (profile = :profile OR (:profile IS NULL AND profile IS NULL)) AND
- section_id = ttrss_prefs_sections.id AND
- ttrss_user_prefs.pref_name = ttrss_prefs.pref_name AND
- owner_uid = :uid
- ORDER BY ttrss_prefs_sections.order_id,pref_name");
- $sth->execute([":uid" => $_SESSION['uid'], ":profile" => $profile]);
-
$listed_boolean_prefs = [];
- while ($line = $sth->fetch()) {
+ foreach (Prefs::get_all($_SESSION["uid"], $profile) as $line) {
if (in_array($line["pref_name"], $this->pref_blacklist)) {
continue;
}
- if ($profile && in_array($line["pref_name"], $this->profile_blacklist)) {
+ if ($profile && in_array($line["pref_name"], Prefs::_PROFILE_BLACKLIST)) {
continue;
}
@@ -607,7 +576,7 @@ class Pref_Prefs extends Handler_Protected {
continue;
$prefs_available[$pref_name] = [
- 'type_name' => $line["type_name"],
+ 'type_hint' => $line['type_hint'],
'value' => $line['value'],
'help_text' => $this->_get_help_text($pref_name),
'short_desc' => $short_desc
@@ -640,7 +609,7 @@ class Pref_Prefs extends Handler_Protected {
print "</label>";
$value = $item['value'];
- $type_name = $item['type_name'];
+ $type_hint = $item['type_hint'];
if ($pref_name == "USER_LANGUAGE") {
print \Controls\select_hash($pref_name, $value, get_translations(),
@@ -701,7 +670,7 @@ class Pref_Prefs extends Handler_Protected {
print \Controls\select_tag($pref_name, $value, Pref_Feeds::get_ts_languages());
- } else if ($type_name == "bool") {
+ } else if ($type_hint == Config::T_BOOL) {
array_push($listed_boolean_prefs, $pref_name);
@@ -726,7 +695,7 @@ class Pref_Prefs extends Handler_Protected {
$attributes = ["required" => true];
}
- if ($type_name == 'integer')
+ if ($type_hint == Config::T_INT)
print \Controls\number_spinner_tag($pref_name, $value, $attributes);
else
print \Controls\input_tag($pref_name, $value, "text", $attributes);
@@ -757,7 +726,7 @@ class Pref_Prefs extends Handler_Protected {
$item['help_text'] .= ". " . T_sprintf("Current server time: %s", date("H:i"));
} else {
- $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : '';
+ $regexp = ($type_hint == Config::T_INT) ? 'regexp="^\d*$"' : '';
print "<input dojoType=\"dijit.form.ValidationTextBox\" $regexp name=\"$pref_name\" value=\"$value\">";
}
@@ -863,7 +832,7 @@ class Pref_Prefs extends Handler_Protected {
private function index_plugins_user() {
$system_enabled = array_map("trim", explode(",", (string)Config::get(Config::PLUGINS)));
- $user_enabled = array_map("trim", explode(",", get_pref("_ENABLED_PLUGINS")));
+ $user_enabled = array_map("trim", explode(",", get_pref(Prefs::_ENABLED_PLUGINS)));
$tmppluginhost = new PluginHost();
$tmppluginhost->load_all($tmppluginhost::KIND_ALL, $_SESSION["uid"], true);
@@ -1164,7 +1133,7 @@ class Pref_Prefs extends Handler_Protected {
else
$plugins = "";
- set_pref("_ENABLED_PLUGINS", $plugins);
+ set_pref(Prefs::_ENABLED_PLUGINS, $plugins);
}
function clearplugindata() {
@@ -1174,7 +1143,7 @@ class Pref_Prefs extends Handler_Protected {
}
function customizeCSS() {
- $value = get_pref("USER_STYLESHEET");
+ $value = get_pref(Prefs::USER_STYLESHEET);
$value = str_replace("<br/>", "\n", $value);
print json_encode(["value" => $value]);
@@ -1219,14 +1188,6 @@ class Pref_Prefs extends Handler_Protected {
$sth = $this->pdo->prepare("SELECT id FROM ttrss_settings_profiles WHERE
title = ? AND owner_uid = ?");
$sth->execute([$title, $_SESSION['uid']]);
-
- if ($row = $sth->fetch()) {
- $profile_id = $row['id'];
-
- if ($profile_id) {
- Pref_Prefs::_init_user_prefs($_SESSION["uid"], $profile_id);
- }
- }
}
$this->pdo->commit();
@@ -1367,57 +1328,4 @@ class Pref_Prefs extends Handler_Protected {
$this->appPasswordList();
}
-
- static function _init_user_prefs($uid, $profile = false) {
-
- if (get_schema_version() < 63) $profile_qpart = "";
-
- $pdo = Db::pdo();
- $in_nested_tr = false;
-
- try {
- $pdo->beginTransaction();
- } catch (Exception $e) {
- $in_nested_tr = true;
- }
-
- $sth = $pdo->query("SELECT pref_name,def_value FROM ttrss_prefs");
-
- if (!is_numeric($profile) || !$profile || get_schema_version() < 63) $profile = null;
-
- $u_sth = $pdo->prepare("SELECT pref_name
- FROM ttrss_user_prefs WHERE owner_uid = :uid AND
- (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
- $u_sth->execute([':uid' => $uid, ':profile' => $profile]);
-
- $active_prefs = array();
-
- while ($line = $u_sth->fetch()) {
- array_push($active_prefs, $line["pref_name"]);
- }
-
- while ($line = $sth->fetch()) {
- if (array_search($line["pref_name"], $active_prefs) === false) {
-// print "adding " . $line["pref_name"] . "<br>";
-
- if (get_schema_version() < 63) {
- $i_sth = $pdo->prepare("INSERT INTO ttrss_user_prefs
- (owner_uid,pref_name,value) VALUES
- (?, ?, ?)");
- $i_sth->execute([$uid, $line["pref_name"], $line["def_value"]]);
-
- } else {
- $i_sth = $pdo->prepare("INSERT INTO ttrss_user_prefs
- (owner_uid,pref_name,value, profile) VALUES
- (?, ?, ?, ?)");
- $i_sth->execute([$uid, $line["pref_name"], $line["def_value"], $profile]);
- }
-
- }
- }
-
- if (!$in_nested_tr) $pdo->commit();
-
- }
-
}
diff --git a/classes/pref/system.php b/classes/pref/system.php
index 67f7133c6..85635e753 100644
--- a/classes/pref/system.php
+++ b/classes/pref/system.php
@@ -129,7 +129,7 @@ class Pref_System extends Handler_Administrative {
?>
<tr>
<td class='errno'>
- <?= Logger::$errornames[$line["errno"]] . " (" . $line["errno"] . ")" ?>
+ <?= Logger::ERROR_NAMES[$line["errno"]] . " (" . $line["errno"] . ")" ?>
</td>
<td class='filename'><?= $line["filename"] . ":" . $line["lineno"] ?></td>
<td class='errstr'><?= $line["errstr"] . "\n" . $line["context"] ?></td>
diff --git a/classes/prefs.php b/classes/prefs.php
new file mode 100644
index 000000000..8eba40ad5
--- /dev/null
+++ b/classes/prefs.php
@@ -0,0 +1,407 @@
+<?php
+class Prefs {
+ // (this is the database-backed version of Config.php)
+
+ const PURGE_OLD_DAYS = "PURGE_OLD_DAYS";
+ const DEFAULT_UPDATE_INTERVAL = "DEFAULT_UPDATE_INTERVAL";
+ //const DEFAULT_ARTICLE_LIMIT = "DEFAULT_ARTICLE_LIMIT";
+ //const ALLOW_DUPLICATE_POSTS = "ALLOW_DUPLICATE_POSTS";
+ const ENABLE_FEED_CATS = "ENABLE_FEED_CATS";
+ const SHOW_CONTENT_PREVIEW = "SHOW_CONTENT_PREVIEW";
+ const SHORT_DATE_FORMAT = "SHORT_DATE_FORMAT";
+ const LONG_DATE_FORMAT = "LONG_DATE_FORMAT";
+ const COMBINED_DISPLAY_MODE = "COMBINED_DISPLAY_MODE";
+ const HIDE_READ_FEEDS = "HIDE_READ_FEEDS";
+ const ON_CATCHUP_SHOW_NEXT_FEED = "ON_CATCHUP_SHOW_NEXT_FEED";
+ const FEEDS_SORT_BY_UNREAD = "FEEDS_SORT_BY_UNREAD";
+ const REVERSE_HEADLINES = "REVERSE_HEADLINES";
+ const DIGEST_ENABLE = "DIGEST_ENABLE";
+ const CONFIRM_FEED_CATCHUP = "CONFIRM_FEED_CATCHUP";
+ const CDM_AUTO_CATCHUP = "CDM_AUTO_CATCHUP";
+ const _DEFAULT_VIEW_MODE = "_DEFAULT_VIEW_MODE";
+ const _DEFAULT_VIEW_LIMIT = "_DEFAULT_VIEW_LIMIT";
+ //const _PREFS_ACTIVE_TAB = "_PREFS_ACTIVE_TAB";
+ //const STRIP_UNSAFE_TAGS = "STRIP_UNSAFE_TAGS";
+ const BLACKLISTED_TAGS = "BLACKLISTED_TAGS";
+ const FRESH_ARTICLE_MAX_AGE = "FRESH_ARTICLE_MAX_AGE";
+ const DIGEST_CATCHUP = "DIGEST_CATCHUP";
+ const CDM_EXPANDED = "CDM_EXPANDED";
+ const PURGE_UNREAD_ARTICLES = "PURGE_UNREAD_ARTICLES";
+ const HIDE_READ_SHOWS_SPECIAL = "HIDE_READ_SHOWS_SPECIAL";
+ const VFEED_GROUP_BY_FEED = "VFEED_GROUP_BY_FEED";
+ const STRIP_IMAGES = "STRIP_IMAGES";
+ const _DEFAULT_VIEW_ORDER_BY = "_DEFAULT_VIEW_ORDER_BY";
+ const ENABLE_API_ACCESS = "ENABLE_API_ACCESS";
+ //const _COLLAPSED_SPECIAL = "_COLLAPSED_SPECIAL";
+ //const _COLLAPSED_LABELS = "_COLLAPSED_LABELS";
+ //const _COLLAPSED_UNCAT = "_COLLAPSED_UNCAT";
+ //const _COLLAPSED_FEEDLIST = "_COLLAPSED_FEEDLIST";
+ //const _MOBILE_ENABLE_CATS = "_MOBILE_ENABLE_CATS";
+ //const _MOBILE_SHOW_IMAGES = "_MOBILE_SHOW_IMAGES";
+ //const _MOBILE_HIDE_READ = "_MOBILE_HIDE_READ";
+ //const _MOBILE_SORT_FEEDS_UNREAD = "_MOBILE_SORT_FEEDS_UNREAD";
+ //const _MOBILE_BROWSE_CATS = "_MOBILE_BROWSE_CATS";
+ //const _THEME_ID = "_THEME_ID";
+ const USER_TIMEZONE = "USER_TIMEZONE";
+ const USER_STYLESHEET = "USER_STYLESHEET";
+ //const SORT_HEADLINES_BY_FEED_DATE = "SORT_HEADLINES_BY_FEED_DATE";
+ const SSL_CERT_SERIAL = "SSL_CERT_SERIAL";
+ const DIGEST_PREFERRED_TIME = "DIGEST_PREFERRED_TIME";
+ //const _PREFS_SHOW_EMPTY_CATS = "_PREFS_SHOW_EMPTY_CATS";
+ const _DEFAULT_INCLUDE_CHILDREN = "_DEFAULT_INCLUDE_CHILDREN";
+ //const AUTO_ASSIGN_LABELS = "AUTO_ASSIGN_LABELS";
+ const _ENABLED_PLUGINS = "_ENABLED_PLUGINS";
+ //const _MOBILE_REVERSE_HEADLINES = "_MOBILE_REVERSE_HEADLINES";
+ const USER_CSS_THEME = "USER_CSS_THEME";
+ const USER_LANGUAGE = "USER_LANGUAGE";
+ const DEFAULT_SEARCH_LANGUAGE = "DEFAULT_SEARCH_LANGUAGE";
+ const _PREFS_MIGRATED = "_PREFS_MIGRATED";
+ const HEADLINES_NO_DISTINCT = "HEADLINES_NO_DISTINCT";
+ const DEBUG_HEADLINE_IDS = "DEBUG_HEADLINE_IDS";
+
+ private const _DEFAULTS = [
+ Prefs::PURGE_OLD_DAYS => [ 60, Config::T_INT ],
+ Prefs::DEFAULT_UPDATE_INTERVAL => [ 30, Config::T_INT ],
+ //Prefs::DEFAULT_ARTICLE_LIMIT => [ 30, Config::T_INT ],
+ //Prefs::ALLOW_DUPLICATE_POSTS => [ false, Config::T_BOOL ],
+ Prefs::ENABLE_FEED_CATS => [ true, Config::T_BOOL ],
+ Prefs::SHOW_CONTENT_PREVIEW => [ true, Config::T_BOOL ],
+ Prefs::SHORT_DATE_FORMAT => [ "M d, G:i", Config::T_STRING ],
+ Prefs::LONG_DATE_FORMAT => [ "D, M d Y - G:i", Config::T_STRING ],
+ Prefs::COMBINED_DISPLAY_MODE => [ true, Config::T_BOOL ],
+ Prefs::HIDE_READ_FEEDS => [ false, Config::T_BOOL ],
+ Prefs::ON_CATCHUP_SHOW_NEXT_FEED => [ false, Config::T_BOOL ],
+ Prefs::FEEDS_SORT_BY_UNREAD => [ false, Config::T_BOOL ],
+ Prefs::REVERSE_HEADLINES => [ false, Config::T_BOOL ],
+ Prefs::DIGEST_ENABLE => [ false, Config::T_BOOL ],
+ Prefs::CONFIRM_FEED_CATCHUP => [ true, Config::T_BOOL ],
+ Prefs::CDM_AUTO_CATCHUP => [ false, Config::T_BOOL ],
+ Prefs::_DEFAULT_VIEW_MODE => [ "adaptive", Config::T_BOOL ],
+ Prefs::_DEFAULT_VIEW_LIMIT => [ 30, Config::T_INT ],
+ //Prefs::_PREFS_ACTIVE_TAB => [ "", Config::T_STRING ],
+ //Prefs::STRIP_UNSAFE_TAGS => [ true, Config::T_BOOL ],
+ Prefs::BLACKLISTED_TAGS => [ 'main, generic, misc, uncategorized, blog, blogroll, general, news', Config::T_STRING ],
+ Prefs::FRESH_ARTICLE_MAX_AGE => [ 24, Config::T_INT ],
+ Prefs::DIGEST_CATCHUP => [ false, Config::T_BOOL ],
+ Prefs::CDM_EXPANDED => [ true, Config::T_BOOL ],
+ Prefs::PURGE_UNREAD_ARTICLES => [ true, Config::T_BOOL ],
+ Prefs::HIDE_READ_SHOWS_SPECIAL => [ true, Config::T_BOOL ],
+ Prefs::VFEED_GROUP_BY_FEED => [ false, Config::T_BOOL ],
+ Prefs::STRIP_IMAGES => [ false, Config::T_BOOL ],
+ Prefs::_DEFAULT_VIEW_ORDER_BY => [ "default", Config::T_STRING ],
+ Prefs::ENABLE_API_ACCESS => [ false, Config::T_BOOL ],
+ //Prefs::_COLLAPSED_SPECIAL => [ false, Config::T_BOOL ],
+ //Prefs::_COLLAPSED_LABELS => [ false, Config::T_BOOL ],
+ //Prefs::_COLLAPSED_UNCAT => [ false, Config::T_BOOL ],
+ //Prefs::_COLLAPSED_FEEDLIST => [ false, Config::T_BOOL ],
+ //Prefs::_MOBILE_ENABLE_CATS => [ false, Config::T_BOOL ],
+ //Prefs::_MOBILE_SHOW_IMAGES => [ false, Config::T_BOOL ],
+ //Prefs::_MOBILE_HIDE_READ => [ false, Config::T_BOOL ],
+ //Prefs::_MOBILE_SORT_FEEDS_UNREAD => [ false, Config::T_BOOL ],
+ //Prefs::_MOBILE_BROWSE_CATS => [ true, Config::T_BOOL ],
+ //Prefs::_THEME_ID => [ 0, Config::T_BOOL ],
+ Prefs::USER_TIMEZONE => [ "Automatic", Config::T_STRING ],
+ Prefs::USER_STYLESHEET => [ "", Config::T_STRING ],
+ //Prefs::SORT_HEADLINES_BY_FEED_DATE => [ false, Config::T_BOOL ],
+ Prefs::SSL_CERT_SERIAL => [ "", Config::T_STRING ],
+ Prefs::DIGEST_PREFERRED_TIME => [ "00:00", Config::T_STRING ],
+ //Prefs::_PREFS_SHOW_EMPTY_CATS => [ false, Config::T_BOOL ],
+ Prefs::_DEFAULT_INCLUDE_CHILDREN => [ false, Config::T_BOOL ],
+ //Prefs::AUTO_ASSIGN_LABELS => [ false, Config::T_BOOL ],
+ Prefs::_ENABLED_PLUGINS => [ "", Config::T_STRING ],
+ //Prefs::_MOBILE_REVERSE_HEADLINES => [ false, Config::T_BOOL ],
+ Prefs::USER_CSS_THEME => [ "" , Config::T_STRING ],
+ Prefs::USER_LANGUAGE => [ "" , Config::T_STRING ],
+ Prefs::DEFAULT_SEARCH_LANGUAGE => [ "" , Config::T_STRING ],
+ Prefs::_PREFS_MIGRATED => [ false, Config::T_BOOL ],
+ Prefs::HEADLINES_NO_DISTINCT => [ false, Config::T_BOOL ],
+ Prefs::DEBUG_HEADLINE_IDS => [ false, Config::T_BOOL ],
+ ];
+
+ const _PROFILE_BLACKLIST = [
+ //Prefs::ALLOW_DUPLICATE_POSTS,
+ Prefs::PURGE_OLD_DAYS,
+ Prefs::PURGE_UNREAD_ARTICLES,
+ Prefs::DIGEST_ENABLE,
+ Prefs::DIGEST_CATCHUP,
+ Prefs::BLACKLISTED_TAGS,
+ Prefs::ENABLE_API_ACCESS,
+ //Prefs::UPDATE_POST_ON_CHECKSUM_CHANGE,
+ Prefs::DEFAULT_UPDATE_INTERVAL,
+ Prefs::USER_TIMEZONE,
+ //Prefs::SORT_HEADLINES_BY_FEED_DATE,
+ Prefs::SSL_CERT_SERIAL,
+ Prefs::DIGEST_PREFERRED_TIME,
+ Prefs::_PREFS_MIGRATED
+ ];
+
+ private static $instance;
+ private $cache = [];
+
+ /** @var PDO */
+ private $pdo;
+
+ public static function get_instance() : Prefs {
+ if (self::$instance == null)
+ self::$instance = new self();
+
+ return self::$instance;
+ }
+
+ static function is_valid(string $pref_name) {
+ return isset(self::_DEFAULTS[$pref_name]);
+ }
+
+ static function get_default(string $pref_name) {
+ if (self::is_valid($pref_name))
+ return self::_DEFAULTS[$pref_name][0];
+ else
+ return null;
+ }
+
+ function __construct() {
+ $this->pdo = Db::pdo();
+
+ if (!empty($_SESSION["uid"])) {
+ $owner_uid = (int) $_SESSION["uid"];
+ $profile_id = $_SESSION["profile"] ?? null;
+
+ $this->cache_all($owner_uid, $profile_id);
+ $this->migrate($owner_uid, $profile_id);
+ };
+ }
+
+ private function __clone() {
+ //
+ }
+
+ static function get_all(int $owner_uid, int $profile_id = null) {
+ return self::get_instance()->_get_all($owner_uid, $profile_id);
+ }
+
+ private function _get_all(int $owner_uid, int $profile_id = null) {
+ $rv = [];
+
+ $ref = new ReflectionClass(get_class($this));
+
+ foreach ($ref->getConstants() as $const => $cvalue) {
+ if (isset($this::_DEFAULTS[$const])) {
+ list ($def_val, $type_hint) = $this::_DEFAULTS[$const];
+
+ array_push($rv, [
+ "pref_name" => $const,
+ "value" => $this->_get($const, $owner_uid, $profile_id),
+ "type_hint" => $type_hint,
+ ]);
+ }
+ }
+
+ return $rv;
+ }
+
+ private function cache_all(int $owner_uid, $profile_id = null) {
+ if (!$profile_id) $profile_id = null;
+
+ // fill cache with defaults
+ $ref = new ReflectionClass(get_class($this));
+ foreach ($ref->getConstants() as $const => $cvalue) {
+ if (isset($this::_DEFAULTS[$const])) {
+ list ($def_val, $type_hint) = $this::_DEFAULTS[$const];
+
+ $this->_set_cache($const, $def_val, $owner_uid, $profile_id);
+ }
+ }
+
+ if (get_schema_version() >= 141) {
+ // fill in any overrides from the database
+ $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs2
+ WHERE owner_uid = :uid AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+
+ $sth->execute(["uid" => $owner_uid, "profile" => $profile_id]);
+
+ while ($row = $sth->fetch()) {
+ $this->_set_cache($row["pref_name"], $row["value"], $owner_uid, $profile_id);
+ }
+ }
+ }
+
+ static function get(string $pref_name, int $owner_uid, int $profile_id = null) {
+ return self::get_instance()->_get($pref_name, $owner_uid, $profile_id);
+ }
+
+ private function _get(string $pref_name, int $owner_uid, int $profile_id = null) {
+ if (isset(self::_DEFAULTS[$pref_name])) {
+ if (!$profile_id || in_array($pref_name, self::_PROFILE_BLACKLIST)) $profile_id = null;
+
+ list ($def_val, $type_hint) = self::_DEFAULTS[$pref_name];
+
+ $cached_value = $this->_get_cache($pref_name, $owner_uid, $profile_id);
+
+ if ($this->_is_cached($pref_name, $owner_uid, $profile_id)) {
+ $cached_value = $this->_get_cache($pref_name, $owner_uid, $profile_id);
+ return Config::cast_to($cached_value, $type_hint);
+ } else if (get_schema_version() >= 141) {
+ $sth = $this->pdo->prepare("SELECT value FROM ttrss_user_prefs2
+ WHERE pref_name = :name AND owner_uid = :uid AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+
+ $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name ]);
+
+ if ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
+ $this->_set_cache($pref_name, $row["value"], $owner_uid, $profile_id);
+
+ return Config::cast_to($row["value"], $type_hint);
+ } else {
+ $this->_set_cache($pref_name, $def_val, $owner_uid, $profile_id);
+
+ return $def_val;
+ }
+ } else {
+ return Config::cast_to($def_val, $type_hint);
+
+ }
+ } else {
+ user_error("Attempt to get invalid preference key: $pref_name (UID: $owner_uid, profile: $profile_id)", E_USER_WARNING);
+ }
+
+ return null;
+ }
+
+ private function _is_cached(string $pref_name, int $owner_uid, int $profile_id = null) {
+ $cache_key = sprintf("%d/%d/%s", $owner_uid, $profile_id, $pref_name);
+ return isset($this->cache[$cache_key]);
+ }
+
+ private function _get_cache(string $pref_name, int $owner_uid, int $profile_id = null) {
+ $cache_key = sprintf("%d/%d/%s", $owner_uid, $profile_id, $pref_name);
+
+ if (isset($this->cache[$cache_key]))
+ return $this->cache[$cache_key];
+
+ return null;
+ }
+
+ private function _set_cache(string $pref_name, $value, int $owner_uid, int $profile_id = null) {
+ $cache_key = sprintf("%d/%d/%s", $owner_uid, $profile_id, $pref_name);
+
+ $this->cache[$cache_key] = $value;
+ }
+
+ static function set(string $pref_name, $value, int $owner_uid, int $profile_id = null, bool $strip_tags = true) {
+ return self::get_instance()->_set($pref_name, $value, $owner_uid, $profile_id);
+ }
+
+ private function _delete(string $pref_name, int $owner_uid, int $profile_id = null) {
+ $sth = $this->pdo->prepare("DELETE FROM ttrss_user_prefs2
+ WHERE pref_name = :name AND owner_uid = :uid AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+
+ return $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name ]);
+ }
+
+ private function _set(string $pref_name, $value, int $owner_uid, int $profile_id = null, bool $strip_tags = true) {
+ if (!$profile_id) $profile_id = null;
+
+ if ($profile_id && in_array($pref_name, self::_PROFILE_BLACKLIST))
+ return false;
+
+ if (isset(self::_DEFAULTS[$pref_name])) {
+ list ($def_val, $type_hint) = self::_DEFAULTS[$pref_name];
+
+ if ($strip_tags)
+ $value = trim(strip_tags($value));
+
+ $value = Config::cast_to($value, $type_hint);
+
+ // is this a good idea or not? probably not (user-set value remains user-set even if its at default)
+ //if ($value == $def_val)
+ // return $this->_delete($pref_name, $owner_uid, $profile_id);
+
+ if ($value == $this->_get($pref_name, $owner_uid, $profile_id))
+ return false;
+
+ $this->_set_cache($pref_name, $value, $owner_uid, $profile_id);
+
+ $sth = $this->pdo->prepare("SELECT COUNT(pref_name) AS count FROM ttrss_user_prefs2
+ WHERE pref_name = :name AND owner_uid = :uid AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+ $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name ]);
+
+ if ($row = $sth->fetch()) {
+ if ($row["count"] == 0) {
+ $sth = $this->pdo->prepare("INSERT INTO ttrss_user_prefs2
+ (pref_name, value, owner_uid, profile)
+ VALUES
+ (:name, :value, :uid, :profile)");
+
+ return $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name, "value" => $value ]);
+
+ } else {
+ $sth = $this->pdo->prepare("UPDATE ttrss_user_prefs2
+ SET value = :value
+ WHERE pref_name = :name AND owner_uid = :uid AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+
+ return $sth->execute(["uid" => $owner_uid, "profile" => $profile_id, "name" => $pref_name, "value" => $value ]);
+ }
+ }
+ } else {
+ user_error("Attempt to set invalid preference key: $pref_name (UID: $owner_uid, profile: $profile_id)", E_USER_WARNING);
+ }
+
+ return false;
+ }
+
+ function migrate(int $owner_uid, int $profile_id = null) {
+ if (get_schema_version() < 141)
+ return;
+
+ if (!$profile_id) $profile_id = null;
+
+ if (!$this->_get(Prefs::_PREFS_MIGRATED, $owner_uid, $profile_id)) {
+
+ $in_nested_tr = false;
+
+ try {
+ $this->pdo->beginTransaction();
+ } catch (PDOException $e) {
+ $in_nested_tr = true;
+ }
+
+ $sth = $this->pdo->prepare("SELECT pref_name, value FROM ttrss_user_prefs
+ WHERE owner_uid = :uid AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+ $sth->execute(["uid" => $owner_uid, "profile" => $profile_id]);
+
+ while ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
+ if (isset(self::_DEFAULTS[$row["pref_name"]])) {
+ list ($def_val, $type_hint) = self::_DEFAULTS[$row["pref_name"]];
+
+ $user_val = Config::cast_to($row["value"], $type_hint);
+
+ if ($user_val != $def_val) {
+ $this->_set($row["pref_name"], $user_val, $owner_uid, $profile_id);
+ }
+ }
+ }
+
+ $this->_set(Prefs::_PREFS_MIGRATED, "1", $owner_uid, $profile_id);
+
+ if (!$in_nested_tr)
+ $this->pdo->commit();
+
+ Logger::log(E_USER_NOTICE, sprintf("Migrated preferences of user %d (profile %d)", $owner_uid, $profile_id));
+ }
+ }
+
+ static function reset(int $owner_uid, int $profile_id = null) {
+ if (!$profile_id) $profile_id = null;
+
+ $sth = Db::pdo()->prepare("DELETE FROM ttrss_user_prefs2
+ WHERE owner_uid = :uid AND pref_name != :mig_key AND
+ (profile = :profile OR (:profile IS NULL AND profile IS NULL))");
+
+ $sth->execute(["uid" => $owner_uid, "mig_key" => self::_PREFS_MIGRATED, "profile" => $profile_id]);
+ }
+}
diff --git a/classes/rpc.php b/classes/rpc.php
index 8945823c6..aaaf4f8d5 100755
--- a/classes/rpc.php
+++ b/classes/rpc.php
@@ -7,6 +7,36 @@ class RPC extends Handler_Protected {
return array_search($method, $csrf_ignored) !== false;
}*/
+ private function _translations_as_array() {
+
+ global $text_domains;
+
+ $rv = [];
+
+ foreach (array_keys($text_domains) as $domain) {
+
+ /** @var gettext_reader $l10n */
+ $l10n = _get_reader($domain);
+
+ for ($i = 0; $i < $l10n->total; $i++) {
+ if (isset($l10n->table_originals[$i * 2 + 2]) && $orig = $l10n->get_original_string($i)) {
+ if(strpos($orig, "\000") !== false) { // Plural forms
+ $key = explode(chr(0), $orig);
+
+ $rv[$key[0]] = _ngettext($key[0], $key[1], 1); // Singular
+ $rv[$key[1]] = _ngettext($key[0], $key[1], 2); // Plural
+ } else {
+ $translation = _dgettext($domain,$orig);
+ $rv[$orig] = $translation;
+ }
+ }
+ }
+ }
+
+ return $rv;
+ }
+
+
function togglepref() {
$key = clean($_REQUEST["key"]);
set_pref($key, !get_pref($key));
@@ -20,7 +50,7 @@ class RPC extends Handler_Protected {
$key = clean($_REQUEST['key']);
$value = $_REQUEST['value'];
- set_pref($key, $value, false, $key != 'USER_STYLESHEET');
+ set_pref($key, $value, $_SESSION["uid"], $key != 'USER_STYLESHEET');
print json_encode(array("param" =>$key, "value" => $value));
}
@@ -66,7 +96,7 @@ class RPC extends Handler_Protected {
function getRuntimeInfo() {
$reply = [
- 'runtime-info' => $this->make_runtime_info()
+ 'runtime-info' => $this->_make_runtime_info()
];
print json_encode($reply);
@@ -91,7 +121,6 @@ class RPC extends Handler_Protected {
else
$label_ids = array_map("intval", clean($_REQUEST["label_ids"] ?? []));
- // @phpstan-ignore-next-line
$counters = is_array($feed_ids) ? Counters::get_conditional($feed_ids, $label_ids) : Counters::get_all();
$reply = [
@@ -141,15 +170,16 @@ class RPC extends Handler_Protected {
$error = Errors::E_SUCCESS;
- if (get_schema_version(true) != SCHEMA_VERSION) {
+ if (get_schema_version() != SCHEMA_VERSION) {
$error = Errors::E_SCHEMA_MISMATCH;
}
if ($error == Errors::E_SUCCESS) {
$reply = [];
- $reply['init-params'] = $this->make_init_params();
- $reply['runtime-info'] = $this->make_runtime_info();
+ $reply['init-params'] = $this->_make_init_params();
+ $reply['runtime-info'] = $this->_make_runtime_info();
+ $reply['translations'] = $this->_translations_as_array();
print json_encode($reply);
} else {
@@ -201,36 +231,42 @@ class RPC extends Handler_Protected {
static function updaterandomfeed_real() {
+ $default_interval = (int) Prefs::get_default(Prefs::DEFAULT_UPDATE_INTERVAL);
+
// Test if the feed need a update (update interval exceded).
if (Config::get(Config::DB_TYPE) == "pgsql") {
$update_limit_qpart = "AND ((
- ttrss_feeds.update_interval = 0
- AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)
+ update_interval = 0
+ AND p.value != '-1'
+ AND last_updated < NOW() - CAST((COALESCE(p.value, '$default_interval') || ' minutes') AS INTERVAL)
) OR (
- ttrss_feeds.update_interval > 0
- AND ttrss_feeds.last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL)
+ update_interval > 0
+ AND last_updated < NOW() - CAST((update_interval || ' minutes') AS INTERVAL)
) OR (
- ttrss_feeds.update_interval >= 0
- AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
+ update_interval >= 0
+ AND p.value != '-1'
+ AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
))";
} else {
$update_limit_qpart = "AND ((
- ttrss_feeds.update_interval = 0
- AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE)
+ update_interval = 0
+ AND p.value != '-1'
+ AND last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(COALESCE(p.value, '$default_interval'), SIGNED INTEGER) MINUTE)
) OR (
- ttrss_feeds.update_interval > 0
- AND ttrss_feeds.last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE)
+ update_interval > 0
+ AND last_updated < DATE_SUB(NOW(), INTERVAL update_interval MINUTE)
) OR (
- ttrss_feeds.update_interval >= 0
- AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
+ update_interval >= 0
+ AND p.value != '-1'
+ AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
))";
}
// Test if feed is currently being updated by another process.
if (Config::get(Config::DB_TYPE) == "pgsql") {
- $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < NOW() - INTERVAL '5 minutes')";
+ $updstart_thresh_qpart = "AND (last_update_started IS NULL OR last_update_started < NOW() - INTERVAL '5 minutes')";
} else {
- $updstart_thresh_qpart = "AND (ttrss_feeds.last_update_started IS NULL OR ttrss_feeds.last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))";
+ $updstart_thresh_qpart = "AND (last_update_started IS NULL OR last_update_started < DATE_SUB(NOW(), INTERVAL 5 MINUTE))";
}
$random_qpart = Db::sql_random_function();
@@ -238,24 +274,24 @@ class RPC extends Handler_Protected {
$pdo = Db::pdo();
// we could be invoked from public.php with no active session
- if ($_SESSION["uid"]) {
- $owner_check_qpart = "AND ttrss_feeds.owner_uid = ".$pdo->quote($_SESSION["uid"]);
+ if (!empty($_SESSION["uid"])) {
+ $owner_check_qpart = "AND f.owner_uid = ".$pdo->quote($_SESSION["uid"]);
} else {
$owner_check_qpart = "";
}
- // We search for feed needing update.
- $res = $pdo->query("SELECT ttrss_feeds.feed_url,ttrss_feeds.id
+ $query = "SELECT f.feed_url,f.id
FROM
- ttrss_feeds, ttrss_users, ttrss_user_prefs
+ ttrss_feeds f, ttrss_users u LEFT JOIN ttrss_user_prefs2 p ON
+ (p.owner_uid = u.id AND profile IS NULL AND pref_name = 'DEFAULT_UPDATE_INTERVAL')
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'
+ f.owner_uid = u.id
$owner_check_qpart
$update_limit_qpart
$updstart_thresh_qpart
- ORDER BY $random_qpart LIMIT 30");
+ ORDER BY $random_qpart LIMIT 30";
+
+ $res = $pdo->query($query);
$num_updated = 0;
@@ -338,7 +374,7 @@ class RPC extends Handler_Protected {
$context = clean($_REQUEST['context']);
if ($msg) {
- Logger::get()->log_error(E_USER_WARNING,
+ Logger::log_error(E_USER_WARNING,
$msg, 'client-js:' . $file, $line, $context);
echo json_encode(array("message" => "HOST_ERROR_LOGGED"));
@@ -372,13 +408,14 @@ class RPC extends Handler_Protected {
print json_encode($rv);
}
- private function make_init_params() {
+ private function _make_init_params() {
$params = array();
- foreach (array("ON_CATCHUP_SHOW_NEXT_FEED", "HIDE_READ_FEEDS",
- "ENABLE_FEED_CATS", "FEEDS_SORT_BY_UNREAD", "CONFIRM_FEED_CATCHUP",
- "CDM_AUTO_CATCHUP", "FRESH_ARTICLE_MAX_AGE",
- "HIDE_READ_SHOWS_SPECIAL", "COMBINED_DISPLAY_MODE") as $param) {
+ foreach ([Prefs::ON_CATCHUP_SHOW_NEXT_FEED, Prefs::HIDE_READ_FEEDS,
+ Prefs::ENABLE_FEED_CATS, Prefs::FEEDS_SORT_BY_UNREAD,
+ Prefs::CONFIRM_FEED_CATCHUP, Prefs::CDM_AUTO_CATCHUP,
+ Prefs::FRESH_ARTICLE_MAX_AGE, Prefs::HIDE_READ_SHOWS_SPECIAL,
+ Prefs::COMBINED_DISPLAY_MODE, Prefs::DEBUG_HEADLINE_IDS] as $param) {
$params[strtolower($param)] = (int) get_pref($param);
}
@@ -387,14 +424,14 @@ class RPC extends Handler_Protected {
$params["check_for_updates"] = Config::get(Config::CHECK_FOR_UPDATES);
$params["icons_url"] = Config::get(Config::ICONS_URL);
$params["cookie_lifetime"] = Config::get(Config::SESSION_COOKIE_LIFETIME);
- $params["default_view_mode"] = get_pref("_DEFAULT_VIEW_MODE");
- $params["default_view_limit"] = (int) get_pref("_DEFAULT_VIEW_LIMIT");
- $params["default_view_order_by"] = get_pref("_DEFAULT_VIEW_ORDER_BY");
+ $params["default_view_mode"] = get_pref(Prefs::_DEFAULT_VIEW_MODE);
+ $params["default_view_limit"] = (int) get_pref(Prefs::_DEFAULT_VIEW_LIMIT);
+ $params["default_view_order_by"] = get_pref(Prefs::_DEFAULT_VIEW_ORDER_BY);
$params["bw_limit"] = (int) $_SESSION["bw_limit"];
$params["is_default_pw"] = Pref_Prefs::isdefaultpassword();
$params["label_base_index"] = LABEL_BASE_INDEX;
- $theme = get_pref( "USER_CSS_THEME", false, false);
+ $theme = get_pref(Prefs::USER_CSS_THEME);
$params["theme"] = theme_exists($theme) ? $theme : "";
$params["plugins"] = implode(", ", PluginHost::getInstance()->get_plugin_names());
@@ -434,7 +471,7 @@ class RPC extends Handler_Protected {
}
}
- static function make_runtime_info() {
+ static function _make_runtime_info() {
$data = array();
$pdo = Db::pdo();
@@ -449,7 +486,7 @@ class RPC extends Handler_Protected {
$data["max_feed_id"] = (int) $max_feed_id;
$data["num_feeds"] = (int) $num_feeds;
- $data['cdm_expanded'] = get_pref('CDM_EXPANDED');
+ $data['cdm_expanded'] = get_pref(Prefs::CDM_EXPANDED);
$data["labels"] = Labels::get_all($_SESSION["uid"]);
if (Config::get(Config::LOG_DESTINATION) == 'sql' && $_SESSION['access_level'] >= 10) {
diff --git a/classes/rssutils.php b/classes/rssutils.php
index 6479d9f97..11a94162c 100755
--- a/classes/rssutils.php
+++ b/classes/rssutils.php
@@ -53,48 +53,54 @@ class RSSUtils {
}
static function update_daemon_common($limit = null, $options = []) {
- $schema_version = get_schema_version();
-
if (!$limit) $limit = Config::get(Config::DAEMON_FEED_LIMIT);
- if ($schema_version != SCHEMA_VERSION) {
+ if (get_schema_version() != SCHEMA_VERSION) {
die("Schema version is wrong, please upgrade the database.\n");
}
$pdo = Db::pdo();
if (!Config::get(Config::SINGLE_USER_MODE) && Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT) > 0) {
+ $login_limit = (int) Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT);
+
if (Config::get(Config::DB_TYPE) == "pgsql") {
- $login_thresh_qpart = "AND ttrss_users.last_login >= NOW() - INTERVAL '".Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT)." days'";
+ $login_thresh_qpart = "AND last_login >= NOW() - INTERVAL '$login_limit days'";
} else {
- $login_thresh_qpart = "AND ttrss_users.last_login >= DATE_SUB(NOW(), INTERVAL ".Config::get(Config::DAEMON_UPDATE_LOGIN_LIMIT)." DAY)";
+ $login_thresh_qpart = "AND last_login >= DATE_SUB(NOW(), INTERVAL $login_limit DAY)";
}
} else {
$login_thresh_qpart = "";
}
+ $default_interval = (int) Prefs::get_default(Prefs::DEFAULT_UPDATE_INTERVAL);
+
if (Config::get(Config::DB_TYPE) == "pgsql") {
$update_limit_qpart = "AND ((
- ttrss_feeds.update_interval = 0
- AND ttrss_user_prefs.value != '-1'
- AND last_updated < NOW() - CAST((ttrss_user_prefs.value || ' minutes') AS INTERVAL)
+ update_interval = 0
+ AND p.value != '-1'
+ AND last_updated < NOW() - CAST((COALESCE(p.value, '$default_interval') || ' minutes') AS INTERVAL)
+ ) OR (
+ update_interval > 0
+ AND last_updated < NOW() - CAST((update_interval || ' minutes') AS INTERVAL)
) OR (
- ttrss_feeds.update_interval > 0
- AND last_updated < NOW() - CAST((ttrss_feeds.update_interval || ' minutes') AS INTERVAL)
- ) OR ((last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
- AND ttrss_feeds.update_interval >= 0
- AND ttrss_user_prefs.value != '-1'))";
+ update_interval >= 0
+ AND p.value != '-1'
+ AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
+ ))";
} else {
$update_limit_qpart = "AND ((
- ttrss_feeds.update_interval = 0
- AND ttrss_user_prefs.value != '-1'
- AND last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(ttrss_user_prefs.value, SIGNED INTEGER) MINUTE)
+ update_interval = 0
+ AND p.value != '-1'
+ AND last_updated < DATE_SUB(NOW(), INTERVAL CONVERT(COALESCE(p.value, '$default_interval'), SIGNED INTEGER) MINUTE)
) OR (
- ttrss_feeds.update_interval > 0
- AND last_updated < DATE_SUB(NOW(), INTERVAL ttrss_feeds.update_interval MINUTE)
- ) OR ((last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
- AND ttrss_feeds.update_interval >= 0
- AND ttrss_user_prefs.value != '-1'))";
+ update_interval > 0
+ AND last_updated < DATE_SUB(NOW(), INTERVAL update_interval MINUTE)
+ ) OR (
+ update_interval >= 0
+ AND p.value != '-1'
+ AND (last_updated = '1970-01-01 00:00:00' OR last_updated IS NULL)
+ ))";
}
// Test if feed is currently being updated by another process.
@@ -108,20 +114,23 @@ class RSSUtils {
// Update the least recently updated feeds first
$query_order = "ORDER BY last_updated";
- if (Config::get(Config::DB_TYPE) == "pgsql") $query_order .= " NULLS FIRST";
- $query = "SELECT DISTINCT ttrss_feeds.feed_url, ttrss_feeds.last_updated
+ if (Config::get(Config::DB_TYPE) == "pgsql")
+ $query_order .= " NULLS FIRST";
+
+ $query = "SELECT f.feed_url, f.last_updated
FROM
- ttrss_feeds, ttrss_users, ttrss_user_prefs
+ ttrss_feeds f, ttrss_users u LEFT JOIN ttrss_user_prefs2 p ON
+ (p.owner_uid = u.id AND profile IS NULL AND pref_name = 'DEFAULT_UPDATE_INTERVAL')
WHERE
- ttrss_feeds.owner_uid = ttrss_users.id
- AND ttrss_user_prefs.profile IS NULL
- AND ttrss_users.id = ttrss_user_prefs.owner_uid
- AND ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL'
- $login_thresh_qpart $update_limit_qpart
+ f.owner_uid = u.id
+ $login_thresh_qpart
+ $update_limit_qpart
$updstart_thresh_qpart
$query_order $query_limit";
+ //print "$query\n";
+
$res = $pdo->query($query);
$feeds_to_update = array();
@@ -144,34 +153,38 @@ class RSSUtils {
$nf = 0;
$bstarted = microtime(true);
- $batch_owners = array();
+ $batch_owners = [];
- // since we have the data cached, we can deal with other feeds with the same url
- $usth = $pdo->prepare("SELECT
- DISTINCT ttrss_feeds.id,
+ $user_query = "SELECT f.id,
last_updated,
- ttrss_feeds.owner_uid,
- ttrss_feeds.title
- FROM ttrss_feeds, ttrss_users, ttrss_user_prefs WHERE
- ttrss_user_prefs.owner_uid = ttrss_feeds.owner_uid AND
- ttrss_users.id = ttrss_user_prefs.owner_uid AND
- ttrss_user_prefs.pref_name = 'DEFAULT_UPDATE_INTERVAL' AND
- ttrss_user_prefs.profile IS NULL AND
- feed_url = ?
- $update_limit_qpart
+ f.owner_uid,
+ u.login AS owner,
+ f.title
+ FROM ttrss_feeds f, ttrss_users u LEFT JOIN ttrss_user_prefs2 p ON
+ (p.owner_uid = u.id AND profile IS NULL AND pref_name = 'DEFAULT_UPDATE_INTERVAL')
+ WHERE
+ f.owner_uid = u.id
+ AND feed_url = :feed
$login_thresh_qpart
- ORDER BY ttrss_feeds.id $query_limit");
+ $update_limit_qpart
+ ORDER BY f.id $query_limit";
+
+ //print "$user_query\n";
+
+ // since we have feed xml cached, we can deal with other feeds with the same url
+ $usth = $pdo->prepare($user_query);
foreach ($feeds_to_update as $feed) {
Debug::log("Base feed: $feed");
- $usth->execute([$feed]);
+ $usth->execute(["feed" => $feed]);
if ($tline = $usth->fetch()) {
- Debug::log(sprintf("=> %s (ID: %d, UID: %d), last updated: %s", $tline["title"], $tline["id"], $tline["owner_uid"],
+ Debug::log(sprintf("=> %s (ID: %d, U: %s [%d]), last updated: %s", $tline["title"], $tline["id"],
+ $tline["owner"], $tline["owner_uid"],
$tline["last_updated"] ? $tline["last_updated"] : "never"));
- if (array_search($tline["owner_uid"], $batch_owners) === false)
+ if (!in_array($tline["owner_uid"], $batch_owners))
array_push($batch_owners, $tline["owner_uid"]);
$fstarted = microtime(true);
@@ -201,7 +214,7 @@ class RSSUtils {
Debug::log("!! Last error: $error_message");
- Logger::get()->log(E_USER_NOTICE,
+ Logger::log(E_USER_NOTICE,
sprintf("Update process for feed %d (%s, owner UID: %d) failed with exit code: %d (%s).",
$tline["id"], clean($tline["title"]), $tline["owner_uid"], $exit_code, clean($error_message)));
@@ -218,7 +231,7 @@ class RSSUtils {
if (!self::update_rss_feed($tline["id"], true)) {
global $fetch_last_error;
- Logger::get()->log(E_USER_NOTICE,
+ Logger::log(E_USER_NOTICE,
sprintf("Update request for feed %d (%s, owner UID: %d) failed: %s.",
$tline["id"], clean($tline["title"]), $tline["owner_uid"], clean($fetch_last_error)));
}
@@ -226,7 +239,7 @@ class RSSUtils {
Debug::log(sprintf("<= %.4f (sec) (not using a separate process)", microtime(true) - $fstarted));
} catch (PDOException $e) {
- Logger::get()->log_error(E_USER_WARNING, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString());
+ Logger::log_error(E_USER_WARNING, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString());
try {
$pdo->rollback();
@@ -275,7 +288,7 @@ class RSSUtils {
$fetch_url = $row["feed_url"];
$pluginhost = new PluginHost();
- $user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
+ $user_plugins = get_pref(Prefs::_ENABLED_PLUGINS, $owner_uid);
$pluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL);
$pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $owner_uid);
@@ -386,7 +399,7 @@ class RSSUtils {
$feed_language = mb_strtolower($row["feed_language"]);
if (!$feed_language)
- $feed_language = mb_strtolower(get_pref('DEFAULT_SEARCH_LANGUAGE', $owner_uid));
+ $feed_language = mb_strtolower(get_pref(Prefs::DEFAULT_SEARCH_LANGUAGE, $owner_uid));
if (!$feed_language)
$feed_language = 'simple';
@@ -400,7 +413,7 @@ class RSSUtils {
$cache_filename = Config::get(Config::CACHE_DIR) . "/feeds/" . sha1($fetch_url) . ".xml";
$pluginhost = new PluginHost();
- $user_plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
+ $user_plugins = get_pref(Prefs::_ENABLED_PLUGINS, $owner_uid);
$pluginhost->load(Config::get(Config::PLUGINS), PluginHost::KIND_ALL);
$pluginhost->load((string)$user_plugins, PluginHost::KIND_USER, $owner_uid);
@@ -1193,7 +1206,7 @@ class RSSUtils {
$boring_tags = array_map('trim',
explode(",", mb_strtolower(
- get_pref('BLACKLISTED_TAGS', $owner_uid))));
+ get_pref(Prefs::BLACKLISTED_TAGS, $owner_uid))));
$entry_tags = FeedItem_Common::normalize_categories(
array_unique(
@@ -1604,7 +1617,7 @@ class RSSUtils {
$sth->execute();
while ($row = $sth->fetch()) {
- Logger::get()->log(E_USER_NOTICE,
+ Logger::log(E_USER_NOTICE,
sprintf("Auto disabling feed %d (%s, UID: %d) because it failed to update for %d days.",
$row["id"], clean($row["title"]), $row["owner_uid"], Config::get(Config::DAEMON_UNSUCCESSFUL_DAYS_LIMIT)));
diff --git a/classes/sanitizer.php b/classes/sanitizer.php
index 2682471d0..52feb5e28 100644
--- a/classes/sanitizer.php
+++ b/classes/sanitizer.php
@@ -95,7 +95,7 @@ class Sanitizer {
}
if ($entry->hasAttribute('src') &&
- ($owner && get_pref("STRIP_IMAGES", $owner)) || $force_remove_images || ($_SESSION["bw_limit"] ?? false)) {
+ ($owner && get_pref(Prefs::STRIP_IMAGES, $owner)) || $force_remove_images || ($_SESSION["bw_limit"] ?? false)) {
$p = $doc->createElement('p');
diff --git a/classes/timehelper.php b/classes/timehelper.php
index 7dff71669..4317f343f 100644
--- a/classes/timehelper.php
+++ b/classes/timehelper.php
@@ -7,16 +7,16 @@ class TimeHelper {
if ($eta_min && time() + $tz_offset - $timestamp < 3600) {
return T_sprintf("%d min", date("i", time() + $tz_offset - $timestamp));
} else if (date("Y.m.d", $timestamp) == date("Y.m.d", time() + $tz_offset)) {
- $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
+ $format = get_pref(Prefs::SHORT_DATE_FORMAT, $owner_uid);
if (strpos((strtolower($format)), "a") === false)
return date("G:i", $timestamp);
else
return date("g:i a", $timestamp);
} else if (date("Y", $timestamp) == date("Y", time() + $tz_offset)) {
- $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
+ $format = get_pref(Prefs::SHORT_DATE_FORMAT, $owner_uid);
return date($format, $timestamp);
} else {
- $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
+ $format = get_pref(Prefs::LONG_DATE_FORMAT, $owner_uid);
return date($format, $timestamp);
}
}
@@ -37,7 +37,7 @@ class TimeHelper {
# We store date in UTC internally
$dt = new DateTime($timestamp, $utc_tz);
- $user_tz_string = get_pref('USER_TIMEZONE', $owner_uid);
+ $user_tz_string = get_pref(Prefs::USER_TIMEZONE, $owner_uid);
if ($user_tz_string != 'Automatic') {
@@ -59,9 +59,9 @@ class TimeHelper {
$tz_offset, $owner_uid, $eta_min);
} else {
if ($long)
- $format = get_pref('LONG_DATE_FORMAT', $owner_uid);
+ $format = get_pref(Prefs::LONG_DATE_FORMAT, $owner_uid);
else
- $format = get_pref('SHORT_DATE_FORMAT', $owner_uid);
+ $format = get_pref(Prefs::SHORT_DATE_FORMAT, $owner_uid);
return date($format, $user_timestamp);
}
diff --git a/classes/userhelper.php b/classes/userhelper.php
index 82a2fe05f..ca673cf58 100644
--- a/classes/userhelper.php
+++ b/classes/userhelper.php
@@ -18,7 +18,9 @@ class UserHelper {
if ($user_id && !$check_only) {
- session_start();
+ if (session_status() != PHP_SESSION_ACTIVE)
+ session_start();
+
session_regenerate_id(true);
$_SESSION["uid"] = $user_id;
@@ -41,11 +43,12 @@ class UserHelper {
$_SESSION["user_agent"] = sha1($_SERVER['HTTP_USER_AGENT']);
$_SESSION["pwd_hash"] = $row["pwd_hash"];
- Pref_Prefs::_init_user_prefs($_SESSION["uid"]);
-
return true;
}
+ if ($login && $password && !$user_id && !$check_only)
+ Logger::log(E_USER_WARNING, "Failed login attempt for $login (service: $service) from " . UserHelper::get_user_ip());
+
return false;
} else {
@@ -64,8 +67,6 @@ class UserHelper {
$_SESSION["ip_address"] = UserHelper::get_user_ip();
- Pref_Prefs::_init_user_prefs($_SESSION["uid"]);
-
return true;
}
}
@@ -75,7 +76,7 @@ class UserHelper {
if (!$pluginhost) $pluginhost = PluginHost::getInstance();
if ($owner_uid && SCHEMA_VERSION >= 100 && empty($_SESSION["safe_mode"])) {
- $plugins = get_pref("_ENABLED_PLUGINS", $owner_uid);
+ $plugins = get_pref(Prefs::_ENABLED_PLUGINS, $owner_uid);
$pluginhost->load((string)$plugins, PluginHost::KIND_USER, $owner_uid);
@@ -89,17 +90,20 @@ class UserHelper {
$pdo = Db::pdo();
if (Config::get(Config::SINGLE_USER_MODE)) {
- @session_start();
+ if (session_status() != PHP_SESSION_ACTIVE)
+ session_start();
+
self::authenticate("admin", null);
startup_gettext();
self::load_user_plugins($_SESSION["uid"]);
} else {
- if (!\Sessions\validate_session()) $_SESSION["uid"] = false;
+ if (!\Sessions\validate_session())
+ $_SESSION["uid"] = null;
if (empty($_SESSION["uid"])) {
if (Config::get(Config::AUTH_AUTO_LOGIN) && self::authenticate(null, null)) {
- $_SESSION["ref_schema_version"] = get_schema_version(true);
+ $_SESSION["ref_schema_version"] = get_schema_version();
} else {
self::authenticate(null, null, true);
}
@@ -127,7 +131,7 @@ class UserHelper {
}
static function print_user_stylesheet() {
- $value = get_pref('USER_STYLESHEET');
+ $value = get_pref(Prefs::USER_STYLESHEET);
if ($value) {
print "<style type='text/css' id='user_css_style'>";
diff --git a/include/controls_compat.php b/include/controls_compat.php
index d62265471..a4e9ad73f 100644
--- a/include/controls_compat.php
+++ b/include/controls_compat.php
@@ -128,7 +128,7 @@ function print_feed_multi_select($id, $default_ids = [],
}
}
- if (get_pref('ENABLE_FEED_CATS')) {
+ if (get_pref(Prefs::ENABLE_FEED_CATS)) {
if (!$root_id) $root_id = null;
diff --git a/include/errorhandler.php b/include/errorhandler.php
index 68e2285c1..1908bd39c 100644
--- a/include/errorhandler.php
+++ b/include/errorhandler.php
@@ -40,13 +40,13 @@ function format_backtrace($trace) {
}
function ttrss_error_handler($errno, $errstr, $file, $line) {
- if (version_compare(PHP_VERSION, '8.0.0', '<')) {
+ /*if (version_compare(PHP_VERSION, '8.0.0', '<')) {
if (error_reporting() == 0 || !$errno) return false;
} else {
if (!(error_reporting() & $errno)) return false;
}
- if (error_reporting() == 0 || !$errno) return false;
+ if (error_reporting() == 0 || !$errno) return false;*/
$file = substr(str_replace(dirname(__DIR__), "", $file), 1);
@@ -54,12 +54,10 @@ function ttrss_error_handler($errno, $errstr, $file, $line) {
$errstr = truncate_middle($errstr, 16384, " (...) ");
if (class_exists("Logger"))
- return Logger::get()->log_error($errno, $errstr, $file, $line, $context);
+ return Logger::log_error((int)$errno, $errstr, $file, (int)$line, $context);
}
function ttrss_fatal_handler() {
- global $last_query;
-
$error = error_get_last();
if ($error !== NULL) {
@@ -74,10 +72,8 @@ function ttrss_fatal_handler() {
$file = substr(str_replace(dirname(__DIR__), "", $file), 1);
- if ($last_query) $errstr .= " [Last query: $last_query]";
-
if (class_exists("Logger"))
- return Logger::get()->log_error($errno, $errstr, $file, $line, $context);
+ return Logger::log_error((int)$errno, $errstr, $file, (int)$line, $context);
}
return false;
diff --git a/include/functions.php b/include/functions.php
index d916301fb..e0580a076 100644
--- a/include/functions.php
+++ b/include/functions.php
@@ -1,5 +1,5 @@
<?php
- define('SCHEMA_VERSION', 140);
+ define('SCHEMA_VERSION', 141);
define('LABEL_BASE_INDEX', -1024);
define('PLUGIN_FEED_BASE_INDEX', -128);
@@ -42,12 +42,12 @@
define('SUBSTRING_FOR_DATE', 'SUBSTRING');
}
- function get_pref($pref_name, $user_id = false, $die_on_error = false) {
- return Db_Prefs::get()->read($pref_name, $user_id, $die_on_error);
+ function get_pref(string $pref_name, int $owner_uid = null) {
+ return Prefs::get($pref_name, $owner_uid ? $owner_uid : $_SESSION["uid"], $_SESSION["profile"] ?? null);
}
- function set_pref($pref_name, $value, $user_id = false, $strip_tags = true) {
- return Db_Prefs::get()->write($pref_name, $value, $user_id, $strip_tags);
+ function set_pref(string $pref_name, $value, int $owner_uid = null, bool $strip_tags = true) {
+ return Prefs::set($pref_name, $value, $owner_uid ? $owner_uid : $_SESSION["uid"], $_SESSION["profile"] ?? null, $strip_tags);
}
function get_translations() {
@@ -140,7 +140,7 @@
}
if (!empty($_SESSION["uid"]) && get_schema_version() >= 120) {
- $pref_locale = get_pref("USER_LANGUAGE", $_SESSION["uid"]);
+ $pref_locale = get_pref(Prefs::USER_LANGUAGE, $_SESSION["uid"]);
if (!empty($pref_locale) && $pref_locale != 'auto') {
$selected_locale = $pref_locale;
@@ -166,8 +166,6 @@
define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . get_version() . ' (http://tt-rss.org/)');
ini_set('user_agent', SELF_USER_AGENT);
- $schema_version = false;
-
/* compat shims */
function _debug($msg) {
@@ -305,22 +303,8 @@
return $s ? 1 : 0;
}
- // Session caching removed due to causing wrong redirects to upgrade
- // script when get_schema_version() is called on an obsolete session
- // created on a previous schema version.
- function get_schema_version($nocache = false) {
- global $schema_version;
-
- $pdo = Db::pdo();
-
- if (!$schema_version && !$nocache) {
- $row = $pdo->query("SELECT schema_version FROM ttrss_version")->fetch();
- $version = $row["schema_version"];
- $schema_version = $version;
- return $version;
- } else {
- return $schema_version;
- }
+ function get_schema_version() {
+ return Config::get_schema_version();
}
function file_is_locked($filename) {
@@ -459,54 +443,6 @@
return in_array($interface, class_implements($class));
}
- function T_js_decl($s1, $s2) {
- if ($s1 && $s2) {
- $s1 = preg_replace("/\n/", "", $s1);
- $s2 = preg_replace("/\n/", "", $s2);
-
- $s1 = preg_replace("/\"/", "\\\"", $s1);
- $s2 = preg_replace("/\"/", "\\\"", $s2);
-
- return "T_messages[\"$s1\"] = \"$s2\";\n";
- }
- }
-
- function init_js_translations() {
-
- print 'var T_messages = new Object();
-
- function __(msg) {
- if (T_messages[msg]) {
- return T_messages[msg];
- } else {
- return msg;
- }
- }
-
- function ngettext(msg1, msg2, n) {
- return __((parseInt(n) > 1) ? msg2 : msg1);
- }';
-
- global $text_domains;
-
- foreach (array_keys($text_domains) as $domain) {
- $l10n = _get_reader($domain);
-
- for ($i = 0; $i < $l10n->total; $i++) {
- $orig = $l10n->get_original_string($i);
- if(strpos($orig, "\000") !== false) { // Plural forms
- $key = explode(chr(0), $orig);
- print T_js_decl($key[0], _ngettext($key[0], $key[1], 1)); // Singular
- print T_js_decl($key[1], _ngettext($key[0], $key[1], 2)); // Plural
- } else {
- $translation = _dgettext($domain,$orig);
- print T_js_decl($orig, $translation);
- }
- }
-
- }
- }
-
function get_theme_path($theme) {
$check = "themes/$theme";
if (file_exists($check)) return $check;
@@ -578,7 +514,7 @@
$git_commit = $commit;
$git_timestamp = $timestamp;
- $ttrss_version['version'] = strftime("%y.%m", $timestamp) . "-$commit";
+ $ttrss_version['version'] = strftime("%y.%m", (int)$timestamp) . "-$commit";
$ttrss_version['commit'] = $commit;
$ttrss_version['timestamp'] = $timestamp;
}
diff --git a/include/sessions.php b/include/sessions.php
index d4f21d8cd..891a6b3fa 100644
--- a/include/sessions.php
+++ b/include/sessions.php
@@ -19,25 +19,10 @@
ini_set("session.gc_maxlifetime", $session_expire);
ini_set("session.cookie_lifetime", "0");
- function session_get_schema_version() {
- global $schema_version;
-
- if (!$schema_version) {
- $row = \Db::pdo()->query("SELECT schema_version FROM ttrss_version")->fetch();
-
- $version = $row["schema_version"];
-
- $schema_version = $version;
- return $version;
- } else {
- return $schema_version;
- }
- }
-
function validate_session() {
if (\Config::get(\Config::SINGLE_USER_MODE)) return true;
- if (isset($_SESSION["ref_schema_version"]) && $_SESSION["ref_schema_version"] != session_get_schema_version()) {
+ if (isset($_SESSION["ref_schema_version"]) && $_SESSION["ref_schema_version"] != \Config::get_schema_version()) {
$_SESSION["login_error_msg"] =
__("Session failed to validate (schema version changed)");
return false;
@@ -152,6 +137,7 @@
if (!defined('NO_SESSION_AUTOSTART')) {
if (isset($_COOKIE[session_name()])) {
- @session_start();
+ if (session_status() != PHP_SESSION_ACTIVE)
+ session_start();
}
}
diff --git a/index.php b/index.php
index a21db6fc5..0218db5e1 100644
--- a/index.php
+++ b/index.php
@@ -29,7 +29,7 @@
<meta name="viewport" content="initial-scale=1,width=device-width" />
<?php if ($_SESSION["uid"] && empty($_SESSION["safe_mode"])) {
- $theme = get_pref("USER_CSS_THEME", false, false);
+ $theme = get_pref(Prefs::USER_CSS_THEME);
if ($theme && theme_exists("$theme")) {
echo stylesheet_tag(get_theme_path($theme), 'theme_css');
}
@@ -97,8 +97,6 @@
}
}
}
-
- init_js_translations();
?>
</script>
diff --git a/js/App.js b/js/App.js
index 67d932369..bb8da578d 100644
--- a/js/App.js
+++ b/js/App.js
@@ -17,6 +17,15 @@ const App = {
hotkey_actions: {},
is_prefs: false,
LABEL_BASE_INDEX: -1024,
+ _translations: {},
+ l10n: {
+ ngettext: function(msg1, msg2, n) {
+ return self.__((parseInt(n) > 1) ? msg2 : msg1);
+ },
+ __: function(msg) {
+ return App._translations[msg] ? App._translations[msg] : msg;
+ }
+ },
FormFields: {
attributes_to_string: function(attributes) {
return Object.keys(attributes).map((k) =>
@@ -525,6 +534,13 @@ const App = {
PluginHost.run(PluginHost.HOOK_PARAMS_LOADED, this._initParams);
}
+ const translations = reply['translations'];
+
+ if (translations) {
+ console.log('reading translations...');
+ App._translations = translations;
+ }
+
this.initSecondStage();
},
Error: {
diff --git a/js/Headlines.js b/js/Headlines.js
index 6dbe24918..fd9bc6661 100755
--- a/js/Headlines.js
+++ b/js/Headlines.js
@@ -440,10 +440,12 @@ const Headlines = {
if (headlines.vfeed_group_enabled && hl.feed_title && this.vgroup_last_feed != hl.feed_id) {
const vgrhdr = `<div data-feed-id='${hl.feed_id}' class='feed-title'>
- <div style='float : right'>${Feeds.renderIcon(hl.feed_id, hl.has_icon)}</div>
- <a class="title" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}
- <a class="catchup" title="${__('mark feed as read')}" onclick="Feeds.catchupFeedInGroup(${hl.feed_id})" href="#"><i class="icon-done material-icons">done_all</i></a>
- </div>`
+ <div class="pull-right">${Feeds.renderIcon(hl.feed_id, hl.has_icon)}</div>
+ <a class="title" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a>
+ <a class="catchup" title="${__('mark feed as read')}" onclick="Feeds.catchupFeedInGroup(${hl.feed_id})" href="#">
+ <i class="icon-done material-icons">done_all</i>
+ </a>
+ </div>`
const tmp = document.createElement("div");
tmp.innerHTML = vgrhdr;
@@ -476,6 +478,7 @@ const Headlines = {
</div>
<span onclick="return Headlines.click(event, ${hl.id});" data-article-id="${hl.id}" class="titleWrap hlMenuAttach">
+ ${App.getInitParam("debug_headline_ids") ? `<span class="text-muted small">A: ${hl.id} F: ${hl.feed_id}</span>` : ""}
<a class="title" title="${App.escapeHtml(hl.title)}" target="_blank" rel="noopener noreferrer" href="${App.escapeHtml(hl.link)}">
${hl.title}</a>
<span class="author">${hl.author}</span>
@@ -542,6 +545,7 @@ const Headlines = {
<i class="pub-pic pub-${hl.id} material-icons" onclick="Headlines.togglePub(${hl.id})">rss_feed</i>
</div>
<div onclick="return Headlines.click(event, ${hl.id})" class="title">
+ ${App.getInitParam("debug_headline_ids") ? `<span class="text-muted small">A: ${hl.id} F: ${hl.feed_id}</span>` : ""}
<span data-article-id="${hl.id}" class="hl-content hlMenuAttach">
<a class="title" href="${App.escapeHtml(hl.link)}">${hl.title} <span class="preview">${hl.content_preview}</span></a>
<span class="author">${hl.author}</span>
diff --git a/js/common.js b/js/common.js
index 670ee1b30..1544e6d0b 100755
--- a/js/common.js
+++ b/js/common.js
@@ -1,8 +1,22 @@
'use strict';
-/* global dijit, __, App, dojo, __csrf_token */
+/* global dijit, App, dojo, __csrf_token */
/* eslint-disable no-new */
+/* exported __ */
+function __(msg) {
+ if (typeof App != "undefined") {
+ return App.l10n.__(msg);
+ } else {
+ return msg;
+ }
+}
+
+/* exported ngettext */
+function ngettext(msg1, msg2, n) {
+ return __((parseInt(n) > 1) ? msg2 : msg1);
+}
+
/* exported $ */
function $(id) {
console.warn("FIXME: please use App.byId() or document.getElementById() instead of $():", id);
diff --git a/plugins/auth_remote/init.php b/plugins/auth_remote/init.php
index f2dcfb318..f24364ffd 100644
--- a/plugins/auth_remote/init.php
+++ b/plugins/auth_remote/init.php
@@ -21,7 +21,7 @@ class Auth_Remote extends Auth_Base {
$cert_serial = get_ssl_certificate_id();
if ($cert_serial) {
- $sth = $this->pdo->prepare("SELECT login FROM ttrss_user_prefs, ttrss_users
+ $sth = $this->pdo->prepare("SELECT login FROM ttrss_user_prefs2, ttrss_users
WHERE pref_name = 'SSL_CERT_SERIAL' AND value = ? AND
owner_uid = ttrss_users.id");
$sth->execute([$cert_serial]);
diff --git a/plugins/close_button/init.php b/plugins/close_button/init.php
index 4f33d1af0..2a5025cf6 100644
--- a/plugins/close_button/init.php
+++ b/plugins/close_button/init.php
@@ -19,7 +19,7 @@ class Close_Button extends Plugin {
}
function hook_article_button($line) {
- if (!get_pref("COMBINED_DISPLAY_MODE")) {
+ if (!get_pref(Prefs::COMBINED_DISPLAY_MODE)) {
return "<i class='material-icons icon-close-article'
style='cursor : pointer' onclick='Article.close()'
title='".__('Close article')."'>close</i>";
diff --git a/prefs.php b/prefs.php
index 57ddbba7c..a6cbfd886 100644
--- a/prefs.php
+++ b/prefs.php
@@ -20,7 +20,7 @@
<meta name="viewport" content="initial-scale=1,width=device-width" />
<?php if ($_SESSION["uid"] && empty($_SESSION["safe_mode"])) {
- $theme = get_pref("USER_CSS_THEME", false, false);
+ $theme = get_pref(Prefs::USER_CSS_THEME);
if ($theme && theme_exists("$theme")) {
echo stylesheet_tag(get_theme_path($theme), 'theme_css');
}
@@ -79,8 +79,6 @@
}
}
}
-
- init_js_translations();
?>
</script>
diff --git a/schema/ttrss_schema_mysql.sql b/schema/ttrss_schema_mysql.sql
index 95d01e721..90b07e116 100644
--- a/schema/ttrss_schema_mysql.sql
+++ b/schema/ttrss_schema_mysql.sql
@@ -18,6 +18,7 @@ drop table if exists ttrss_filters;
drop table if exists ttrss_filter_types;
drop table if exists ttrss_filter_actions;
drop table if exists ttrss_user_prefs;
+drop table if exists ttrss_user_prefs2;
drop table if exists ttrss_prefs;
drop table if exists ttrss_prefs_types;
drop table if exists ttrss_prefs_sections;
@@ -297,7 +298,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 (140);
+insert into ttrss_version values (141);
create table ttrss_enclosures (id integer primary key auto_increment,
content_url text not null,
@@ -318,18 +319,9 @@ create table ttrss_settings_profiles(id integer primary key auto_increment,
create table ttrss_prefs_types (id integer not null primary key,
type_name varchar(100) not null) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
-insert into ttrss_prefs_types (id, type_name) values (1, 'bool');
-insert into ttrss_prefs_types (id, type_name) values (2, 'string');
-insert into ttrss_prefs_types (id, type_name) values (3, 'integer');
-
create table ttrss_prefs_sections (id integer not null primary key,
order_id integer not null) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
-insert into ttrss_prefs_sections (id, order_id) values (1, 0);
-insert into ttrss_prefs_sections (id, order_id) values (2, 1);
-insert into ttrss_prefs_sections (id, order_id) values (3, 3);
-insert into ttrss_prefs_sections (id, order_id) values (4, 2);
-
create table ttrss_prefs (pref_name varchar(250) not null primary key,
type_id integer not null,
section_id integer not null default 1,
@@ -338,69 +330,6 @@ create table ttrss_prefs (pref_name varchar(250) not null primary key,
foreign key (type_id) references ttrss_prefs_types(id),
foreign key (section_id) references ttrss_prefs_sections(id)) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
-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);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ALLOW_DUPLICATE_POSTS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ENABLE_FEED_CATS', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SHOW_CONTENT_PREVIEW', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SHORT_DATE_FORMAT', 2, 'M d, G:i', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('LONG_DATE_FORMAT', 2, 'D, M d Y - G:i', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('COMBINED_DISPLAY_MODE', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('HIDE_READ_FEEDS', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ON_CATCHUP_SHOW_NEXT_FEED', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('FEEDS_SORT_BY_UNREAD', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('REVERSE_HEADLINES', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DIGEST_ENABLE', 1, 'false', 4);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('CONFIRM_FEED_CATCHUP', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('CDM_AUTO_CATCHUP', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_VIEW_MODE', 2, 'adaptive', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_VIEW_LIMIT', 3, '30', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_PREFS_ACTIVE_TAB', 2, '', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('STRIP_UNSAFE_TAGS', 1, 'true', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('BLACKLISTED_TAGS', 2, 'main, generic, misc, uncategorized, blog, blogroll, general, news', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('FRESH_ARTICLE_MAX_AGE', 3, '24', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DIGEST_CATCHUP', 1, 'false', 4);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('CDM_EXPANDED', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('PURGE_UNREAD_ARTICLES', 1, 'true', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('HIDE_READ_SHOWS_SPECIAL', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('VFEED_GROUP_BY_FEED', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('STRIP_IMAGES', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_VIEW_ORDER_BY', 2, 'default', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ENABLE_API_ACCESS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_SPECIAL', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_LABELS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_UNCAT', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_FEEDLIST', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_ENABLE_CATS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_SHOW_IMAGES', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_HIDE_READ', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_SORT_FEEDS_UNREAD', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_THEME_ID', 2, '0', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_TIMEZONE', 2, 'Automatic', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_STYLESHEET', 2, '', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SORT_HEADLINES_BY_FEED_DATE', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_BROWSE_CATS', 1, 'true', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SSL_CERT_SERIAL', 2, '', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DIGEST_PREFERRED_TIME', 2, '00:00', 4);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_PREFS_SHOW_EMPTY_CATS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_INCLUDE_CHILDREN', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('AUTO_ASSIGN_LABELS', 1, 'false', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_ENABLED_PLUGINS', 2, '', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_REVERSE_HEADLINES', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_CSS_THEME', 2, '', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_LANGUAGE', 2, '', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DEFAULT_SEARCH_LANGUAGE', 2, '', 2);
-
-update ttrss_prefs set access_level = 1 where pref_name in ('ON_CATCHUP_SHOW_NEXT_FEED',
- 'SORT_HEADLINES_BY_FEED_DATE',
- 'VFEED_GROUP_BY_FEED',
- 'FRESH_ARTICLE_MAX_AGE',
- 'CDM_EXPANDED',
- 'SHOW_CONTENT_PREVIEW',
- 'AUTO_ASSIGN_LABELS',
- 'HIDE_READ_SHOWS_SPECIAL');
-
create table ttrss_user_prefs (
owner_uid integer not null,
pref_name varchar(250),
@@ -411,6 +340,14 @@ create table ttrss_user_prefs (
foreign key (owner_uid) references ttrss_users(id) ON DELETE CASCADE,
foreign key (pref_name) references ttrss_prefs(pref_name) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
+create table ttrss_user_prefs2 (
+ owner_uid integer not null,
+ pref_name varchar(250),
+ profile integer null,
+ value longtext not null,
+ foreign key (profile) references ttrss_settings_profiles(id) ON DELETE CASCADE,
+ foreign key (owner_uid) references ttrss_users(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
+
create table ttrss_sessions (id varchar(250) not null primary key,
data text,
expire integer not null,
diff --git a/schema/ttrss_schema_pgsql.sql b/schema/ttrss_schema_pgsql.sql
index f73933ba1..d7200264a 100644
--- a/schema/ttrss_schema_pgsql.sql
+++ b/schema/ttrss_schema_pgsql.sql
@@ -15,6 +15,7 @@ drop table if exists ttrss_filters;
drop table if exists ttrss_filter_types;
drop table if exists ttrss_filter_actions;
drop table if exists ttrss_user_prefs;
+drop table if exists ttrss_user_prefs2;
drop table if exists ttrss_prefs;
drop table if exists ttrss_prefs_types;
drop table if exists ttrss_prefs_sections;
@@ -279,7 +280,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 (140);
+insert into ttrss_version values (141);
create table ttrss_enclosures (id serial not null primary key,
content_url text not null,
@@ -299,88 +300,16 @@ create table ttrss_settings_profiles(id serial not null primary key,
create table ttrss_prefs_types (id integer not null primary key,
type_name varchar(100) not null);
-insert into ttrss_prefs_types (id, type_name) values (1, 'bool');
-insert into ttrss_prefs_types (id, type_name) values (2, 'string');
-insert into ttrss_prefs_types (id, type_name) values (3, 'integer');
-
create table ttrss_prefs_sections (id integer not null primary key,
order_id integer not null,
section_name varchar(100) not null);
-insert into ttrss_prefs_sections (id, section_name, order_id) values (1, 'General', 0);
-insert into ttrss_prefs_sections (id, section_name, order_id) values (2, 'Interface', 1);
-insert into ttrss_prefs_sections (id, section_name, order_id) values (3, 'Advanced', 3);
-insert into ttrss_prefs_sections (id, section_name, order_id) values (4, 'Digest', 2);
-
create table ttrss_prefs (pref_name varchar(250) not null primary key,
type_id integer not null references ttrss_prefs_types(id),
section_id integer not null default 1 references ttrss_prefs_sections(id),
access_level integer not null default 0,
def_value text not null);
-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);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ALLOW_DUPLICATE_POSTS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ENABLE_FEED_CATS', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SHOW_CONTENT_PREVIEW', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SHORT_DATE_FORMAT', 2, 'M d, G:i', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('LONG_DATE_FORMAT', 2, 'D, M d Y - G:i', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('COMBINED_DISPLAY_MODE', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('HIDE_READ_FEEDS', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ON_CATCHUP_SHOW_NEXT_FEED', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('FEEDS_SORT_BY_UNREAD', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('REVERSE_HEADLINES', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DIGEST_ENABLE', 1, 'false', 4);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('CONFIRM_FEED_CATCHUP', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('CDM_AUTO_CATCHUP', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_VIEW_MODE', 2, 'adaptive', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_VIEW_LIMIT', 3, '30', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_PREFS_ACTIVE_TAB', 2, '', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('STRIP_UNSAFE_TAGS', 1, 'true', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('BLACKLISTED_TAGS', 2, 'main, generic, misc, uncategorized, blog, blogroll, general, news', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('FRESH_ARTICLE_MAX_AGE', 3, '24', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DIGEST_CATCHUP', 1, 'false', 4);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('CDM_EXPANDED', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('PURGE_UNREAD_ARTICLES', 1, 'true', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('HIDE_READ_SHOWS_SPECIAL', 1, 'true', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('VFEED_GROUP_BY_FEED', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('STRIP_IMAGES', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_VIEW_ORDER_BY', 2, 'default', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('ENABLE_API_ACCESS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_SPECIAL', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_LABELS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_UNCAT', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_COLLAPSED_FEEDLIST', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_ENABLE_CATS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_SHOW_IMAGES', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_HIDE_READ', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_SORT_FEEDS_UNREAD', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_THEME_ID', 2, '0', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_TIMEZONE', 2, 'Automatic', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_STYLESHEET', 2, '', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SORT_HEADLINES_BY_FEED_DATE', 1, 'false', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_BROWSE_CATS', 1, 'true', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('SSL_CERT_SERIAL', 2, '', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DIGEST_PREFERRED_TIME', 2, '00:00', 4);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_PREFS_SHOW_EMPTY_CATS', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_DEFAULT_INCLUDE_CHILDREN', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('AUTO_ASSIGN_LABELS', 1, 'false', 3);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_ENABLED_PLUGINS', 2, '', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('_MOBILE_REVERSE_HEADLINES', 1, 'false', 1);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_CSS_THEME', 2, '', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('USER_LANGUAGE', 2, '', 2);
-insert into ttrss_prefs (pref_name,type_id,def_value,section_id) values('DEFAULT_SEARCH_LANGUAGE', 2, '', 2);
-
-update ttrss_prefs set access_level = 1 where pref_name in ('ON_CATCHUP_SHOW_NEXT_FEED',
- 'SORT_HEADLINES_BY_FEED_DATE',
- 'VFEED_GROUP_BY_FEED',
- 'FRESH_ARTICLE_MAX_AGE',
- 'CDM_EXPANDED',
- 'SHOW_CONTENT_PREVIEW',
- 'AUTO_ASSIGN_LABELS',
- 'HIDE_READ_SHOWS_SPECIAL');
-
create table ttrss_user_prefs (
owner_uid integer not null references ttrss_users(id) ON DELETE CASCADE,
pref_name varchar(250) not null references ttrss_prefs(pref_name) ON DELETE CASCADE,
@@ -389,7 +318,16 @@ create table ttrss_user_prefs (
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_user_prefs2 (
+ owner_uid integer not null references ttrss_users(id) ON DELETE CASCADE,
+ pref_name varchar(250) not null,
+ profile integer references ttrss_settings_profiles(id) ON DELETE CASCADE,
+ value text not null);
+
+create index ttrss_user_prefs2_owner_uid_index on ttrss_user_prefs2(owner_uid);
+create index ttrss_user_prefs2_pref_name_idx on ttrss_user_prefs2(pref_name);
+create unique index ttrss_user_prefs2_composite_idx on ttrss_user_prefs2(pref_name, owner_uid, coalesce(profile, -1));
create table ttrss_sessions (id varchar(250) not null primary key,
data text,
diff --git a/schema/versions/mysql/141.sql b/schema/versions/mysql/141.sql
new file mode 100644
index 000000000..03caf23d0
--- /dev/null
+++ b/schema/versions/mysql/141.sql
@@ -0,0 +1,9 @@
+create table ttrss_user_prefs2 (
+ owner_uid integer not null,
+ pref_name varchar(250),
+ profile integer null,
+ value longtext not null,
+ foreign key (profile) references ttrss_settings_profiles(id) ON DELETE CASCADE,
+ foreign key (owner_uid) references ttrss_users(id) ON DELETE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=UTF8;
+
+update ttrss_version set schema_version = 141;
diff --git a/schema/versions/pgsql/141.sql b/schema/versions/pgsql/141.sql
new file mode 100644
index 000000000..679ca9498
--- /dev/null
+++ b/schema/versions/pgsql/141.sql
@@ -0,0 +1,11 @@
+create table ttrss_user_prefs2 (
+ owner_uid integer not null references ttrss_users(id) ON DELETE CASCADE,
+ pref_name varchar(250) not null,
+ profile integer references ttrss_settings_profiles(id) ON DELETE CASCADE,
+ value text not null);
+
+create index ttrss_user_prefs2_owner_uid_index on ttrss_user_prefs2(owner_uid);
+create index ttrss_user_prefs2_pref_name_idx on ttrss_user_prefs2(pref_name);
+create unique index ttrss_user_prefs2_composite_idx on ttrss_user_prefs2(pref_name, owner_uid, coalesce(profile, -1));
+
+update ttrss_version set schema_version = 141;
diff --git a/update.php b/update.php
index 8d8566db7..aab84bfdb 100755
--- a/update.php
+++ b/update.php
@@ -145,9 +145,7 @@
}
if (!isset($options['update-schema'])) {
- $schema_version = get_schema_version();
-
- if ($schema_version != SCHEMA_VERSION) {
+ if (get_schema_version() != SCHEMA_VERSION) {
die("Schema version is wrong, please upgrade the database (--update-schema).\n");
}
}
@@ -242,7 +240,7 @@
Debug::log(sprintf("Exception while updating feed %d: %s (%s:%d)",
$options["update-feed"], $e->getMessage(), $e->getFile(), $e->getLine()));
- Logger::get()->log_error(E_USER_WARNING, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString());
+ Logger::log_error(E_USER_WARNING, $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString());
exit(110);
}
diff --git a/update_daemon2.php b/update_daemon2.php
index b75f06ae5..1e8b1d072 100755
--- a/update_daemon2.php
+++ b/update_daemon2.php
@@ -188,9 +188,7 @@
"Maybe another daemon is already running.\n");
}
- $schema_version = get_schema_version();
-
- if ($schema_version != SCHEMA_VERSION) {
+ if (get_schema_version() != SCHEMA_VERSION) {
die("Schema version is wrong, please upgrade the database.\n");
}