mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 02:59:07 +08:00
Previously, the RSS Polling plugin's admin page predated Discourse's admin interface guidelines: it was a single inline-editable table with no breadcrumbs or page header, it truncated long feed URLs, it required horizontal scrolling on mobile, and it gave admins no way to verify a feed before saving it. This change rebuilds the page on the standard plugin "show route" structure (breadcrumbs, page header, and Settings + Feeds tabs) with a `d-table` feed list and dedicated FormKit new/edit routes, so the UI now matches the rest of the admin interface and works on mobile. It also: - Adds a **Test feed** dry-run that fetches a feed and previews which items would be imported or skipped, with plain-language reasons, so admins can validate a feed (and fix setup errors) before saving it. - Adds the `rss_polling_feed_request_timeout` and `rss_polling_verbose_logging` site settings. - Extracts the fetch/parse and import/skip logic into `FeedFetcher` and `FeedAnalyzer`, shared by the poll job and the new preview so the dry-run and the real import always agree. - Normalizes feed dates and categories so Atom feeds (which use `<updated>` and `term`-style `<category>` elements) are handled like RSS, and returns a clean error instead of a 500 when a feed cannot be read. --------- Co-authored-by: Martin Brennan <martin@discourse.org>
52 lines
1.8 KiB
Ruby
Vendored
52 lines
1.8 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
RSpec.describe DiscourseRssPolling::FeedUrl do
|
|
describe ".redact" do
|
|
it "removes api_key and api_username from the query" do
|
|
redacted =
|
|
described_class.redact(
|
|
"https://example.com/feed?api_key=secret&api_username=system&foo=bar",
|
|
)
|
|
|
|
expect(redacted).to eq("https://example.com/feed?foo=bar")
|
|
expect(redacted).not_to include("secret")
|
|
expect(redacted).not_to include("system")
|
|
end
|
|
|
|
it "drops the query entirely when only credentials are present" do
|
|
expect(described_class.redact("https://example.com/feed?api_key=secret")).to eq(
|
|
"https://example.com/feed",
|
|
)
|
|
end
|
|
|
|
it "leaves a credential-free url untouched" do
|
|
expect(described_class.redact("https://example.com/feed")).to eq("https://example.com/feed")
|
|
end
|
|
|
|
it "does not leak credentials when the url is malformed" do
|
|
expect(described_class.redact("https://exa mple.com/feed?api_key=secret")).not_to include(
|
|
"secret",
|
|
)
|
|
end
|
|
|
|
it "handles blank input" do
|
|
expect(described_class.redact(nil)).to eq("")
|
|
end
|
|
end
|
|
|
|
describe ".http?" do
|
|
it "is true for http and https urls (any case)" do
|
|
expect(described_class.http?("http://example.com/feed")).to eq(true)
|
|
expect(described_class.http?("https://example.com/feed")).to eq(true)
|
|
expect(described_class.http?("HTTPS://example.com/feed")).to eq(true)
|
|
end
|
|
|
|
it "is false for other schemes, scheme-less urls, and blank input" do
|
|
expect(described_class.http?("javascript:alert(1)")).to eq(false)
|
|
expect(described_class.http?("ftp://example.com/feed")).to eq(false)
|
|
expect(described_class.http?("example.com/feed")).to eq(false)
|
|
expect(described_class.http?("")).to eq(false)
|
|
expect(described_class.http?(nil)).to eq(false)
|
|
end
|
|
end
|
|
end
|