summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2022-01-13 13:59:36 +0300
committerAndrew Dolgov <[email protected]>2022-01-13 13:59:36 +0300
commit304845f3807cc1021de1f29a35e2e3c370ff9882 (patch)
tree12206fcb33d26907254ee6adfe2bb20a553bbbca
parent8cf9c451dc1d5f3ed23ead40bee41592f7c07254 (diff)
parentf1607902e6953aa5c486157835105c0c8f08779f (diff)
Merge branch 'master' of git.fakecake.org:fox/tt-rss
-rw-r--r--classes/config.php2
-rw-r--r--classes/errors.php23
-rw-r--r--classes/feedparser.php5
-rwxr-xr-xclasses/feeds.php28
-rw-r--r--classes/plugin.php17
-rwxr-xr-xclasses/pluginhost.php28
-rwxr-xr-xclasses/pref/filters.php28
-rwxr-xr-xclasses/rssutils.php42
-rw-r--r--classes/urlhelper.php2
-rw-r--r--index.php21
-rw-r--r--js/CommonFilters.js3
-rw-r--r--js/Feeds.js4
-rwxr-xr-xjs/Headlines.js94
-rw-r--r--js/PluginHost.js12
-rwxr-xr-xjs/form/Select.js64
-rw-r--r--locale/cs_CZ/LC_MESSAGES/messages.mobin51303 -> 51781 bytes
-rw-r--r--locale/cs_CZ/LC_MESSAGES/messages.po62
-rw-r--r--locale/zh_TW/LC_MESSAGES/messages.mobin47495 -> 47495 bytes
-rw-r--r--locale/zh_TW/LC_MESSAGES/messages.po8
-rwxr-xr-xplugins/af_redditimgur/init.php21
-rw-r--r--sql/mysql/migrations/146.sql2
-rw-r--r--sql/mysql/schema.sql3
-rw-r--r--sql/pgsql/migrations/146.sql2
-rw-r--r--sql/pgsql/schema.sql3
-rw-r--r--themes/compact.css3
-rw-r--r--themes/compact_night.css3
-rw-r--r--themes/light-high-contrast.css3
-rw-r--r--themes/light.css3
-rw-r--r--themes/light/cdm.less3
-rw-r--r--themes/night.css3
-rw-r--r--themes/night_blue.css3
31 files changed, 340 insertions, 155 deletions
diff --git a/classes/config.php b/classes/config.php
index 1c65fc76f..2c78b908d 100644
--- a/classes/config.php
+++ b/classes/config.php
@@ -6,7 +6,7 @@ class Config {
const T_STRING = 2;
const T_INT = 3;
- const SCHEMA_VERSION = 145;
+ const SCHEMA_VERSION = 146;
/** override default values, defined below in _DEFAULTS[], prefixing with _ENVVAR_PREFIX:
*
diff --git a/classes/errors.php b/classes/errors.php
index 31be558cf..aa626d017 100644
--- a/classes/errors.php
+++ b/classes/errors.php
@@ -14,4 +14,27 @@ class Errors {
static function to_json(string $code, array $params = []): string {
return json_encode(["error" => ["code" => $code, "params" => $params]]);
}
+
+ static function libxml_last_error() : string {
+ $error = libxml_get_last_error();
+ $error_formatted = "";
+
+ if ($error) {
+ foreach (libxml_get_errors() as $error) {
+ if ($error->level == LIBXML_ERR_FATAL) {
+ // currently only the first error is reported
+ $error_formatted = self::format_libxml_error($error);
+ break;
+ }
+ }
+ }
+
+ return UConverter::transcode($error_formatted, 'UTF-8', 'UTF-8');
+ }
+
+ static function format_libxml_error(LibXMLError $error) : string {
+ return sprintf("LibXML error %s at line %d (column %d): %s",
+ $error->code, $error->line, $error->column,
+ $error->message);
+ }
}
diff --git a/classes/feedparser.php b/classes/feedparser.php
index 6ce69cc89..3ed0647d2 100644
--- a/classes/feedparser.php
+++ b/classes/feedparser.php
@@ -193,10 +193,9 @@ class FeedParser {
}
}
+ /** @deprecated use Errors::format_libxml_error() instead */
function format_error(LibXMLError $error) : string {
- return sprintf("LibXML error %s at line %d (column %d): %s",
- $error->code, $error->line, $error->column,
- $error->message);
+ return Errors::format_libxml_error($error);
}
// libxml may have invalid unicode data in error messages
diff --git a/classes/feeds.php b/classes/feeds.php
index a9afb70f2..2c37d659a 100755
--- a/classes/feeds.php
+++ b/classes/feeds.php
@@ -133,7 +133,7 @@ class Feeds extends Handler_Protected {
$reply['vfeed_group_enabled'] = $vfeed_group_enabled;
$plugin_menu_items = "";
- PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM,
+ PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2,
function ($result) use (&$plugin_menu_items) {
$plugin_menu_items .= $result;
},
@@ -254,6 +254,10 @@ class Feeds extends Handler_Protected {
$line["buttons_left"] .= $button_doc->saveXML($button_doc->firstChild);
}
+ } else if ($result) {
+ user_error(get_class($plugin) .
+ " plugin: content provided in HOOK_ARTICLE_LEFT_BUTTON is not valid XML: " .
+ Errors::libxml_last_error() . " $result", E_USER_WARNING);
}
},
$line);
@@ -273,6 +277,10 @@ class Feeds extends Handler_Protected {
$line["buttons"] .= $button_doc->saveXML($button_doc->firstChild);
}
+ } else if ($result) {
+ user_error(get_class($plugin) .
+ " plugin: content provided in HOOK_ARTICLE_BUTTON is not valid XML: " .
+ Errors::libxml_last_error() . " $result", E_USER_WARNING);
}
},
$line);
@@ -718,7 +726,7 @@ class Feeds extends Handler_Protected {
<fieldset>
<label>
<?= \Controls\select_hash("xdebug", $xdebug,
- [Debug::$LOG_VERBOSE => "LOG_VERBOSE", Debug::$LOG_EXTENDED => "LOG_EXTENDED"]);
+ [Debug::LOG_VERBOSE => "LOG_VERBOSE", Debug::LOG_EXTENDED => "LOG_EXTENDED"]);
?></label>
</fieldset>
@@ -955,7 +963,8 @@ class Feeds extends Handler_Protected {
$sth->execute([$owner_uid, $feed]);
$row = $sth->fetch();
- return $row["count"];
+ // Handle 'SUM()' returning null if there are no results
+ return $row["count"] ?? 0;
} else if ($n_feed == -1) {
$match_part = "marked = true";
@@ -1359,7 +1368,8 @@ class Feeds extends Handler_Protected {
$sth->execute([$user_id]);
$row = $sth->fetch();
- return $row["count"];
+ // Handle 'SUM()' returning null if there are no articles/results (e.g. admin user with no feeds)
+ return $row["count"] ?? 0;
}
static function _get_cat_title(int $cat_id): string {
@@ -2132,7 +2142,7 @@ class Feeds extends Handler_Protected {
$owner_uid = $row["owner_uid"];
if (Config::get(Config::FORCE_ARTICLE_PURGE) != 0) {
- Debug::log("purge_feed: FORCE_ARTICLE_PURGE is set, overriding interval to " . Config::get(Config::FORCE_ARTICLE_PURGE), Debug::$LOG_VERBOSE);
+ Debug::log("purge_feed: FORCE_ARTICLE_PURGE is set, overriding interval to " . Config::get(Config::FORCE_ARTICLE_PURGE), Debug::LOG_VERBOSE);
$purge_unread = true;
$purge_interval = Config::get(Config::FORCE_ARTICLE_PURGE);
} else {
@@ -2141,10 +2151,10 @@ class Feeds extends Handler_Protected {
$purge_interval = (int) $purge_interval;
- Debug::log("purge_feed: interval $purge_interval days for feed $feed_id, owner: $owner_uid, purge unread: $purge_unread", Debug::$LOG_VERBOSE);
+ Debug::log("purge_feed: interval $purge_interval days for feed $feed_id, owner: $owner_uid, purge unread: $purge_unread", Debug::LOG_VERBOSE);
if ($purge_interval <= 0) {
- Debug::log("purge_feed: purging disabled for this feed, nothing to do.", Debug::$LOG_VERBOSE);
+ Debug::log("purge_feed: purging disabled for this feed, nothing to do.", Debug::LOG_VERBOSE);
return null;
}
@@ -2177,10 +2187,10 @@ class Feeds extends Handler_Protected {
$rows_deleted = $sth->rowCount();
- Debug::log("purge_feed: deleted $rows_deleted articles.", Debug::$LOG_VERBOSE);
+ Debug::log("purge_feed: deleted $rows_deleted articles.", Debug::LOG_VERBOSE);
} else {
- Debug::log("purge_feed: owner of $feed_id not found", Debug::$LOG_VERBOSE);
+ Debug::log("purge_feed: owner of $feed_id not found", Debug::LOG_VERBOSE);
}
return $rows_deleted;
diff --git a/classes/plugin.php b/classes/plugin.php
index be8376925..39af6a9a1 100644
--- a/classes/plugin.php
+++ b/classes/plugin.php
@@ -98,7 +98,7 @@ abstract class Plugin {
/* GLOBAL hooks are invoked in global context, only available to system plugins (loaded via .env for all users) */
- /** Adds buttons for article (on the right) - e.g. mail, share, add note.
+ /** Adds buttons for article (on the right) - e.g. mail, share, add note. Generated markup must be valid XML.
* @param array<string,mixed> $line
* @return string
* @see PluginHost::HOOK_ARTICLE_BUTTON
@@ -307,7 +307,7 @@ abstract class Plugin {
return [];
}
- /** Adds per-article buttons on the left side
+ /** Adds per-article buttons on the left side. Generated markup must be valid XML.
* @param array<string,mixed> $row
* @return string
* @see PluginHost::HOOK_ARTICLE_LEFT_BUTTON
@@ -647,6 +647,7 @@ abstract class Plugin {
}
/** Allows adding custom elements to headlines Select... dropdown
+ * @deprecated removed, see Plugin::hook_headline_toolbar_select_menu_item2()
* @param int $feed_id
* @param int $is_cat
* @return string
@@ -658,6 +659,18 @@ abstract class Plugin {
return "";
}
+ /** Allows adding custom elements to headlines Select... select dropdown (<option> format)
+ * @param int $feed_id
+ * @param int $is_cat
+ * @return string
+ * @see PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2
+ */
+ function hook_headline_toolbar_select_menu_item2($feed_id, $is_cat) {
+ user_error("Dummy method invoked.", E_USER_ERROR);
+
+ return "";
+ }
+
/** Invoked when user tries to subscribe to feed, may override information (i.e. feed URL) used afterwards
* @param string $url
* @param string $auth_login
diff --git a/classes/pluginhost.php b/classes/pluginhost.php
index a3a389def..952d4df77 100755
--- a/classes/pluginhost.php
+++ b/classes/pluginhost.php
@@ -189,9 +189,14 @@ class PluginHost {
/** @see Plugin::hook_headlines_custom_sort_override() */
const HOOK_HEADLINES_CUSTOM_SORT_OVERRIDE = "hook_headlines_custom_sort_override";
- /** @see Plugin::hook_headline_toolbar_select_menu_item() */
+ /** @see Plugin::hook_headline_toolbar_select_menu_item()
+ * @deprecated removed, see PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2
+ */
const HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM = "hook_headline_toolbar_select_menu_item";
+ /** @see Plugin::hook_headline_toolbar_select_menu_item() */
+ const HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2 = "hook_headline_toolbar_select_menu_item2";
+
/** @see Plugin::hook_pre_subscribe() */
const HOOK_PRE_SUBSCRIBE = "hook_pre_subscribe";
@@ -270,9 +275,10 @@ class PluginHost {
* @param mixed $args
*/
function run_hooks(string $hook, ...$args): void {
- $method = strtolower($hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ $method = strtolower((string)$hook);
+
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
try {
@@ -291,9 +297,9 @@ class PluginHost {
* @param mixed $check
*/
function run_hooks_until(string $hook, $check, ...$args): bool {
- $method = strtolower($hook);
+ $method = strtolower((string)$hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
try {
$result = $plugin->$method(...$args);
@@ -315,9 +321,9 @@ class PluginHost {
* @param mixed $args
*/
function run_hooks_callback(string $hook, Closure $callback, ...$args): void {
- $method = strtolower($hook);
+ $method = strtolower((string)$hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
try {
@@ -336,9 +342,9 @@ class PluginHost {
* @param mixed $args
*/
function chain_hooks_callback(string $hook, Closure $callback, &...$args): void {
- $method = strtolower($hook);
+ $method = strtolower((string)$hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
try {
@@ -358,7 +364,7 @@ class PluginHost {
function add_hook(string $type, Plugin $sender, int $priority = 50): void {
$priority = (int) $priority;
- if (!method_exists($sender, strtolower($type))) {
+ if (!method_exists($sender, strtolower((string)$type))) {
user_error(
sprintf("Plugin %s tried to register a hook without implementation: %s",
get_class($sender), $type),
@@ -422,7 +428,7 @@ class PluginHost {
asort($plugins);
- $this->load(join(",", $plugins), $kind, $owner_uid, $skip_init);
+ $this->load(join(",", $plugins), (int)$kind, $owner_uid, $skip_init);
}
/**
diff --git a/classes/pref/filters.php b/classes/pref/filters.php
index 6e6e3d9ee..04178f1a6 100755
--- a/classes/pref/filters.php
+++ b/classes/pref/filters.php
@@ -1,6 +1,15 @@
<?php
class Pref_Filters extends Handler_Protected {
+ const ACTION_TAG = 4;
+ const ACTION_SCORE = 6;
+ const ACTION_LABEL = 7;
+ const ACTION_PLUGIN = 9;
+ const ACTION_REMOVE_TAG = 10;
+
+ const PARAM_ACTIONS = [self::ACTION_TAG, self::ACTION_SCORE,
+ self::ACTION_LABEL, self::ACTION_PLUGIN, self::ACTION_REMOVE_TAG];
+
function csrf_ignore(string $method): bool {
$csrf_ignored = array("index", "getfiltertree", "savefilterorder");
@@ -274,7 +283,7 @@ class Pref_Filters extends Handler_Protected {
}
}
- if ($line['action_id'] == 7) {
+ if ($line['action_id'] == self::ACTION_LABEL) {
$label_sth = $this->pdo->prepare("SELECT fg_color, bg_color
FROM ttrss_labels2 WHERE caption = ? AND
owner_uid = ?");
@@ -474,11 +483,7 @@ class Pref_Filters extends Handler_Protected {
$title = __($row["description"]);
- if ($action["action_id"] == 4 || $action["action_id"] == 6 ||
- $action["action_id"] == 7)
- $title .= ": " . $action["action_param"];
-
- if ($action["action_id"] == 9) {
+ if ($action["action_id"] == self::ACTION_PLUGIN) {
list ($pfclass, $pfaction) = explode(":", $action["action_param"]);
$filter_actions = PluginHost::getInstance()->get_filter_actions();
@@ -491,6 +496,8 @@ class Pref_Filters extends Handler_Protected {
}
}
}
+ } else if (in_array($action["action_id"], self::PARAM_ACTIONS)) {
+ $title .= ": " . $action["action_param"];
}
}
@@ -596,14 +603,19 @@ class Pref_Filters extends Handler_Protected {
$action_param = $action["action_param"];
$action_param_label = $action["action_param_label"];
- if ($action_id == 7) {
+ if ($action_id == self::ACTION_LABEL) {
$action_param = $action_param_label;
}
- if ($action_id == 6) {
+ if ($action_id == self::ACTION_SCORE) {
$action_param = (int)str_replace("+", "", $action_param);
}
+ if (in_array($action_id, [self::ACTION_TAG, self::ACTION_REMOVE_TAG])) {
+ $action_param = implode(", ", FeedItem_Common::normalize_categories(
+ explode(",", $action_param)));
+ }
+
$asth->execute([$filter_id, $action_id, $action_param]);
}
}
diff --git a/classes/rssutils.php b/classes/rssutils.php
index b886a060c..e826a32f8 100755
--- a/classes/rssutils.php
+++ b/classes/rssutils.php
@@ -557,7 +557,7 @@ class RSSUtils {
Debug::log("language: $feed_language", Debug::LOG_VERBOSE);
Debug::log("processing feed data...", Debug::LOG_VERBOSE);
- $site_url = mb_substr(rewrite_relative_url($feed_obj->feed_url, clean($rss->get_link())), 0, 245);
+ $site_url = mb_substr(UrlHelper::rewrite_relative($feed_obj->feed_url, clean($rss->get_link())), 0, 245);
Debug::log("site_url: $site_url", Debug::LOG_VERBOSE);
Debug::log("feed_title: {$rss->get_title()}", Debug::LOG_VERBOSE);
@@ -736,7 +736,7 @@ class RSSUtils {
// TODO: Just use FeedEnclosure (and modify it to cover whatever justified this)?
$e_item = array(
- rewrite_relative_url($site_url, $e->link),
+ UrlHelper::rewrite_relative($site_url, $e->link),
$e->type, $e->length, $e->title, $e->width, $e->height);
// Yet another episode of "mysql utf8_general_ci is gimped"
@@ -1164,32 +1164,30 @@ class RSSUtils {
}
// check for manual tags (we have to do it here since they're loaded from filters)
-
foreach ($article_filters as $f) {
if ($f["type"] == "tag") {
+ $entry_tags = array_merge($entry_tags,
+ FeedItem_Common::normalize_categories(explode(",", $f["param"])));
+ }
+ }
- $manual_tags = array_map('trim', explode(",", mb_strtolower($f["param"])));
-
- foreach ($manual_tags as $tag) {
- array_push($entry_tags, $tag);
- }
+ // like boring tags, but filter-based
+ foreach ($article_filters as $f) {
+ if ($f["type"] == "ignore-tag") {
+ $entry_tags = array_diff($entry_tags,
+ FeedItem_Common::normalize_categories(explode(",", $f["param"])));
}
}
// Skip boring tags
-
- $boring_tags = array_map('trim',
- explode(",", mb_strtolower(
- get_pref(Prefs::BLACKLISTED_TAGS, $feed_obj->owner_uid))));
-
$entry_tags = FeedItem_Common::normalize_categories(
- array_unique(
- array_diff($entry_tags, $boring_tags)));
+ array_diff($entry_tags,
+ FeedItem_Common::normalize_categories(explode(",",
+ get_pref(Prefs::BLACKLISTED_TAGS, $feed_obj->owner_uid)))));
- Debug::log("filtered tags: " . implode(", ", $entry_tags), Debug::LOG_VERBOSE);
+ Debug::log("resulting article tags: " . implode(", ", $entry_tags), Debug::LOG_VERBOSE);
// Save article tags in the database
-
if (count($entry_tags) > 0) {
$tsth = $pdo->prepare("SELECT id FROM ttrss_tags
@@ -1286,7 +1284,7 @@ class RSSUtils {
foreach ($enclosures as $enc) {
if (preg_match("/(image|audio|video)/", $enc[1])) {
- $src = rewrite_relative_url($site_url, $enc[0]);
+ $src = UrlHelper::rewrite_relative($site_url, $enc[0]);
$local_filename = sha1($src);
@@ -1312,7 +1310,7 @@ class RSSUtils {
/* TODO: move to DiskCache? */
static function cache_media_url(DiskCache $cache, string $url, string $site_url): void {
- $url = rewrite_relative_url($site_url, $url);
+ $url = UrlHelper::rewrite_relative($site_url, $url);
$local_filename = sha1($url);
Debug::log("cache_media: checking $url", Debug::LOG_VERBOSE);
@@ -1874,14 +1872,14 @@ class RSSUtils {
$base = $xpath->query('/html/head/base[@href]');
foreach ($base as $b) {
- $url = rewrite_relative_url($url, $b->getAttribute("href"));
+ $url = UrlHelper::rewrite_relative($url, $b->getAttribute("href"));
break;
}
$entries = $xpath->query('/html/head/link[@rel="shortcut icon" or @rel="icon"]');
if (count($entries) > 0) {
foreach ($entries as $entry) {
- $favicon_url = rewrite_relative_url($url, $entry->getAttribute("href"));
+ $favicon_url = UrlHelper::rewrite_relative($url, $entry->getAttribute("href"));
break;
}
}
@@ -1889,7 +1887,7 @@ class RSSUtils {
}
if (!$favicon_url)
- $favicon_url = rewrite_relative_url($url, "/favicon.ico");
+ $favicon_url = UrlHelper::rewrite_relative($url, "/favicon.ico");
return $favicon_url;
}
diff --git a/classes/urlhelper.php b/classes/urlhelper.php
index 9696c16db..9ac7781ef 100644
--- a/classes/urlhelper.php
+++ b/classes/urlhelper.php
@@ -419,6 +419,8 @@ class UrlHelper {
if (curl_errno($ch) != 0) {
self::$fetch_last_error .= "; " . curl_errno($ch) . " " . curl_error($ch);
+ } else {
+ self::$fetch_last_error = "HTTP Code: $http_code ";
}
self::$fetch_last_error_content = $contents;
diff --git a/index.php b/index.php
index 538346549..2dbc078cd 100644
--- a/index.php
+++ b/index.php
@@ -215,20 +215,13 @@
?>
</select>
- <div class="catchup-button" dojoType="fox.form.ComboButton" onclick="Feeds.catchupCurrent()">
- <span><?= __('Mark as read') ?></span>
- <div dojoType="dijit.DropDownMenu">
- <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('1day')">
- <?= __('Older than one day') ?>
- </div>
- <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('1week')">
- <?= __('Older than one week') ?>
- </div>
- <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('2week')">
- <?= __('Older than two weeks') ?>
- </div>
- </div>
- </div>
+ <select class="catchup-button" id="main-catchup-dropdown" dojoType="fox.form.Select"
+ data-prevent-value-change="true">
+ <option value=""><?= __('Mark as read') ?></option>
+ <option value="1day"><?= __('Older than one day') ?></option>
+ <option value="1week"><?= __('Older than one week') ?></option>
+ <option value="2week"><?= __('Older than two weeks') ?></option>
+ </select>
</form>
diff --git a/js/CommonFilters.js b/js/CommonFilters.js
index 8a20480f0..434ee72c7 100644
--- a/js/CommonFilters.js
+++ b/js/CommonFilters.js
@@ -16,7 +16,8 @@ const Filters = {
ACTION_SCORE: 6,
ACTION_LABEL: 7,
ACTION_PLUGIN: 9,
- PARAM_ACTIONS: [4, 6, 7, 9],
+ ACTION_REMOVE_TAG: 10,
+ PARAM_ACTIONS: [4, 6, 7, 9, 10],
filter_info: {},
test: function() {
const test_dialog = new fox.SingleUseDialog({
diff --git a/js/Feeds.js b/js/Feeds.js
index 5ef554af0..714eb77d2 100644
--- a/js/Feeds.js
+++ b/js/Feeds.js
@@ -282,6 +282,10 @@ const Feeds = {
CommonDialogs.safeModeWarning();
}
+ dojo.connect(dijit.byId("main-catchup-dropdown"), 'onItemClick',
+ (item) => Feeds.catchupCurrent(item.option.value)
+ );
+
// bw_limit disables timeout() so we request initial counters separately
if (App.getInitParam("bw_limit")) {
this.requestCounters();
diff --git a/js/Headlines.js b/js/Headlines.js
index 18e47d740..2be3cd697 100755
--- a/js/Headlines.js
+++ b/js/Headlines.js
@@ -626,6 +626,12 @@ const Headlines = {
const search_query = Feeds._search_query ? Feeds._search_query.query : "";
const target = dijit.byId('toolbar-headlines');
+ // TODO: is this needed? destroyDescendants() below might take care of it (?)
+ if (this._headlinesSelectClickHandle)
+ dojo.disconnect(this._headlinesSelectClickHandle);
+
+ target.destroyDescendants();
+
if (tb && typeof tb == 'object') {
target.attr('innerHTML',
`
@@ -646,27 +652,37 @@ const Headlines = {
</span>
<span class='right'>
<span id='selected_prompt'></span>
- <div class='select-articles-dropdown' dojoType='fox.form.DropDownButton' title='"${__('Select articles')}'>
- <span>${__("Select...")}</span>
- <div dojoType='dijit.Menu' style='display: none;'>
- <div dojoType='dijit.MenuItem' onclick='Headlines.select("all")'>${__('All')}</div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.select("unread")'>${__('Unread')}</div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.select("invert")'>${__('Invert')}</div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.select("none")'>${__('None')}</div>
- <div dojoType='dijit.MenuSeparator'></div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.selectionToggleUnread()'>${__('Toggle unread')}</div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.selectionToggleMarked()'>${__('Toggle starred')}</div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.selectionTogglePublished()'>${__('Toggle published')}</div>
- <div dojoType='dijit.MenuSeparator'></div>
- <div dojoType='dijit.MenuItem' onclick='Headlines.catchupSelection()'>${__('Mark as read')}</div>
- <div dojoType='dijit.MenuItem' onclick='Article.selectionSetScore()'>${__('Set score')}</div>
- ${tb.plugin_menu_items}
+
+ <select class='select-articles-dropdown'
+ id='headlines-select-articles-dropdown'
+ data-prevent-value-change="true"
+ data-dropdown-skip-first="true"
+ dojoType="fox.form.Select"
+ title="${__('Show articles')}">
+ <option value='' selected="selected">${__("Select...")}</option>
+ <option value='headlines_select_all'>${__('All')}</option>
+ <option value='headlines_select_unread'>${__('Unread')}</option>
+ <option value='headlines_select_invert'>${__('Invert')}</option>
+ <option value='headlines_select_none'>${__('None')}</option>
+ <option></option>
+ <option value='headlines_selectionToggleUnread'>${__('Toggle unread')}</option>
+ <option value='headlines_selectionToggleMarked'>${__('Toggle starred')}</option>
+ <option value='headlines_selectionTogglePublished'>${__('Toggle published')}</option>
+ <option></option>
+ <option value='headlines_catchupSelection'>${__('Mark as read')}</option>
+ <option value='article_selectionSetScore'>${__('Set score')}</option>
+ ${tb.plugin_menu_items != '' ?
+ `
+ <option></option>
+ ${tb.plugin_menu_items}
+ ` : ''}
${headlines.id === 0 && !headlines.is_cat ?
`
- <div dojoType='dijit.MenuSeparator'></div>
- <div dojoType='dijit.MenuItem' class='text-error' onclick='Headlines.deleteSelection()'>${__('Delete permanently')}</div>
+ <option></option>
+ <option class='text-error' value='headlines_deleteSelection'>${__('Delete permanently')}</option>
` : ''}
- </div>
+ </select>
+
${tb.plugin_buttons}
</span>
`);
@@ -675,6 +691,48 @@ const Headlines = {
}
dojo.parser.parse(target.domNode);
+
+ this._headlinesSelectClickHandle = dojo.connect(dijit.byId("headlines-select-articles-dropdown"), 'onItemClick',
+ (item) => {
+ const action = item.option.value;
+
+ switch (action) {
+ case 'headlines_select_all':
+ Headlines.select('all');
+ break;
+ case 'headlines_select_unread':
+ Headlines.select('unread');
+ break;
+ case 'headlines_select_invert':
+ Headlines.select('invert');
+ break;
+ case 'headlines_select_none':
+ Headlines.select('none');
+ break;
+ case 'headlines_selectionToggleUnread':
+ Headlines.selectionToggleUnread();
+ break;
+ case 'headlines_selectionToggleMarked':
+ Headlines.selectionToggleMarked();
+ break;
+ case 'headlines_selectionTogglePublished':
+ Headlines.selectionTogglePublished();
+ break;
+ case 'headlines_catchupSelection':
+ Headlines.catchupSelection();
+ break;
+ case 'article_selectionSetScore':
+ Article.selectionSetScore();
+ break;
+ case 'headlines_deleteSelection':
+ Headlines.deleteSelection();
+ break;
+ default:
+ if (!PluginHost.run_until(PluginHost.HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2, true, action))
+ console.warn('unknown headlines action', action);
+ }
+ }
+ );
},
onLoaded: function (reply, offset, append) {
console.log("Headlines.onLoaded: offset=", offset, "append=", append);
diff --git a/js/PluginHost.js b/js/PluginHost.js
index deb7c0645..513429e4a 100644
--- a/js/PluginHost.js
+++ b/js/PluginHost.js
@@ -21,6 +21,7 @@ const PluginHost = {
HOOK_HEADLINE_MUTATIONS_SYNCED: 16,
HOOK_HEADLINES_RENDERED: 17,
HOOK_HEADLINES_SCROLL_HANDLER: 18,
+ HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2: 19,
hooks: [],
register: function (name, callback) {
if (typeof(this.hooks[name]) == 'undefined')
@@ -36,6 +37,17 @@ const PluginHost = {
this.hooks[name][i](args);
}
},
+ run_until: function (name, check, ...args) {
+ //console.warn('PluginHost.run_until', name, check, args);
+
+ if (typeof(this.hooks[name]) != 'undefined')
+ for (let i = 0; i < this.hooks[name].length; i++) {
+ if (this.hooks[name][i](args) == check)
+ return true;
+ }
+
+ return false;
+ },
unregister: function (name, callback) {
for (let i = 0; i < this.hooks[name].length; i++)
if (this.hooks[name][i] == callback)
diff --git a/js/form/Select.js b/js/form/Select.js
index 530880e2d..0c73cd52c 100755
--- a/js/form/Select.js
+++ b/js/form/Select.js
@@ -1,8 +1,66 @@
-/* global dijit, define */
-define(["dojo/_base/declare", "dijit/form/Select"], function (declare) {
- return declare("fox.form.Select", dijit.form.Select, {
+/* eslint-disable prefer-rest-params */
+/* global define */
+// FIXME: there probably is a better, more dojo-like notation for custom data- properties
+define(["dojo/_base/declare",
+ "dijit/form/Select",
+ "dojo/_base/lang", // lang.hitch
+ "dijit/MenuItem",
+ "dijit/MenuSeparator",
+ "dojo/aspect",
+ ], function (declare, select, lang, MenuItem, MenuSeparator, aspect) {
+ return declare("fox.form.Select", select, {
focus: function() {
return; // Stop dijit.form.Select from keeping focus after closing the menu
},
+ startup: function() {
+ this.inherited(arguments);
+
+ if (this.attr('data-dropdown-skip-first') == 'true') {
+ aspect.before(this, "_loadChildren", () => {
+ this.options = this.options.splice(1);
+ });
+ }
+ },
+ // hook invoked when dropdown MenuItem is clicked
+ onItemClick: function(/*item, menu*/) {
+ //
+ },
+ _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
+ if (this.attr('data-prevent-value-change') == 'true' && newValue != '')
+ return;
+
+ this.inherited(arguments);
+ },
+ // the only difference from dijit/form/Select is _onItemClicked() handler
+ _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option){
+ // summary:
+ // For the given option, return the menu item that should be
+ // used to display it. This can be overridden as needed
+ if (!option.value && !option.label){
+ // We are a separator (no label set for it)
+ return new MenuSeparator({ownerDocument: this.ownerDocument});
+ } else {
+ // Just a regular menu option
+ const click = lang.hitch(this, "_setValueAttr", option);
+ const item = new MenuItem({
+ option: option,
+ label: (this.labelType === 'text' ? (option.label || '').toString()
+ .replace(/&/g, '&amp;').replace(/</g, '&lt;') :
+ option.label) || this.emptyLabel,
+ onClick: () => {
+ this.onItemClick(item, this.dropDown);
+
+ click();
+ },
+ ownerDocument: this.ownerDocument,
+ dir: this.dir,
+ textDir: this.textDir,
+ disabled: option.disabled || false
+ });
+ item.focusNode.setAttribute("role", "option");
+
+ return item;
+ }
+ },
});
});
diff --git a/locale/cs_CZ/LC_MESSAGES/messages.mo b/locale/cs_CZ/LC_MESSAGES/messages.mo
index 470547241..31ff2c923 100644
--- a/locale/cs_CZ/LC_MESSAGES/messages.mo
+++ b/locale/cs_CZ/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/locale/cs_CZ/LC_MESSAGES/messages.po b/locale/cs_CZ/LC_MESSAGES/messages.po
index f716401ec..a8dca4355 100644
--- a/locale/cs_CZ/LC_MESSAGES/messages.po
+++ b/locale/cs_CZ/LC_MESSAGES/messages.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: TT-RSS CZech\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-06-18 13:34+0300\n"
-"PO-Revision-Date: 2021-03-12 06:52+0000\n"
+"PO-Revision-Date: 2021-11-24 07:41+0000\n"
"Last-Translator: Marek Pavelka <[email protected]>\n"
"Language-Team: Czech <https://weblate.tt-rss.org/projects/tt-rss/messages/cs/"
">\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
-"X-Generator: Weblate 4.5.1\n"
+"X-Generator: Weblate 4.9.1\n"
#: backend.php:60
msgid "Use default"
@@ -280,20 +280,16 @@ msgid "Open next feed"
msgstr "Otevřít další kanál"
#: classes/rpc.php:567
-#, fuzzy
-#| msgid "Open next feed"
msgid "Open next unread feed"
-msgstr "Otevřít další kanál"
+msgstr "Otevřít další nepřečtený kanál"
#: classes/rpc.php:568
msgid "Open previous feed"
msgstr "Otevřít předchozí kanál"
#: classes/rpc.php:569
-#, fuzzy
-#| msgid "Open previous feed"
msgid "Open previous unread feed"
-msgstr "Otevřít předchozí kanál"
+msgstr "Otevřít předchozí nepřečtený kanál"
#: classes/rpc.php:570
msgid "Open next article (in combined mode, scroll down)"
@@ -461,10 +457,8 @@ msgid "Toggle headline grouping"
msgstr "Přepnout seskupování nadpisů"
#: classes/rpc.php:613
-#, fuzzy
-#| msgid "Toggle sidebar"
msgid "Toggle grid view"
-msgstr "Přepnout postranní panel"
+msgstr "Přepnout zobrazení v mřížce"
#: classes/rpc.php:614
msgid "Debug feed update"
@@ -851,14 +845,12 @@ msgid "May increase server load"
msgstr "Může zvýšit zatížení serveru"
#: classes/pref/prefs.php:121
-#, fuzzy
-#| msgid "Preview"
msgid "Grid view"
-msgstr "Náhled"
+msgstr "Zobrazení v mřížce"
#: classes/pref/prefs.php:121
msgid "On wider screens, if always expanded"
-msgstr ""
+msgstr "Na širších obrazovkách, pokud je vždy rozbaleno"
#: classes/pref/prefs.php:222
msgid "The configuration was saved."
@@ -927,14 +919,12 @@ msgid "Disable OTP"
msgstr "Zakázat jednorázové heslo"
#: classes/pref/prefs.php:472
-#, fuzzy
-#| msgid "OTP Key:"
msgid "OTP secret:"
-msgstr "Klíč jednorázového hesla:"
+msgstr "Tajné jednorázové heslo:"
#: classes/pref/prefs.php:499
msgid "Verification code:"
-msgstr ""
+msgstr "Ověřovací kód:"
#: classes/pref/prefs.php:507
msgid "Enable OTP"
@@ -1609,10 +1599,8 @@ msgid "%d min"
msgstr "%d min"
#: plugins/auth_internal/init.php:112
-#, fuzzy
-#| msgid "Please enter label caption:"
msgid "Please enter verification code (OTP):"
-msgstr "Zadejte titulek štítku:"
+msgstr "Zadejte ověřovací kód (jednorázové heslo):"
#: plugins/auth_internal/init.php:114
msgid "Continue"
@@ -1620,7 +1608,7 @@ msgstr "Pokračovat"
#: plugins/auth_internal/init.php:166
msgid "Too many authentication attempts, throttled."
-msgstr ""
+msgstr "Příliš mnoho pokusů o ověření, omezeno."
#: plugins/auth_internal/init.php:247
msgid "Password has been changed."
@@ -1833,8 +1821,6 @@ msgid "The following comics are currently supported:"
msgstr "Nyní jsou podporovány následující komiksy:"
#: plugins/nsfw/init.php:39
-#, fuzzy
-#| msgid "Not work safe (click to toggle)"
msgid "Not safe for work (click to toggle)"
msgstr "Neotvírat v práci (kliknutím přepnout)"
@@ -2331,13 +2317,11 @@ msgstr "Vymazat protokol událostí?"
#: js/PrefHelpers.js:135
msgid "Name for cloned profile:"
-msgstr ""
+msgstr "Název pro klonovaný profil:"
#: js/PrefHelpers.js:145
-#, fuzzy
-#| msgid "Please select an image file."
msgid "Please select a single profile to clone."
-msgstr "Vyberte soubor obrázku."
+msgstr "Vyberte jeden profil pro klonování."
#: js/PrefHelpers.js:153
msgid ""
@@ -2362,7 +2346,7 @@ msgstr "(aktivní)"
#: js/PrefHelpers.js:219
msgid "(empty)"
-msgstr ""
+msgstr "(prázdný)"
#: js/PrefHelpers.js:242
msgid "Activate selected profile?"
@@ -2749,10 +2733,8 @@ msgid "Looking for articles (%d processed, %f found)..."
msgstr "Hledání článků (%d zpracováno, %f nalezeno)..."
#: js/CommonFilters.js:72
-#, fuzzy
-#| msgid "Found %d articles matching this filter:"
msgid "Articles matching this filter:"
-msgstr "Nalezeno %d článků odpovídajících tomuto filtru:"
+msgstr "Články odpovídající tomuto filtru:"
#: js/CommonFilters.js:74
msgid "Found %d articles matching this filter:"
@@ -2763,10 +2745,8 @@ msgid "Error while trying to get filter test results."
msgstr "Chyba při pokusu o získání výsledků testu filtru."
#: js/CommonFilters.js:95
-#, fuzzy
-#| msgid "Looking for plugins..."
msgid "Looking for articles..."
-msgstr "Vyhledávání modulů..."
+msgstr "Vyhledávání článků..."
#: js/CommonFilters.js:174
msgid "Edit rule"
@@ -2829,10 +2809,8 @@ msgid "Unable to fetch full text for this article"
msgstr "Nelze načíst úplný text pro tento článek"
#: plugins/shorten_expanded/init.js:32
-#, fuzzy
-#| msgid "Email article"
msgid "Expand article"
-msgstr "Odeslat článek e-mailem"
+msgstr "Rozbalit článek"
#: plugins/share/share.js:7
msgid "Share article by URL"
@@ -2896,13 +2874,11 @@ msgstr "URL stránky:"
#: js/PrefHelpers.js:229
msgid "Clone"
-msgstr ""
+msgstr "Klonovat"
#: js/PrefHelpers.js:231
-#, fuzzy
-#| msgid "Activate profile"
msgid "Activate"
-msgstr "Aktivovat profil"
+msgstr "Aktivovat"
#: js/PrefHelpers.js:299
msgid "Apply"
diff --git a/locale/zh_TW/LC_MESSAGES/messages.mo b/locale/zh_TW/LC_MESSAGES/messages.mo
index 6ec886518..74a2f11b3 100644
--- a/locale/zh_TW/LC_MESSAGES/messages.mo
+++ b/locale/zh_TW/LC_MESSAGES/messages.mo
Binary files differ
diff --git a/locale/zh_TW/LC_MESSAGES/messages.po b/locale/zh_TW/LC_MESSAGES/messages.po
index 0ba915e8b..83316edc1 100644
--- a/locale/zh_TW/LC_MESSAGES/messages.po
+++ b/locale/zh_TW/LC_MESSAGES/messages.po
@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: Tiny Tiny RSS\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-06-18 13:34+0300\n"
-"PO-Revision-Date: 2021-09-22 14:10+0000\n"
+"PO-Revision-Date: 2021-11-21 17:41+0000\n"
"Last-Translator: TonyRL <[email protected]>\n"
"Language-Team: Chinese (Traditional) <https://weblate.tt-rss.org/projects/"
"tt-rss/messages/zh_Hant/>\n"
@@ -17,7 +17,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Weblate 4.8.1\n"
+"X-Generator: Weblate 4.9.1\n"
#: backend.php:60
msgid "Use default"
@@ -149,7 +149,7 @@ msgstr "未讀"
#: index.php:192
msgid "With Note"
-msgstr "筆記"
+msgstr "附註記"
#: index.php:195
msgid "Sort articles"
@@ -650,7 +650,7 @@ msgstr "進階"
#: classes/pref/prefs.php:81
msgid "Debugging"
-msgstr "除錯中"
+msgstr "除錯"
#: classes/pref/prefs.php:87
msgid "Never apply these tags automatically (comma-separated list)."
diff --git a/plugins/af_redditimgur/init.php b/plugins/af_redditimgur/init.php
index 8ec947b86..f2a04ce24 100755
--- a/plugins/af_redditimgur/init.php
+++ b/plugins/af_redditimgur/init.php
@@ -158,7 +158,24 @@ class Af_RedditImgur extends Plugin {
private function process_post_media(array $data, DOMDocument $doc, DOMXPath $xpath, DOMElement $anchor) : bool {
$found = 0;
- if (isset($data["media_metadata"])) {
+ // process galleries in the right order
+ if (isset($data["gallery_data"]) && isset($data["media_metadata"])) {
+ foreach ($data["gallery_data"]["items"] as $gal_item) {
+ $media_id = $gal_item["media_id"] ?? null;
+
+ if ($media_id) {
+ $media_url = htmlspecialchars_decode($data["media_metadata"][$media_id]["s"]["u"] ?? "");
+
+ if ($media_url) {
+ Debug::log("found gallery item: $media_id, url: $media_url", Debug::LOG_EXTENDED);
+
+ $this->handle_as_image($doc, $anchor, $media_url);
+ $found = 1;
+ }
+ }
+ }
+ // i'm not sure if this is a thing, but if there's no gallery just process any possible attaches in the random order...
+ } else if (isset($data["media_metadata"])) {
foreach ($data["media_metadata"] as $media) {
if (!empty($media["s"]["u"])) {
$media_url = htmlspecialchars_decode($media["s"]["u"]);
@@ -205,7 +222,7 @@ class Af_RedditImgur extends Plugin {
Debug::log("found hosted video url: $media_url / poster $poster_url, looking up fallback url...", Debug::LOG_VERBOSE);
- $fallback_url = $data["media"]["reddit_video"]["fallback_url"];
+ $fallback_url = $data["media"]["reddit_video"]["fallback_url"] ?? null;
if ($fallback_url) {
Debug::log("found video fallback_url: $fallback_url", Debug::LOG_VERBOSE);
diff --git a/sql/mysql/migrations/146.sql b/sql/mysql/migrations/146.sql
new file mode 100644
index 000000000..6d4824727
--- /dev/null
+++ b/sql/mysql/migrations/146.sql
@@ -0,0 +1,2 @@
+insert into ttrss_filter_actions (id,name,description) values (10, 'ignore-tag',
+ 'Ignore tags');
diff --git a/sql/mysql/schema.sql b/sql/mysql/schema.sql
index ff6ff4797..589d1013a 100644
--- a/sql/mysql/schema.sql
+++ b/sql/mysql/schema.sql
@@ -249,6 +249,9 @@ insert into ttrss_filter_actions (id,name,description) values (8, 'stop',
insert into ttrss_filter_actions (id,name,description) values (9, 'plugin',
'Invoke plugin');
+insert into ttrss_filter_actions (id,name,description) values (10, 'ignore-tag',
+ 'Ignore tags');
+
create table ttrss_filters2(id integer primary key auto_increment,
owner_uid integer not null,
match_any_rule boolean not null default false,
diff --git a/sql/pgsql/migrations/146.sql b/sql/pgsql/migrations/146.sql
new file mode 100644
index 000000000..6d4824727
--- /dev/null
+++ b/sql/pgsql/migrations/146.sql
@@ -0,0 +1,2 @@
+insert into ttrss_filter_actions (id,name,description) values (10, 'ignore-tag',
+ 'Ignore tags');
diff --git a/sql/pgsql/schema.sql b/sql/pgsql/schema.sql
index b539419b6..938ccc905 100644
--- a/sql/pgsql/schema.sql
+++ b/sql/pgsql/schema.sql
@@ -245,6 +245,9 @@ insert into ttrss_filter_actions (id,name,description) values (8, 'stop',
insert into ttrss_filter_actions (id,name,description) values (9, 'plugin',
'Invoke plugin');
+insert into ttrss_filter_actions (id,name,description) values (10, 'ignore-tag',
+ 'Ignore tags');
+
create table ttrss_filters2(id serial not null primary key,
owner_uid integer not null references ttrss_users(id) on delete cascade,
match_any_rule boolean not null default false,
diff --git a/themes/compact.css b/themes/compact.css
index ccd6ef76c..d462892db 100644
--- a/themes/compact.css
+++ b/themes/compact.css
@@ -1475,8 +1475,7 @@ body.ttrss_utility hr {
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
border: 0 solid #ddd;
border-bottom-width: 1px;
- background: white ! important;
- opacity: 0.9;
+ background: rgba(255, 255, 255, 0.9) ! important;
backdrop-filter: blur(6px);
}
body.ttrss_prefs {
diff --git a/themes/compact_night.css b/themes/compact_night.css
index 6b072e510..8b1cd17bc 100644
--- a/themes/compact_night.css
+++ b/themes/compact_night.css
@@ -1475,8 +1475,7 @@ body.ttrss_utility hr {
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
border: 0 solid #222;
border-bottom-width: 1px;
- background: #333 ! important;
- opacity: 0.9;
+ background: rgba(51, 51, 51, 0.9) ! important;
backdrop-filter: blur(6px);
}
body.ttrss_prefs {
diff --git a/themes/light-high-contrast.css b/themes/light-high-contrast.css
index 18fc67f6a..77f3def7e 100644
--- a/themes/light-high-contrast.css
+++ b/themes/light-high-contrast.css
@@ -1475,8 +1475,7 @@ body.ttrss_utility hr {
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
border: 0 solid #ddd;
border-bottom-width: 1px;
- background: white ! important;
- opacity: 0.9;
+ background: rgba(255, 255, 255, 0.9) ! important;
backdrop-filter: blur(6px);
}
body.ttrss_prefs {
diff --git a/themes/light.css b/themes/light.css
index 475e4dbbf..8367b07cc 100644
--- a/themes/light.css
+++ b/themes/light.css
@@ -1475,8 +1475,7 @@ body.ttrss_utility hr {
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
border: 0 solid #ddd;
border-bottom-width: 1px;
- background: white ! important;
- opacity: 0.9;
+ background: rgba(255, 255, 255, 0.9) ! important;
backdrop-filter: blur(6px);
}
body.ttrss_prefs {
diff --git a/themes/light/cdm.less b/themes/light/cdm.less
index 4cbfa1d28..6bb3378c1 100644
--- a/themes/light/cdm.less
+++ b/themes/light/cdm.less
@@ -325,8 +325,7 @@
box-shadow : 0 1px 1px -1px rgba(0,0,0,0.1);
border: 0 solid @border-default;
border-bottom-width: 1px;
- background : @default-bg ! important;
- opacity: 0.9;
+ background : fade(@default-bg, 90%) ! important;
backdrop-filter: blur(6px);
}
}
diff --git a/themes/night.css b/themes/night.css
index 24288e149..447ca6f7f 100644
--- a/themes/night.css
+++ b/themes/night.css
@@ -1476,8 +1476,7 @@ body.ttrss_utility hr {
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
border: 0 solid #222;
border-bottom-width: 1px;
- background: #333 ! important;
- opacity: 0.9;
+ background: rgba(51, 51, 51, 0.9) ! important;
backdrop-filter: blur(6px);
}
body.ttrss_prefs {
diff --git a/themes/night_blue.css b/themes/night_blue.css
index 209484935..7a6ce2b69 100644
--- a/themes/night_blue.css
+++ b/themes/night_blue.css
@@ -1476,8 +1476,7 @@ body.ttrss_utility hr {
box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.1);
border: 0 solid #222;
border-bottom-width: 1px;
- background: #333 ! important;
- opacity: 0.9;
+ background: rgba(51, 51, 51, 0.9) ! important;
backdrop-filter: blur(6px);
}
body.ttrss_prefs {