Santizes right to left div elements from Feedly content.

This commit is contained in:
Kiel Gillard 2020-01-29 18:28:37 +11:00
parent 13a1ef0605
commit a339b05bf8
11 changed files with 365 additions and 28 deletions

View File

@ -111,6 +111,9 @@
9E44C90F23C6FF3600CCC286 /* FeedlyIngestStreamArticleIdsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E44C90E23C6FF3600CCC286 /* FeedlyIngestStreamArticleIdsOperation.swift */; };
9E5ABE9A236BE6BD00B5DE9F /* feedly-1-initial in Resources */ = {isa = PBXBuildFile; fileRef = 9E5ABE99236BE6BC00B5DE9F /* feedly-1-initial */; };
9E5DE60E23C3F4B70064DA30 /* FeedlyFetchIdsForMissingArticlesOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5DE60D23C3F4B70064DA30 /* FeedlyFetchIdsForMissingArticlesOperation.swift */; };
9E5EC15923E01D8A00A4E503 /* FeedlyCollectionParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5EC15823E01D8A00A4E503 /* FeedlyCollectionParser.swift */; };
9E5EC15B23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5EC15A23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift */; };
9E5EC15D23E0D58500A4E503 /* FeedlyFeedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5EC15C23E0D58500A4E503 /* FeedlyFeedParser.swift */; };
9E672394236F7CA0000BE141 /* FeedlyRefreshAccessTokenOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E672393236F7CA0000BE141 /* FeedlyRefreshAccessTokenOperation.swift */; };
9E672396236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E672395236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift */; };
9E7299D723505E9600DAEFB7 /* FeedlyAddFeedToCollectionOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7299D623505E9600DAEFB7 /* FeedlyAddFeedToCollectionOperation.swift */; };
@ -164,6 +167,10 @@
9EF1B10723590D61000A486A /* FeedlyGetStreamIdsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF1B10623590D61000A486A /* FeedlyGetStreamIdsOperation.swift */; };
9EF1B10923590E93000A486A /* FeedlyStreamIds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF1B10823590E93000A486A /* FeedlyStreamIds.swift */; };
9EF2602C23C91FFE006D160C /* FeedlyGetUpdatedArticleIdsOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF2602B23C91FFE006D160C /* FeedlyGetUpdatedArticleIdsOperation.swift */; };
9EF58EB023E1606000992A2B /* FeedlyTextSanitizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EAF23E1606000992A2B /* FeedlyTextSanitizationTests.swift */; };
9EF58EB223E1647400992A2B /* FeedlyCollectionParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EB123E1647400992A2B /* FeedlyCollectionParserTests.swift */; };
9EF58EB423E1655300992A2B /* FeedlyFeedParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EB323E1655300992A2B /* FeedlyFeedParserTests.swift */; };
9EF58EB623E1669F00992A2B /* FeedlyEntryParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EF58EB523E1669F00992A2B /* FeedlyEntryParserTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -327,6 +334,9 @@
9E489E92236101FC004372EE /* FeedlyUpdateAccountFeedsWithItemsOperationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyUpdateAccountFeedsWithItemsOperationTests.swift; sourceTree = "<group>"; };
9E5ABE99236BE6BC00B5DE9F /* feedly-1-initial */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "feedly-1-initial"; sourceTree = "<group>"; };
9E5DE60D23C3F4B70064DA30 /* FeedlyFetchIdsForMissingArticlesOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFetchIdsForMissingArticlesOperation.swift; sourceTree = "<group>"; };
9E5EC15823E01D8A00A4E503 /* FeedlyCollectionParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCollectionParser.swift; sourceTree = "<group>"; };
9E5EC15A23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRTLTextSanitizer.swift; sourceTree = "<group>"; };
9E5EC15C23E0D58500A4E503 /* FeedlyFeedParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedParser.swift; sourceTree = "<group>"; };
9E672393236F7CA0000BE141 /* FeedlyRefreshAccessTokenOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyRefreshAccessTokenOperation.swift; sourceTree = "<group>"; };
9E672395236F7E68000BE141 /* OAuthAcessTokenRefreshing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthAcessTokenRefreshing.swift; sourceTree = "<group>"; };
9E7299D623505E9600DAEFB7 /* FeedlyAddFeedToCollectionOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyAddFeedToCollectionOperation.swift; sourceTree = "<group>"; };
@ -386,6 +396,10 @@
9EF1B10623590D61000A486A /* FeedlyGetStreamIdsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetStreamIdsOperation.swift; sourceTree = "<group>"; };
9EF1B10823590E93000A486A /* FeedlyStreamIds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyStreamIds.swift; sourceTree = "<group>"; };
9EF2602B23C91FFE006D160C /* FeedlyGetUpdatedArticleIdsOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyGetUpdatedArticleIdsOperation.swift; sourceTree = "<group>"; };
9EF58EAF23E1606000992A2B /* FeedlyTextSanitizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyTextSanitizationTests.swift; sourceTree = "<group>"; };
9EF58EB123E1647400992A2B /* FeedlyCollectionParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyCollectionParserTests.swift; sourceTree = "<group>"; };
9EF58EB323E1655300992A2B /* FeedlyFeedParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyFeedParserTests.swift; sourceTree = "<group>"; };
9EF58EB523E1669F00992A2B /* FeedlyEntryParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedlyEntryParserTests.swift; sourceTree = "<group>"; };
D511EEB5202422BB00712EC3 /* Account_project_debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_debug.xcconfig; sourceTree = "<group>"; };
D511EEB6202422BB00712EC3 /* Account_target.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_target.xcconfig; sourceTree = "<group>"; };
D511EEB7202422BB00712EC3 /* Account_project_release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Account_project_release.xcconfig; sourceTree = "<group>"; };
@ -652,6 +666,10 @@
9E0260CA236FF99A00D122D3 /* FeedlyRefreshAccessTokenOperationTests.swift */,
9E784EBF237E8BE100099B1B /* FeedlyLogoutOperationTests.swift */,
9EA643D823945CE00018A28C /* FeedlyAddNewFeedOperationTests.swift */,
9EF58EAF23E1606000992A2B /* FeedlyTextSanitizationTests.swift */,
9EF58EB123E1647400992A2B /* FeedlyCollectionParserTests.swift */,
9EF58EB323E1655300992A2B /* FeedlyFeedParserTests.swift */,
9EF58EB523E1669F00992A2B /* FeedlyEntryParserTests.swift */,
9E79F7732395C9EF0031DB98 /* feedly-add-new-feed */,
9E5ABE99236BE6BC00B5DE9F /* feedly-1-initial */,
9EC804E4236C1A7F0057CFCB /* feedly-2-changestatuses */,
@ -720,7 +738,9 @@
children = (
9E1773D823458D590056A5A8 /* FeedlyResourceId.swift */,
9EAEC60B2332FE830085D7C9 /* FeedlyCollection.swift */,
9E5EC15823E01D8A00A4E503 /* FeedlyCollectionParser.swift */,
9EAEC60D2332FEC20085D7C9 /* FeedlyFeed.swift */,
9E5EC15C23E0D58500A4E503 /* FeedlyFeedParser.swift */,
9EAEC623233315F60085D7C9 /* FeedlyEntry.swift */,
9E1773D4234570E30056A5A8 /* FeedlyEntryParser.swift */,
9EAEC625233318400085D7C9 /* FeedlyStream.swift */,
@ -731,6 +751,7 @@
9E1773D6234575AB0056A5A8 /* FeedlyTag.swift */,
9EA643D4239306AC0018A28C /* FeedlyFeedsSearchResponse.swift */,
9EBD49C123C67784005AD5CD /* FeedlyEntryIdentifierProviding.swift */,
9E5EC15A23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift */,
);
path = Models;
sourceTree = "<group>";
@ -977,10 +998,12 @@
51E5959B228C781500FCC42B /* FeedbinStarredEntry.swift in Sources */,
846E77451F6EF9B900A165E2 /* Container.swift in Sources */,
9EA643D3239305680018A28C /* FeedlySearchOperation.swift in Sources */,
9E5EC15D23E0D58500A4E503 /* FeedlyFeedParser.swift in Sources */,
9E1D15532334304B00F4944C /* FeedlyGetStreamContentsOperation.swift in Sources */,
9E12B0202334696A00ADE5A0 /* FeedlyCreateFeedsForCollectionFoldersOperation.swift in Sources */,
552032FD229D5D5A009559E0 /* ReaderAPITagging.swift in Sources */,
9EAEC62A23331EE70085D7C9 /* FeedlyOrigin.swift in Sources */,
9E5EC15B23E01DEF00A4E503 /* FeedlyRTLTextSanitizer.swift in Sources */,
511B9804237CD4270028BCAA /* FeedIdentifier.swift in Sources */,
84F73CF1202788D90000BCEF /* ArticleFetcher.swift in Sources */,
841974251F6DDCE4006346C4 /* AccountDelegate.swift in Sources */,
@ -995,6 +1018,7 @@
9EEAE06E235D002D00E3FEE4 /* FeedlyGetCollectionsService.swift in Sources */,
5165D72922835F7A00D9D53D /* FeedSpecifier.swift in Sources */,
9E85C8ED2367020700D0F1F7 /* FeedlyGetEntriesService.swift in Sources */,
9E5EC15923E01D8A00A4E503 /* FeedlyCollectionParser.swift in Sources */,
9E84DC492359A73600D6E809 /* FeedlyCheckpointOperation.swift in Sources */,
9E85C8EB236700E600D0F1F7 /* FeedlyGetEntriesOperation.swift in Sources */,
9E1D154D233370D800F4944C /* FeedlySyncAllOperation.swift in Sources */,
@ -1081,6 +1105,7 @@
9EC228572362C7F900766EF8 /* FeedlyCheckpointOperationTests.swift in Sources */,
9E03C122235E62E100FB6D9E /* FeedlyTestSupport.swift in Sources */,
9EAADA1023C93144003A801F /* TestGetEntriesService.swift in Sources */,
9EF58EB423E1655300992A2B /* FeedlyFeedParserTests.swift in Sources */,
9E1FF8622368219B00834C24 /* TestGetPagedStreamIdsService.swift in Sources */,
9E03C11E235D976500FB6D9E /* FeedlyGetCollectionsOperationTests.swift in Sources */,
9E85C8E62366FED600D0F1F7 /* TestGetStreamContentsService.swift in Sources */,
@ -1088,14 +1113,17 @@
9E03C11C235D921400FB6D9E /* FeedlyOperationTests.swift in Sources */,
9E1FF8602368216B00834C24 /* TestGetStreamIdsService.swift in Sources */,
9E85C8E82366FF4200D0F1F7 /* TestGetPagedStreamContentsService.swift in Sources */,
9EF58EB023E1606000992A2B /* FeedlyTextSanitizationTests.swift in Sources */,
5165D7122282080C00D9D53D /* AccountFeedbinFolderContentsSyncTest.swift in Sources */,
51D5875E227F643C00900287 /* AccountFeedbinFolderSyncTest.swift in Sources */,
9EF58EB623E1669F00992A2B /* FeedlyEntryParserTests.swift in Sources */,
9EC804E3236C18AB0057CFCB /* FeedlySyncAllMockResponseProvider.swift in Sources */,
9E1FF8682368EE4900834C24 /* TestGetCollectionsService.swift in Sources */,
5107A09B227DE49500C7C3C5 /* TestAccountManager.swift in Sources */,
513323082281070D00C30F19 /* AccountFeedbinSyncTest.swift in Sources */,
5107A09D227DE77700C7C3C5 /* TestTransport.swift in Sources */,
9E1773DB234593CF0056A5A8 /* FeedlyResourceIdTests.swift in Sources */,
9EF58EB223E1647400992A2B /* FeedlyCollectionParserTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -0,0 +1,28 @@
//
// FeedlyCollectionParserTests.swift
// AccountTests
//
// Created by Kiel Gillard on 29/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import XCTest
@testable import Account
class FeedlyCollectionParserTests: XCTestCase {
func testParsing() {
let collection = FeedlyCollection(feeds: [], label: "Test Collection", id: "test/collection/1")
let parser = FeedlyCollectionParser(collection: collection)
XCTAssertEqual(parser.folderName, collection.label)
XCTAssertEqual(parser.externalID, collection.id)
}
func testSanitization() {
let name = "Test Collection"
let collection = FeedlyCollection(feeds: [], label: "<div style=\"direction:rtl;text-align:right\">\(name)</div>", id: "test/collection/1")
let parser = FeedlyCollectionParser(collection: collection)
XCTAssertEqual(parser.folderName, name)
XCTAssertEqual(parser.externalID, collection.id)
}
}

View File

@ -0,0 +1,121 @@
//
// FeedlyEntryParserTests.swift
// AccountTests
//
// Created by Kiel Gillard on 29/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import XCTest
@testable import Account
class FeedlyEntryParserTests: XCTestCase {
func testParsing() {
let content = FeedlyEntry.Content(content: "Test Content", direction: .leftToRight)
let summary = FeedlyEntry.Content(content: "Test Summary", direction: .leftToRight)
let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil)
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
title: "Test Entry 1",
content: content,
summary: summary,
author: nil,
crawled: .distantPast,
recrawled: Date(timeIntervalSinceReferenceDate: 0),
origin: origin,
canonical: nil,
alternate: nil,
unread: false,
tags: nil,
categories: nil,
enclosure: nil)
let parser = FeedlyEntryParser(entry: entry)
XCTAssertEqual(parser.id, entry.id)
XCTAssertEqual(parser.feedUrl, origin.streamId)
XCTAssertEqual(parser.externalUrl, nil)
XCTAssertEqual(parser.title, entry.title)
XCTAssertEqual(parser.contentHMTL, content.content)
XCTAssertEqual(parser.summary, summary.content)
XCTAssertEqual(parser.datePublished, .distantPast)
XCTAssertEqual(parser.dateModified, Date(timeIntervalSinceReferenceDate: 0))
}
func testSanitization() {
let content = FeedlyEntry.Content(content: "<div style=\"direction:rtl;text-align:right\">Test Content</div>", direction: .rightToLeft)
let summaryContent = "Test Summary"
let summary = FeedlyEntry.Content(content: "<div style=\"direction:rtl;text-align:right\">\(summaryContent)</div>", direction: .rightToLeft)
let origin = FeedlyOrigin(title: "Test Feed", streamId: "tests://feeds/1", htmlUrl: nil)
let title = "Test Entry 1"
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
title: "<div style=\"direction:rtl;text-align:right\">\(title)</div>",
content: content,
summary: summary,
author: nil,
crawled: .distantPast,
recrawled: nil,
origin: origin,
canonical: nil,
alternate: nil,
unread: false,
tags: nil,
categories: nil,
enclosure: nil)
let parser = FeedlyEntryParser(entry: entry)
// These should be sanitized
XCTAssertEqual(parser.title, title)
XCTAssertEqual(parser.summary, summaryContent)
// These should not be sanitized because it is supposed to be HTML content.
XCTAssertEqual(parser.contentHMTL, content.content)
}
func testLocatesCanonicalExternalUrl() {
let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/html")
let alternateLink = FeedlyLink(href: "tests://feeds/1/entries/alternate/1", type: "text/html")
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
title: "Test Entry 1",
content: nil,
summary: nil,
author: nil,
crawled: .distantPast,
recrawled: Date(timeIntervalSinceReferenceDate: 0),
origin: nil,
canonical: [canonicalLink],
alternate: [alternateLink],
unread: false,
tags: nil,
categories: nil,
enclosure: nil)
let parser = FeedlyEntryParser(entry: entry)
XCTAssertEqual(parser.externalUrl, canonicalLink.href)
}
func testLocatesAlternateExternalUrl() {
let canonicalLink = FeedlyLink(href: "tests://feeds/1/entries/1", type: "text/json")
let alternateLink = FeedlyLink(href: "tests://feeds/1/entries/alternate/1", type: nil)
let entry = FeedlyEntry(id: "tests/feeds/1/entries/1",
title: "Test Entry 1",
content: nil,
summary: nil,
author: nil,
crawled: .distantPast,
recrawled: Date(timeIntervalSinceReferenceDate: 0),
origin: nil,
canonical: [canonicalLink],
alternate: [alternateLink],
unread: false,
tags: nil,
categories: nil,
enclosure: nil)
let parser = FeedlyEntryParser(entry: entry)
XCTAssertEqual(parser.externalUrl, alternateLink.href)
}
}

View File

@ -0,0 +1,41 @@
//
// FeedlyFeedParserTests.swift
// AccountTests
//
// Created by Kiel Gillard on 29/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import XCTest
@testable import Account
class FeedlyFeedParserTests: XCTestCase {
func testParsing() {
let name = "Test Feed"
let website = "tests://nnw/feed/1"
let url = "tests://nnw/feed.xml"
let id = "feed/\(url)"
let updated = Date.distantPast
let feed = FeedlyFeed(id: id, title: name, updated: updated, website: website)
let parser = FeedlyFeedParser(feed: feed)
XCTAssertEqual(parser.title, name)
XCTAssertEqual(parser.homePageURL, website)
XCTAssertEqual(parser.url, url)
XCTAssertEqual(parser.webFeedID, id)
}
func testSanitization() {
let name = "Test Feed"
let website = "tests://nnw/feed/1"
let url = "tests://nnw/feed.xml"
let id = "feed/\(url)"
let updated = Date.distantPast
let feed = FeedlyFeed(id: id, title: "<div style=\"direction:rtl;text-align:right\">\(name)</div>", updated: updated, website: website)
let parser = FeedlyFeedParser(feed: feed)
XCTAssertEqual(parser.title, name)
XCTAssertEqual(parser.homePageURL, website)
XCTAssertEqual(parser.url, url)
XCTAssertEqual(parser.webFeedID, id)
}
}

View File

@ -0,0 +1,38 @@
//
// FeedlyTextSanitizationTests.swift
// AccountTests
//
// Created by Kiel Gillard on 29/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import XCTest
@testable import Account
class FeedlyTextSanitizationTests: XCTestCase {
func testRTLSanitization() {
let targetsAndExpectations: [(target: String?, expectation: String?)] = [
(nil, nil),
("", ""),
(" ", " "),
("text", "text"),
("<div style=\"direction:rtl;text-align:right\">", "<div style=\"direction:rtl;text-align:right\">"),
("</div>", "</div>"),
("<div style=\"direction:rtl;text-align:right\">text", "<div style=\"direction:rtl;text-align:right\">text"),
("text</div>", "text</div>"),
("<div style=\"direction:rtl;text-align:right\"></div>", ""),
("<DIV style=\"direction:rtl;text-align:right\"></div>", "<DIV style=\"direction:rtl;text-align:right\"></div>"),
("<div style=\"direction:rtl;text-align:right\"></DIV>", "<div style=\"direction:rtl;text-align:right\"></DIV>"),
("<div style=\"direction:rtl;text-align:right\">text</div>", "text"),
]
let sanitizer = FeedlyRTLTextSanitizer()
for (target, expectation) in targetsAndExpectations {
let calculated = sanitizer.sanitize(target)
XCTAssertEqual(expectation, calculated)
}
}
}

View File

@ -0,0 +1,23 @@
//
// FeedlyCollectionParser.swift
// Account
//
// Created by Kiel Gillard on 28/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct FeedlyCollectionParser {
let collection: FeedlyCollection
private let rightToLeftTextSantizer = FeedlyRTLTextSanitizer()
var folderName: String {
return rightToLeftTextSantizer.sanitize(collection.label) ?? ""
}
var externalID: String {
return collection.id
}
}

View File

@ -13,6 +13,8 @@ import RSParser
struct FeedlyEntryParser {
let entry: FeedlyEntry
private let rightToLeftTextSantizer = FeedlyRTLTextSanitizer()
var id: String {
return entry.id
}
@ -36,7 +38,7 @@ struct FeedlyEntryParser {
}
var title: String? {
return entry.title
return rightToLeftTextSantizer.sanitize(entry.title)
}
var contentHMTL: String? {
@ -49,7 +51,7 @@ struct FeedlyEntryParser {
}
var summary: String? {
return entry.summary?.content
return rightToLeftTextSantizer.sanitize(entry.summary?.content)
}
var datePublished: Date {

View File

@ -0,0 +1,32 @@
//
// FeedlyFeedParser.swift
// Account
//
// Created by Kiel Gillard on 29/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct FeedlyFeedParser {
let feed: FeedlyFeed
private let rightToLeftTextSantizer = FeedlyRTLTextSanitizer()
var title: String? {
return rightToLeftTextSantizer.sanitize(feed.title) ?? ""
}
var webFeedID: String {
return feed.id
}
var url: String {
let resource = FeedlyFeedResourceId(id: feed.id)
return resource.url
}
var homePageURL: String? {
return feed.website
}
}

View File

@ -0,0 +1,28 @@
//
// FeedlyRTLTextSanitizer.swift
// Account
//
// Created by Kiel Gillard on 28/1/20.
// Copyright © 2020 Ranchero Software, LLC. All rights reserved.
//
import Foundation
struct FeedlyRTLTextSanitizer {
private let rightToLeftPrefix = "<div style=\"direction:rtl;text-align:right\">"
private let rightToLeftSuffix = "</div>"
func sanitize(_ sourceText: String?) -> String? {
guard let source = sourceText, !source.isEmpty else {
return sourceText
}
guard source.hasPrefix(rightToLeftPrefix) && source.hasSuffix(rightToLeftSuffix) else {
return source
}
let start = source.index(source.startIndex, offsetBy: rightToLeftPrefix.indices.count)
let end = source.index(source.endIndex, offsetBy: -rightToLeftSuffix.indices.count)
return String(source[start..<end])
}
}

View File

@ -68,9 +68,11 @@ final class FeedlyCreateFeedsForCollectionFoldersOperation: FeedlyOperation {
}
// no exsiting feed, create a new one
let id = collectionFeed.id
let url = FeedlyFeedResourceId(id: id).url
let feed = account.createWebFeed(with: collectionFeed.title, url: url, webFeedID: id, homePageURL: collectionFeed.website)
let parser = FeedlyFeedParser(feed: collectionFeed)
let feed = account.createWebFeed(with: parser.title,
url: parser.url,
webFeedID: parser.webFeedID,
homePageURL: parser.homePageURL)
// So the same feed isn't created more than once.
feedsAdded.insert(feed)

View File

@ -9,22 +9,17 @@
import Foundation
import os.log
protocol FeedlyCollectionsAndFoldersProviding: class {
var collectionsAndFolders: [(FeedlyCollection, Folder)] { get }
}
protocol FeedlyFeedsAndFoldersProviding {
var feedsAndFolders: [([FeedlyFeed], Folder)] { get }
}
/// Reflect Collections from Feedly as Folders.
final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyCollectionsAndFoldersProviding, FeedlyFeedsAndFoldersProviding {
final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyFeedsAndFoldersProviding {
let account: Account
let collectionsProvider: FeedlyCollectionProviding
let log: OSLog
private(set) var collectionsAndFolders = [(FeedlyCollection, Folder)]()
private(set) var feedsAndFolders = [([FeedlyFeed], Folder)]()
init(account: Account, collectionsProvider: FeedlyCollectionProviding, log: OSLog) {
@ -41,29 +36,28 @@ final class FeedlyMirrorCollectionsAsFoldersOperation: FeedlyOperation, FeedlyCo
let localFolders = account.folders ?? Set()
let collections = collectionsProvider.collections
let pairs = collections.compactMap { collection -> (FeedlyCollection, Folder)? in
guard let folder = account.ensureFolder(with: collection.label) else {
feedsAndFolders = collections.compactMap { collection -> ([FeedlyFeed], Folder)? in
let parser = FeedlyCollectionParser(collection: collection)
guard let folder = account.ensureFolder(with: parser.folderName) else {
assertionFailure("Why wasn't a folder created?")
return nil
}
folder.externalID = collection.id
return (collection, folder)
}
collectionsAndFolders = pairs
os_log(.debug, log: log, "Ensured %i folders for %i collections.", pairs.count, collections.count)
feedsAndFolders = pairs.map { (collection, folder) -> (([FeedlyFeed], Folder)) in
folder.externalID = parser.externalID
return (collection.feeds, folder)
}
// Remove folders without a corresponding collection
let collectionFolders = Set(pairs.map { $0.1 })
let foldersWithoutCollections = localFolders.subtracting(collectionFolders)
for unmatched in foldersWithoutCollections {
account.removeFolder(unmatched)
}
os_log(.debug, log: log, "Ensured %i folders for %i collections.", feedsAndFolders.count, collections.count)
os_log(.debug, log: log, "Removed %i folders: %@", foldersWithoutCollections.count, foldersWithoutCollections.map { $0.externalID ?? $0.nameForDisplay })
// Remove folders without a corresponding collection
let collectionFolders = Set(feedsAndFolders.map { $0.1 })
let foldersWithoutCollections = localFolders.subtracting(collectionFolders)
if !foldersWithoutCollections.isEmpty {
for unmatched in foldersWithoutCollections {
account.removeFolder(unmatched)
}
os_log(.debug, log: log, "Removed %i folders: %@", foldersWithoutCollections.count, foldersWithoutCollections.map { $0.externalID ?? $0.nameForDisplay })
}
}
}