summaryrefslogtreecommitdiff
path: root/classes
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 /classes
parent8cf9c451dc1d5f3ed23ead40bee41592f7c07254 (diff)
parentf1607902e6953aa5c486157835105c0c8f08779f (diff)
Merge branch 'master' of git.fakecake.org:fox/tt-rss
Diffstat (limited to 'classes')
-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
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;