diff options
author | Andrew Dolgov <[email protected]> | 2022-01-13 13:59:36 +0300 |
---|---|---|
committer | Andrew Dolgov <[email protected]> | 2022-01-13 13:59:36 +0300 |
commit | 304845f3807cc1021de1f29a35e2e3c370ff9882 (patch) | |
tree | 12206fcb33d26907254ee6adfe2bb20a553bbbca /classes | |
parent | 8cf9c451dc1d5f3ed23ead40bee41592f7c07254 (diff) | |
parent | f1607902e6953aa5c486157835105c0c8f08779f (diff) |
Merge branch 'master' of git.fakecake.org:fox/tt-rss
Diffstat (limited to 'classes')
-rw-r--r-- | classes/config.php | 2 | ||||
-rw-r--r-- | classes/errors.php | 23 | ||||
-rw-r--r-- | classes/feedparser.php | 5 | ||||
-rwxr-xr-x | classes/feeds.php | 28 | ||||
-rw-r--r-- | classes/plugin.php | 17 | ||||
-rwxr-xr-x | classes/pluginhost.php | 28 | ||||
-rwxr-xr-x | classes/pref/filters.php | 28 | ||||
-rwxr-xr-x | classes/rssutils.php | 42 | ||||
-rw-r--r-- | classes/urlhelper.php | 2 |
9 files changed, 119 insertions, 56 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; |