diff --git a/Sources/Publish/API/ItemRSSProperties.swift b/Sources/Publish/API/ItemRSSProperties.swift index 8b245cd4..594e91c0 100644 --- a/Sources/Publish/API/ItemRSSProperties.swift +++ b/Sources/Publish/API/ItemRSSProperties.swift @@ -10,7 +10,8 @@ import Foundation public struct ItemRSSProperties: Codable, Hashable { /// Any specific GUID that should be added for the item. When `nil`, /// the item's URL will be used and the `isPermaLink` attribute will - /// be set to `true`. If not `nil`, a non-permalink will be assumed. + /// be set to `true`, unless an explicit `link` was specified. If this + /// property is not `nil`, then a non-permalink GUID will be assumed. public var guid: String? /// Any prefix that should be added to the item's title within an RSS feed. public var titlePrefix: String? @@ -20,6 +21,12 @@ public struct ItemRSSProperties: Codable, Hashable { public var bodyPrefix: String? /// Any suffix that should be added to the item's body HTML within an RSS feed. public var bodySuffix: String? + /// Any specific URL that the item should link to when inlcluded in an RSS + /// feed. By default, the item's location on its website will be used. Note that + /// this link won't be automatically used as the item's GUID, however, setting + /// this property to a non-`nil` value will set the GUID's `isPermaLink` attribute + /// to `false`. + public var link: URL? = nil /// Initialize an instance of this type /// - parameter guid: Any specific GUID that should be added for the item. @@ -27,15 +34,18 @@ public struct ItemRSSProperties: Codable, Hashable { /// - parameter titleSuffix: Any suffix that should be added to the item's title. /// - parameter bodyPrefix: Any prefix that should be added to the item's body HTML. /// - parameter bodySuffix: Any suffix that should be added to the item's body HTML. + /// - parameter link: Any specific URL that the item should link to, other than its location. public init(guid: String? = nil, titlePrefix: String? = nil, titleSuffix: String? = nil, bodyPrefix: String? = nil, - bodySuffix: String? = nil) { + bodySuffix: String? = nil, + link: URL? = nil) { self.guid = guid self.titlePrefix = titlePrefix self.titleSuffix = titleSuffix self.bodyPrefix = bodyPrefix self.bodySuffix = bodySuffix + self.link = link } } diff --git a/Sources/Publish/API/PlotComponents.swift b/Sources/Publish/API/PlotComponents.swift index f6f9b311..af6a9db2 100644 --- a/Sources/Publish/API/PlotComponents.swift +++ b/Sources/Publish/API/PlotComponents.swift @@ -149,7 +149,7 @@ internal extension Node where Context: RSSItemContext { static func guid(for item: Item, site: T) -> Node { return .guid( .text(item.rssProperties.guid ?? site.url(for: item).absoluteString), - .isPermaLink(item.rssProperties.guid == nil) + .isPermaLink(item.rssProperties.guid == nil && item.rssProperties.link == nil) ) } diff --git a/Sources/Publish/Internal/RSSFeedGenerator.swift b/Sources/Publish/Internal/RSSFeedGenerator.swift index a6124655..d6a2e1d6 100644 --- a/Sources/Publish/Internal/RSSFeedGenerator.swift +++ b/Sources/Publish/Internal/RSSFeedGenerator.swift @@ -71,7 +71,7 @@ private extension RSSFeedGenerator { .guid(for: item, site: context.site), .title(item.rssTitle), .description(item.description), - .link(context.site.url(for: item)), + .link(item.rssProperties.link ?? context.site.url(for: item)), .pubDate(item.date, timeZone: context.dateFormatter.timeZone), .content(for: item, site: context.site) ) diff --git a/Tests/PublishTests/Tests/RSSFeedGenerationTests.swift b/Tests/PublishTests/Tests/RSSFeedGenerationTests.swift index a1cf61c0..1f2698c9 100644 --- a/Tests/PublishTests/Tests/RSSFeedGenerationTests.swift +++ b/Tests/PublishTests/Tests/RSSFeedGenerationTests.swift @@ -96,6 +96,27 @@ final class RSSFeedGenerationTests: PublishTestCase { """)) } + func testCustomItemLink() throws { + let folder = try Folder.createTemporary() + + try generateFeed(in: folder, content: [ + "one/item.md": """ + --- + rss.link: custom.link + --- + Body + """ + ]) + + let feed = try folder.file(at: "Output/feed.rss").readAsString() + + XCTAssertTrue(feed.contains("custom.link")) + + XCTAssertTrue(feed.contains(""" + https://swiftbysundell.com/one/item + """)) + } + func testReusingPreviousFeedIfNoItemsWereModified() throws { let folder = try Folder.createTemporary() let contentFile = try folder.createFile(at: "Content/one/item.md") @@ -160,6 +181,7 @@ extension RSSFeedGenerationTests { ("testConvertingRelativeLinksToAbsolute", testConvertingRelativeLinksToAbsolute), ("testItemTitlePrefixAndSuffix", testItemTitlePrefixAndSuffix), ("testItemBodyPrefixAndSuffix", testItemBodyPrefixAndSuffix), + ("testCustomItemLink", testCustomItemLink), ("testReusingPreviousFeedIfNoItemsWereModified", testReusingPreviousFeedIfNoItemsWereModified), ("testNotReusingPreviousFeedIfConfigChanged", testNotReusingPreviousFeedIfConfigChanged), ("testNotReusingPreviousFeedIfItemWasAdded", testNotReusingPreviousFeedIfItemWasAdded)