Merge branch 'master' of git.fakecake.org:fox/tt-rss
This commit is contained in:
commit
304845f380
|
@ -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:
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
21
index.php
21
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>
|
||||
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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, '&').replace(/</g, '<') :
|
||||
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;
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
Binary file not shown.
|
@ -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 <mara.pavelka@gmail.com>\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"
|
||||
|
|
Binary file not shown.
|
@ -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 <tony_lao@outlook.com>\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)."
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
insert into ttrss_filter_actions (id,name,description) values (10, 'ignore-tag',
|
||||
'Ignore tags');
|
|
@ -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,
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
insert into ttrss_filter_actions (id,name,description) values (10, 'ignore-tag',
|
||||
'Ignore tags');
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue