Merge branch 'feature/subscription-fail-info' into 'master'

Show more info when subscribing fails

See merge request tt-rss/tt-rss!153
This commit is contained in:
Andrew Dolgov 2025-06-21 07:52:36 +03:00
commit 664f832aac
2 changed files with 32 additions and 22 deletions

View File

@ -985,19 +985,23 @@ class Feeds extends Handler_Protected {
} }
/** /**
* @return array<string, mixed> (code => Status code, message => error message if available) * @return array{code: int, message?: string}|array{code: int, feeds: array<string>}|array{code: int, feed_id: int}
* code - status code (see below)
* message - optional error message
* feeds - list of discovered feed URLs
* feed_id - ID of the existing or added feed
* *
* 0 - OK, Feed already exists * 0 - OK, Feed already exists
* 1 - OK, Feed added * 1 - OK, Feed added
* 2 - Invalid URL * 2 - Invalid URL
* 3 - URL content is HTML, no feeds available * 3 - URL content is HTML, no feeds available
* 4 - URL content is HTML which contains multiple feeds. * 4 - URL content is HTML which contains multiple feeds.
* Here you should call extractfeedurls in rpc-backend * Here you should call extractfeedurls in rpc-backend
* to get all possible feeds. * to get all possible feeds.
* 5 - Couldn't download the URL content. * 5 - Couldn't download the URL content.
* 6 - Content is an invalid XML. * 6 - currently unused
* 7 - Error while creating feed database entry. * 7 - Error while creating feed database entry.
* 8 - Permission denied (ACCESS_LEVEL_READONLY). * 8 - Permission denied (ACCESS_LEVEL_READONLY).
*/ */
static function _subscribe(string $url, int $cat_id = 0, string $auth_login = '', string $auth_pass = '', int $update_interval = 0): array { static function _subscribe(string $url, int $cat_id = 0, string $auth_login = '', string $auth_pass = '', int $update_interval = 0): array {
@ -1009,7 +1013,10 @@ class Feeds extends Handler_Protected {
$url = UrlHelper::validate($url); $url = UrlHelper::validate($url);
if (!$url) return ["code" => 2]; if (!$url) {
Logger::log(E_USER_NOTICE, "An attempt to subscribe to '{$url}' failed due to URL validation (User: '{$user->login}'; ID: {$user->id}).");
return ["code" => 2];
}
PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_PRE_SUBSCRIBE, PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_PRE_SUBSCRIBE,
/** @phpstan-ignore closure.unusedUse, closure.unusedUse, closure.unusedUse */ /** @phpstan-ignore closure.unusedUse, closure.unusedUse, closure.unusedUse */
@ -1031,14 +1038,20 @@ class Feeds extends Handler_Protected {
UrlHelper::$fetch_last_error .= " (feed behind Cloudflare)"; UrlHelper::$fetch_last_error .= " (feed behind Cloudflare)";
} }
return array("code" => 5, "message" => UrlHelper::$fetch_last_error); Logger::log(E_USER_NOTICE, "An attempt to subscribe to '{$url}' failed (User: '{$user->login}'; ID: {$user->id}).",
truncate_string(UrlHelper::$fetch_last_error, 500, '…'));
return array("code" => 5, "message" => truncate_string(clean(UrlHelper::$fetch_last_error), 250, '…'));
} }
if (str_contains(UrlHelper::$fetch_last_content_type, "html") && self::_is_html($contents)) { if (str_contains(UrlHelper::$fetch_last_content_type, "html") && self::_is_html($contents)) {
$feedUrls = self::_get_feeds_from_html($url, $contents); $feedUrls = self::_get_feeds_from_html($url, $contents);
if (count($feedUrls) == 0) { if (count($feedUrls) == 0) {
return array("code" => 3); Logger::log(E_USER_NOTICE, "An attempt to subscribe to '{$url}' failed due to content being HTML without detected feed URLs (User: '{$user->login}'; ID: {$user->id}).",
truncate_string($contents, 500, '…'));
return array("code" => 3, "message" => truncate_string(clean($contents), 250, '…'));
} else if (count($feedUrls) > 1) { } else if (count($feedUrls) > 1) {
return array("code" => 4, "feeds" => $feedUrls); return array("code" => 4, "feeds" => $feedUrls);
} }

View File

@ -118,10 +118,10 @@ const CommonDialogs = {
</footer> </footer>
</form> </form>
`, `,
show_error: function (msg) { show_error: function (msg, additional_info) {
const elem = App.byId("fadd_error_message"); const elem = App.byId("fadd_error_message");
elem.innerHTML = msg; elem.innerHTML = `${msg}${additional_info ? `<br><br><h4>${__('Additional information')}</h4>${additional_info}` : ''}`;
Element.show(elem); Element.show(elem);
}, },
@ -168,7 +168,7 @@ const CommonDialogs = {
dialog.show_error(__("Specified URL seems to be invalid.")); dialog.show_error(__("Specified URL seems to be invalid."));
break; break;
case 3: case 3:
dialog.show_error(__("Specified URL doesn't seem to contain any feeds.")); dialog.show_error(__("Specified URL doesn't seem to contain any feeds."), App.escapeHtml(rc['message']));
break; break;
case 4: case 4:
{ {
@ -193,10 +193,7 @@ const CommonDialogs = {
} }
break; break;
case 5: case 5:
dialog.show_error(__("Couldn't download the specified URL: %s").replace("%s", rc['message'])); dialog.show_error(__("Couldn't download the specified URL."), App.escapeHtml(rc['message']));
break;
case 6:
dialog.show_error(__("XML validation failed: %s").replace("%s", rc['message']));
break; break;
case 7: case 7:
dialog.show_error(__("Error while creating feed database entry.")); dialog.show_error(__("Error while creating feed database entry."));