From 19145505c5d9be6bce48aa38af6dc73d8d973e90 Mon Sep 17 00:00:00 2001 From: Maurice Parker Date: Mon, 14 Oct 2019 20:45:58 -0500 Subject: [PATCH] Add the ability to import NNW 3 Subscription plist files. Issue #1129 --- Mac/AppDelegate.swift | 16 ++- Mac/Base.lproj/Main.storyboard | 11 +- Mac/MainWindow/NNW3/ImportNNW3Sheet.xib | 104 +++++++++++++++++ .../NNW3/ImportNNW3WindowController.swift | 110 ++++++++++++++++++ Mac/MainWindow/NNW3/NNW3Document.swift | 53 +++++++++ Mac/MainWindow/NNW3/NNW3Entry.swift | 61 ++++++++++ Mac/MainWindow/NNW3/NNW3Feed.swift | 48 ++++++++ Mac/MainWindow/NNW3/NNW3FeedsImporter.swift | 50 ++++++++ Mac/MainWindow/NNW3/NNW3PlistConverter.swift | 39 +++++++ NetNewsWire.xcodeproj/project.pbxproj | 48 +++++++- 10 files changed, 527 insertions(+), 13 deletions(-) create mode 100644 Mac/MainWindow/NNW3/ImportNNW3Sheet.xib create mode 100644 Mac/MainWindow/NNW3/ImportNNW3WindowController.swift create mode 100644 Mac/MainWindow/NNW3/NNW3Document.swift create mode 100644 Mac/MainWindow/NNW3/NNW3Entry.swift create mode 100644 Mac/MainWindow/NNW3/NNW3Feed.swift create mode 100644 Mac/MainWindow/NNW3/NNW3FeedsImporter.swift create mode 100644 Mac/MainWindow/NNW3/NNW3PlistConverter.swift diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 4270d4899..e38ee7bed 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -57,6 +57,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, private var addFeedController: AddFeedController? private var addFolderWindowController: AddFolderWindowController? private var importOPMLController: ImportOPMLWindowController? + private var importNNW3Controller: ImportNNW3WindowController? private var exportOPMLController: ExportOPMLWindowController? private var keyboardShortcutsWindowController: WebViewWindowController? private var inspectorWindowController: InspectorWindowController? @@ -126,6 +127,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, logDebugMessage("Is first run.") } let localAccount = AccountManager.shared.defaultAccount + NNW3FeedsImporter.importIfNeeded(isFirstRun, account: localAccount) DefaultFeedsImporter.importIfNeeded(isFirstRun, account: localAccount) let tempDirectory = NSTemporaryDirectory() @@ -395,7 +397,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, } @IBAction func toggleInspectorWindow(_ sender: Any?) { - if inspectorWindowController == nil { inspectorWindowController = (windowControllerWithName("Inspector") as! InspectorWindowController) } @@ -410,7 +411,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, } @IBAction func importOPMLFromFile(_ sender: Any?) { - createAndShowMainWindow() if mainWindowController!.isDisplayingSheet { return @@ -418,11 +418,19 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, importOPMLController = ImportOPMLWindowController() importOPMLController?.runSheetOnWindow(mainWindowController!.window!) + } + + @IBAction func importNNW3FromFile(_ sender: Any?) { + createAndShowMainWindow() + if mainWindowController!.isDisplayingSheet { + return + } + importNNW3Controller = ImportNNW3WindowController() + importNNW3Controller?.runSheetOnWindow(mainWindowController!.window!) } @IBAction func exportOPML(_ sender: Any?) { - createAndShowMainWindow() if mainWindowController!.isDisplayingSheet { return @@ -430,11 +438,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations, exportOPMLController = ExportOPMLWindowController() exportOPMLController?.runSheetOnWindow(mainWindowController!.window!) - } @IBAction func addAppNews(_ sender: Any?) { - if AccountManager.shared.anyAccountHasFeedWithURL(appNewsURLString) { return } diff --git a/Mac/Base.lproj/Main.storyboard b/Mac/Base.lproj/Main.storyboard index 26af7046d..b2769eccb 100644 --- a/Mac/Base.lproj/Main.storyboard +++ b/Mac/Base.lproj/Main.storyboard @@ -1,7 +1,8 @@ - + - + + @@ -91,6 +92,12 @@ + + + + + + diff --git a/Mac/MainWindow/NNW3/ImportNNW3Sheet.xib b/Mac/MainWindow/NNW3/ImportNNW3Sheet.xib new file mode 100644 index 000000000..522ead006 --- /dev/null +++ b/Mac/MainWindow/NNW3/ImportNNW3Sheet.xib @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Mac/MainWindow/NNW3/ImportNNW3WindowController.swift b/Mac/MainWindow/NNW3/ImportNNW3WindowController.swift new file mode 100644 index 000000000..3488ecb03 --- /dev/null +++ b/Mac/MainWindow/NNW3/ImportNNW3WindowController.swift @@ -0,0 +1,110 @@ +// +// ImportNNW3WindowController.swift +// NetNewsWire +// +// Created by Maurice Parker on 10/14/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import AppKit +import Account + +class ImportNNW3WindowController: NSWindowController { + + @IBOutlet weak var accountPopUpButton: NSPopUpButton! + private weak var hostWindow: NSWindow? + + convenience init() { + self.init(windowNibName: NSNib.Name("ImportNNW3Sheet")) + } + + override func windowDidLoad() { + accountPopUpButton.removeAllItems() + + let menu = NSMenu() + accountPopUpButton.menu = menu + + for oneAccount in AccountManager.shared.sortedActiveAccounts { + + let oneMenuItem = NSMenuItem() + oneMenuItem.title = oneAccount.nameForDisplay + oneMenuItem.representedObject = oneAccount + menu.addItem(oneMenuItem) + + if oneAccount.accountID == AppDefaults.importOPMLAccountID { + accountPopUpButton.select(oneMenuItem) + } + + } + } + + // MARK: API + + func runSheetOnWindow(_ hostWindow: NSWindow) { + + self.hostWindow = hostWindow + + if AccountManager.shared.activeAccounts.count == 1 { + let account = AccountManager.shared.activeAccounts.first! + importNNW3(account: account) + } else { + hostWindow.beginSheet(window!) + } + + } + + // MARK: Actions + + @IBAction func cancel(_ sender: Any) { + hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.cancel) + } + + @IBAction func importNNW3(_ sender: Any) { + + guard let menuItem = accountPopUpButton.selectedItem else { + return + } + + let account = menuItem.representedObject as! Account + AppDefaults.importOPMLAccountID = account.accountID + hostWindow!.endSheet(window!, returnCode: NSApplication.ModalResponse.OK) + importNNW3(account: account) + + } + + func importNNW3(account: Account) { + + let panel = NSOpenPanel() + panel.canDownloadUbiquitousContents = true + panel.canResolveUbiquitousConflicts = true + panel.canChooseFiles = true + panel.allowsMultipleSelection = false + panel.canChooseDirectories = false + panel.resolvesAliases = true + panel.directoryURL = URL(fileURLWithPath: NNW3PlistConverter.defaultFilePath) + panel.allowedFileTypes = ["plist"] + panel.allowsOtherFileTypes = false + + panel.beginSheetModal(for: hostWindow!) { modalResult in + if modalResult == NSApplication.ModalResponse.OK, let url = panel.url { + + guard let opmlURL = NNW3PlistConverter.convertToOPML(url: url) else { + return + } + + account.importOPML(opmlURL) { result in + try? FileManager.default.removeItem(at: opmlURL) + switch result { + case .success: + break + case .failure(let error): + NSApplication.shared.presentError(error) + } + } + + } + } + + } + +} diff --git a/Mac/MainWindow/NNW3/NNW3Document.swift b/Mac/MainWindow/NNW3/NNW3Document.swift new file mode 100644 index 000000000..d5738631f --- /dev/null +++ b/Mac/MainWindow/NNW3/NNW3Document.swift @@ -0,0 +1,53 @@ +// +// NNW3Document.swift +// NetNewsWire +// +// Created by Maurice Parker on 10/14/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import Foundation + +class NNW3Document: NNW3Entry { + + init(plist: [[String: Any]]) { + super.init(title: "NNW3") + + for child in plist { + if child["isContainer"] as? Bool ?? false { + entries.append(NNW3Entry(plist: child, parent: self)) + } else { + entries.append(NNW3Feed(plist: child, parent: self)) + } + } + + } + + override func makeXML(indentLevel: Int) -> String { + + var s = + """ + + + + \(title ?? "") + + + + """ + + for entry in entries { + s += entry.makeXML(indentLevel: indentLevel + 1) + } + + s += + """ + + + """ + + return s + + } + +} diff --git a/Mac/MainWindow/NNW3/NNW3Entry.swift b/Mac/MainWindow/NNW3/NNW3Entry.swift new file mode 100644 index 000000000..4cc19b911 --- /dev/null +++ b/Mac/MainWindow/NNW3/NNW3Entry.swift @@ -0,0 +1,61 @@ +// +// NNW3Entry.swift +// NetNewsWire +// +// Created by Maurice Parker on 10/14/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import Foundation +import RSCore + +class NNW3Entry { + + var title: String? + var entries = [NNW3Entry]() + + weak var parent: NNW3Entry? + + var isFolder: Bool { + return type(of: self) == NNW3Entry.self + } + + init(title: String?, parent: NNW3Entry? = nil) { + self.title = title + self.parent = parent + } + + convenience init(plist: [String: Any], parent: NNW3Entry? = nil) { + let title = plist["name"] as? String + self.init(title: title, parent: parent) + + guard let childrenArray = plist["childrenArray"] as? [[String: AnyObject]] else { + return + } + + for child in childrenArray { + if child["isContainer"] as? Bool ?? false { + entries.append(NNW3Entry(plist: child, parent: self)) + } else { + entries.append(NNW3Feed(plist: child, parent: self)) + } + } + + } + + func makeXML(indentLevel: Int) -> String { + + let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? "" + var s = "\n".rs_string(byPrependingNumberOfTabs: indentLevel) + + for entry in entries { + s += entry.makeXML(indentLevel: indentLevel + 1) + } + + s += "\n".rs_string(byPrependingNumberOfTabs: indentLevel) + + return s + + } + +} diff --git a/Mac/MainWindow/NNW3/NNW3Feed.swift b/Mac/MainWindow/NNW3/NNW3Feed.swift new file mode 100644 index 000000000..f6d54a480 --- /dev/null +++ b/Mac/MainWindow/NNW3/NNW3Feed.swift @@ -0,0 +1,48 @@ +// +// NNW3Feed.swift +// NetNewsWire +// +// Created by Maurice Parker on 10/14/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import Foundation +import RSCore + +class NNW3Feed: NNW3Entry { + + var pageURL: String? + var feedURL: String? + + init(feedURL: String) { + super.init(title: nil) + self.feedURL = feedURL + } + + init(title: String?, pageURL: String?, feedURL: String?, parent: NNW3Entry? = nil) { + super.init(title: title, parent: parent) + self.pageURL = pageURL + self.feedURL = feedURL + } + + convenience init(plist: [String: Any], parent: NNW3Entry? = nil) { + let title = plist["name"] as? String + let pageURL = plist["home"] as? String + let feedURL = plist["rss"] as? String + self.init(title: title, pageURL: pageURL, feedURL: feedURL, parent: parent) + } + + override func makeXML(indentLevel: Int) -> String { + + let t = title?.rs_stringByEscapingSpecialXMLCharacters() ?? "" + let p = pageURL?.rs_stringByEscapingSpecialXMLCharacters() ?? "" + let f = feedURL?.rs_stringByEscapingSpecialXMLCharacters() ?? "" + + var s = "\n" + s = s.rs_string(byPrependingNumberOfTabs: indentLevel) + + return s + + } + +} diff --git a/Mac/MainWindow/NNW3/NNW3FeedsImporter.swift b/Mac/MainWindow/NNW3/NNW3FeedsImporter.swift new file mode 100644 index 000000000..baaa14eba --- /dev/null +++ b/Mac/MainWindow/NNW3/NNW3FeedsImporter.swift @@ -0,0 +1,50 @@ +// +// NNW3FeedsImporter.swift +// NetNewsWire +// +// Created by Maurice Parker on 10/14/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import Foundation +import Account +import RSCore + +struct NNW3FeedsImporter { + + static func importIfNeeded(_ isFirstRun: Bool, account: Account) { + guard shouldImportDefaultFeeds(isFirstRun) else { + return + } + + if !FileManager.default.fileExists(atPath: NNW3PlistConverter.defaultFilePath) { + return + } + + appDelegate.logDebugMessage("Importing NNW3 feeds.") + + let url = URL(fileURLWithPath: NNW3PlistConverter.defaultFilePath) + guard let opmlURL = NNW3PlistConverter.convertToOPML(url: url) else { + return + } + + account.importOPML(opmlURL) { result in + try? FileManager.default.removeItem(at: opmlURL) + switch result { + case .success: + appDelegate.logDebugMessage("Importing NNW3 feeds succeeded.") + case .failure(let error): + appDelegate.logDebugMessage("Importing NNW3 feeds failed. \(error.localizedDescription)") + } + } + + } + + private static func shouldImportDefaultFeeds(_ isFirstRun: Bool) -> Bool { + if !isFirstRun || AccountManager.shared.anyAccountHasAtLeastOneFeed() { + return false + } + return true + } + +} diff --git a/Mac/MainWindow/NNW3/NNW3PlistConverter.swift b/Mac/MainWindow/NNW3/NNW3PlistConverter.swift new file mode 100644 index 000000000..310a2d93d --- /dev/null +++ b/Mac/MainWindow/NNW3/NNW3PlistConverter.swift @@ -0,0 +1,39 @@ +// +// NNW3Importer.swift +// NetNewsWire +// +// Created by Maurice Parker on 10/14/19. +// Copyright © 2019 Ranchero Software. All rights reserved. +// + +import Foundation + +class NNW3PlistConverter { + + static var defaultFilePath: String { + return ("~/Library/Application Support/NetNewsWire/Subscriptions.plist" as NSString).expandingTildeInPath + } + + static func convertToOPML(url: URL) -> URL? { + guard let data = try? Data(contentsOf: url) else { + return nil + } + + guard let nnw3plist = try? PropertyListSerialization.propertyList(from: data, options: [], format: nil) as? [[String: AnyObject]] else { + return nil + } + + let opmlURL = FileManager.default.temporaryDirectory.appendingPathComponent("NNW3.opml") + let doc = NNW3Document(plist: nnw3plist) + let opml = doc.makeXML(indentLevel: 0) + do { + try opml.write(to: opmlURL, atomically: true, encoding: .utf8) + } catch let error as NSError { + NSApplication.shared.presentError(error) + return nil + } + + return opmlURL + } + +} diff --git a/NetNewsWire.xcodeproj/project.pbxproj b/NetNewsWire.xcodeproj/project.pbxproj index d7a7360bd..0f20efa2e 100644 --- a/NetNewsWire.xcodeproj/project.pbxproj +++ b/NetNewsWire.xcodeproj/project.pbxproj @@ -35,6 +35,8 @@ 51554C25228B71910055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 51554C30228B71A10055115A /* SyncDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; }; 51554C31228B71A10055115A /* SyncDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 51554C01228B6EB50055115A /* SyncDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 516CAC4C235521070038D354 /* ImportNNW3Sheet.xib in Resources */ = {isa = PBXBuildFile; fileRef = 516CAC42235521070038D354 /* ImportNNW3Sheet.xib */; }; + 516CAC4D235521070038D354 /* ImportNNW3WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 516CAC4B235521070038D354 /* ImportNNW3WindowController.swift */; }; 5183CCD0226E1E880010922C /* NonIntrinsicLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */; }; 5183CCDA226E31A50010922C /* NonIntrinsicImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */; }; 5183CCDD226F1F5C0010922C /* NavigationProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCDC226F1F5C0010922C /* NavigationProgressView.swift */; }; @@ -46,6 +48,11 @@ 5183CCE9226F68D90010922C /* AccountRefreshTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */; }; 5183CCED22711DCE0010922C /* Settings.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5183CCEC22711DCE0010922C /* Settings.storyboard */; }; 5183CCEF227125970010922C /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5183CCEE227125970010922C /* SettingsViewController.swift */; }; + 5186515223552E610078E021 /* NNW3PlistConverter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515123552E610078E021 /* NNW3PlistConverter.swift */; }; + 5186515E23552F040078E021 /* NNW3Feed.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515B23552F040078E021 /* NNW3Feed.swift */; }; + 5186515F23552F040078E021 /* NNW3Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515C23552F040078E021 /* NNW3Document.swift */; }; + 5186516023552F040078E021 /* NNW3Entry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5186515D23552F040078E021 /* NNW3Entry.swift */; }; + 51865167235556240078E021 /* NNW3FeedsImporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51865166235556240078E021 /* NNW3FeedsImporter.swift */; }; 519B8D332143397200FA689C /* SharingServiceDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 519B8D322143397200FA689C /* SharingServiceDelegate.swift */; }; 51C451A9226377C200C03939 /* ArticlesDatabase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; }; 51C451AA226377C200C03939 /* ArticlesDatabase.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8407167F2262A61100344432 /* ArticlesDatabase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -690,6 +697,8 @@ 515436872291D75D005E1CDF /* AddLocalAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddLocalAccountViewController.swift; sourceTree = ""; }; 515436892291FED9005E1CDF /* FeedbinAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeedbinAccountViewController.swift; sourceTree = ""; }; 51554BFC228B6EB50055115A /* SyncDatabase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SyncDatabase.xcodeproj; path = Frameworks/SyncDatabase/SyncDatabase.xcodeproj; sourceTree = SOURCE_ROOT; }; + 516CAC42235521070038D354 /* ImportNNW3Sheet.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ImportNNW3Sheet.xib; sourceTree = ""; }; + 516CAC4B235521070038D354 /* ImportNNW3WindowController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportNNW3WindowController.swift; sourceTree = ""; }; 5183CCCF226E1E880010922C /* NonIntrinsicLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicLabel.swift; sourceTree = ""; }; 5183CCD9226E31A50010922C /* NonIntrinsicImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonIntrinsicImageView.swift; sourceTree = ""; }; 5183CCDC226F1F5C0010922C /* NavigationProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationProgressView.swift; sourceTree = ""; }; @@ -699,6 +708,11 @@ 5183CCE7226F68D90010922C /* AccountRefreshTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountRefreshTimer.swift; sourceTree = ""; }; 5183CCEC22711DCE0010922C /* Settings.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Settings.storyboard; sourceTree = ""; }; 5183CCEE227125970010922C /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; + 5186515123552E610078E021 /* NNW3PlistConverter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNW3PlistConverter.swift; sourceTree = ""; }; + 5186515B23552F040078E021 /* NNW3Feed.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Feed.swift; sourceTree = ""; }; + 5186515C23552F040078E021 /* NNW3Document.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Document.swift; sourceTree = ""; }; + 5186515D23552F040078E021 /* NNW3Entry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NNW3Entry.swift; sourceTree = ""; }; + 51865166235556240078E021 /* NNW3FeedsImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NNW3FeedsImporter.swift; sourceTree = ""; }; 519B8D322143397200FA689C /* SharingServiceDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharingServiceDelegate.swift; sourceTree = ""; }; 51C4524E226506F400C03939 /* UIStoryboard-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStoryboard-Extensions.swift"; sourceTree = ""; }; 51C45250226506F400C03939 /* String-Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String-Extensions.swift"; sourceTree = ""; }; @@ -1041,6 +1055,20 @@ name = Products; sourceTree = ""; }; + 516CAC53235529180038D354 /* NNW3 */ = { + isa = PBXGroup; + children = ( + 516CAC42235521070038D354 /* ImportNNW3Sheet.xib */, + 516CAC4B235521070038D354 /* ImportNNW3WindowController.swift */, + 51865166235556240078E021 /* NNW3FeedsImporter.swift */, + 5186515123552E610078E021 /* NNW3PlistConverter.swift */, + 5186515C23552F040078E021 /* NNW3Document.swift */, + 5186515D23552F040078E021 /* NNW3Entry.swift */, + 5186515B23552F040078E021 /* NNW3Feed.swift */, + ); + path = NNW3; + sourceTree = ""; + }; 5183CCDB226F1EEB0010922C /* Progress */ = { isa = PBXGroup; children = ( @@ -1256,6 +1284,7 @@ 849A97551ED9EAC3007D329B /* Add Feed */, 849A97411ED9EAA9007D329B /* Add Folder */, 5144EA39227A377700D19003 /* OPML */, + 516CAC53235529180038D354 /* NNW3 */, ); path = MainWindow; sourceTree = ""; @@ -1969,12 +1998,12 @@ ORGANIZATIONNAME = "Ranchero Software"; TargetAttributes = { 6581C73220CED60000F4AD34 = { - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = SHJK2V3AJG; + ProvisioningStyle = Automatic; }; 840D617B2029031C009BC708 = { CreatedOnToolsVersion = 9.3; - DevelopmentTeam = M8L2WTLA8W; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.BackgroundModes = { @@ -1990,8 +2019,8 @@ }; 849C645F1ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = M8L2WTLA8W; - ProvisioningStyle = Manual; + DevelopmentTeam = SHJK2V3AJG; + ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.HardenedRuntime = { enabled = 1; @@ -2000,7 +2029,7 @@ }; 849C64701ED37A5D003D8FC0 = { CreatedOnToolsVersion = 8.2.1; - DevelopmentTeam = 9C84TZ7Q6Z; + DevelopmentTeam = SHJK2V3AJG; ProvisioningStyle = Automatic; TestTargetID = 849C645F1ED37A5D003D8FC0; }; @@ -2255,6 +2284,7 @@ files = ( 844B5B651FEA11F200C7C76A /* GlobalKeyboardShortcuts.plist in Resources */, 5127B23A222B4849006D641D /* DetailKeyboardShortcuts.plist in Resources */, + 516CAC4C235521070038D354 /* ImportNNW3Sheet.xib in Resources */, 845479881FEB77C000AD8B59 /* TimelineKeyboardShortcuts.plist in Resources */, 848362FF2262A30E00DA1D35 /* template.html in Resources */, 848363082262A3DD00DA1D35 /* Main.storyboard in Resources */, @@ -2462,9 +2492,11 @@ D5A2678C20130ECF00A8D3C0 /* Author+Scriptability.swift in Sources */, 84F2D5371FC22FCC00998D64 /* PseudoFeed.swift in Sources */, 51EF0F902279C9500050506E /* AccountsAddViewController.swift in Sources */, + 5186515223552E610078E021 /* NNW3PlistConverter.swift in Sources */, D57BE6E0204CD35F00D11AAC /* NSScriptCommand+NetNewsWire.swift in Sources */, D553738B20186C20006D8857 /* Article+Scriptability.swift in Sources */, 845EE7C11FC2488C00854A1F /* SmartFeed.swift in Sources */, + 51865167235556240078E021 /* NNW3FeedsImporter.swift in Sources */, 84702AA41FA27AC0006B8943 /* MarkStatusCommand.swift in Sources */, D5907D7F2004AC00005947E5 /* NSApplication+Scriptability.swift in Sources */, 8405DD9C22153BD7008CE1BF /* NSView-Extensions.swift in Sources */, @@ -2503,6 +2535,7 @@ 5144EA51227B8E4500D19003 /* AccountsFeedbinWindowController.swift in Sources */, 84AD1EBC2032AF5C00BC20B7 /* SidebarOutlineDataSource.swift in Sources */, 845A29241FC9255E007B49E3 /* SidebarCellAppearance.swift in Sources */, + 5186515E23552F040078E021 /* NNW3Feed.swift in Sources */, 845EE7B11FC2366500854A1F /* StarredFeedDelegate.swift in Sources */, 848F6AE51FC29CFB002D422E /* FaviconDownloader.swift in Sources */, 84C9FC7722629E1200D921D6 /* AdvancedPreferencesViewController.swift in Sources */, @@ -2517,12 +2550,14 @@ 84DEE56522C32CA4005FC42C /* SmartFeedDelegate.swift in Sources */, 845213231FCA5B11003B6E93 /* ImageDownloader.swift in Sources */, 51EF0F922279CA620050506E /* AccountsAddTableCellView.swift in Sources */, + 5186515F23552F040078E021 /* NNW3Document.swift in Sources */, 849A97431ED9EAA9007D329B /* AddFolderWindowController.swift in Sources */, 8405DDA522168C62008CE1BF /* TimelineContainerViewController.swift in Sources */, 844B5B671FEA18E300C7C76A /* MainWIndowKeyboardHandler.swift in Sources */, 848D578E21543519005FFAD5 /* PasteboardFeed.swift in Sources */, 5144EA2F2279FAB600D19003 /* AccountsDetailViewController.swift in Sources */, 849A97801ED9EC42007D329B /* DetailViewController.swift in Sources */, + 5186516023552F040078E021 /* NNW3Entry.swift in Sources */, 84C9FC6722629B9000D921D6 /* AppDelegate.swift in Sources */, 84C9FC7A22629E1200D921D6 /* AccountsTableViewBackgroundView.swift in Sources */, 84CAFCAF22BC8C35007694F0 /* FetchRequestOperation.swift in Sources */, @@ -2544,6 +2579,7 @@ 5144EA40227A37EC00D19003 /* ImportOPMLWindowController.swift in Sources */, 849A976D1ED9EBC8007D329B /* TimelineTableView.swift in Sources */, 84D52E951FE588BB00D14F5B /* DetailStatusBarView.swift in Sources */, + 516CAC4D235521070038D354 /* ImportNNW3WindowController.swift in Sources */, D5E4CC64202C1AC1009B4FFC /* MainWindowController+Scriptability.swift in Sources */, 84C9FC7922629E1200D921D6 /* PreferencesWindowController.swift in Sources */, 84411E711FE5FBFA004B527F /* SmallIconProvider.swift in Sources */,