From 0e3fea9b0d2871e7ab334988542f0db55a1c8ac6 Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sat, 30 Nov 2024 08:44:12 +0100 Subject: [PATCH] Refactor content --- CHDataManagement.xcodeproj/project.pbxproj | 4 + CHDataManagement/Model/Content+Generate.swift | 38 +++- CHDataManagement/Model/Content+Import.swift | 63 +++++++ CHDataManagement/Model/Content.swift | 175 +++--------------- 4 files changed, 132 insertions(+), 148 deletions(-) create mode 100644 CHDataManagement/Model/Content+Import.swift diff --git a/CHDataManagement.xcodeproj/project.pbxproj b/CHDataManagement.xcodeproj/project.pbxproj index b400eac..685cffa 100644 --- a/CHDataManagement.xcodeproj/project.pbxproj +++ b/CHDataManagement.xcodeproj/project.pbxproj @@ -24,6 +24,7 @@ E218502B2CF790B30090B18B /* PostContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502A2CF790AC0090B18B /* PostContentView.swift */; }; E218502D2CF791440090B18B /* PostImagesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502C2CF791440090B18B /* PostImagesView.swift */; }; E218502F2CFAF69C0090B18B /* Content+Generate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E218502E2CFAF6990090B18B /* Content+Generate.swift */; }; + E21850312CFAF8880090B18B /* Content+Import.swift in Sources */ = {isa = PBXBuildFile; fileRef = E21850302CFAF8840090B18B /* Content+Import.swift */; }; E24252012C50E0A40029FF16 /* HighlightedTextEditor in Frameworks */ = {isa = PBXBuildFile; productRef = E24252002C50E0A40029FF16 /* HighlightedTextEditor */; }; E24252032C5163CF0029FF16 /* Importer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24252022C5163CF0029FF16 /* Importer.swift */; }; E24252062C51684E0029FF16 /* GenericMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24252052C51684E0029FF16 /* GenericMetadata.swift */; }; @@ -101,6 +102,7 @@ E218502A2CF790AC0090B18B /* PostContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostContentView.swift; sourceTree = ""; }; E218502C2CF791440090B18B /* PostImagesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostImagesView.swift; sourceTree = ""; }; E218502E2CFAF6990090B18B /* Content+Generate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Generate.swift"; sourceTree = ""; }; + E21850302CFAF8840090B18B /* Content+Import.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Content+Import.swift"; sourceTree = ""; }; E24252022C5163CF0029FF16 /* Importer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Importer.swift; sourceTree = ""; }; E24252052C51684E0029FF16 /* GenericMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GenericMetadata.swift; sourceTree = ""; }; E24252072C5168750029FF16 /* GenericMetadata+Localized.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GenericMetadata+Localized.swift"; sourceTree = ""; }; @@ -258,6 +260,7 @@ children = ( E2E06DFA2CA4A6570019C2AF /* Content.swift */, E218502E2CFAF6990090B18B /* Content+Generate.swift */, + E21850302CFAF8840090B18B /* Content+Import.swift */, E24252092C52C9260029FF16 /* ContentLanguage.swift */, E2A21C502CBBD53C0060935B /* FileResource.swift */, E2A21C3A2CB9D9A50060935B /* ImageResource.swift */, @@ -473,6 +476,7 @@ E2A21C562CBBF9880060935B /* FlexibleColumnView.swift in Sources */, E2A37D192CEA36A90000979F /* LocalizedTag.swift in Sources */, E24252062C51684E0029FF16 /* GenericMetadata.swift in Sources */, + E21850312CFAF8880090B18B /* Content+Import.swift in Sources */, E2A21C462CBAE2E60060935B /* FeedEntryContent.swift in Sources */, E21850252CF38BCE0090B18B /* TextEntrySheet.swift in Sources */, E2A37D1D2CEA922D0000979F /* LocalizedPost.swift in Sources */, diff --git a/CHDataManagement/Model/Content+Generate.swift b/CHDataManagement/Model/Content+Generate.swift index 8e0016d..b3c8d69 100644 --- a/CHDataManagement/Model/Content+Generate.swift +++ b/CHDataManagement/Model/Content+Generate.swift @@ -2,7 +2,41 @@ import Foundation extension Content { - func generateWebsite(into folder: URL) throws { - + func generateFeed(for language: ContentLanguage, bookmarkKey: String) { + let posts = posts.map { $0.feedEntry(for: language) } + DispatchQueue.global(qos: .userInitiated).async { + + let navigationItems: [FeedNavigationLink] = [ + .init(text: .init(en: "Projects", de: "Projekte"), + url: .init(en: "/projects", de: "/projekte")), + .init(text: .init(en: "Adventures", de: "Abenteuer"), + url: .init(en: "/adventures", de: "/abenteuer")), + .init(text: .init(en: "Services", de: "Dienste"), + url: .init(en: "/services", de: "/dienste")), + .init(text: .init(en: "Tags", de: "Kategorien"), + url: .init(en: "/tags", de: "/kategorien")), + ] + + let feed = Feed( + language: language, + title: .init(en: "Blog | CH", de: "Blog | CH"), + description: .init(en: "The latests posts, projects and adventures", + de: "Die neusten Beiträge, Projekte und Abenteuer"), + iconDescription: .init(en: "An icon consisting of the letters C and H in blue and orange", + de: "Ein Logo aus den Buchstaben C und H in Blau und Orange"), + navigationItems: navigationItems, + posts: posts) + let fileContent = feed.content + Content.accessFolderFromBookmark(key: bookmarkKey) { folder in + let outputFile = folder.appendingPathComponent("feed.html", isDirectory: false) + do { + try fileContent + .data(using: .utf8)! + .write(to: outputFile) + } catch { + print("Failed to save: \(error)") + } + } + } } } diff --git a/CHDataManagement/Model/Content+Import.swift b/CHDataManagement/Model/Content+Import.swift new file mode 100644 index 0000000..315d388 --- /dev/null +++ b/CHDataManagement/Model/Content+Import.swift @@ -0,0 +1,63 @@ +import Foundation + +extension Content { + + func importOldContent() { + + let importer = Importer() + do { + try importer.importContent() + } catch { + print(error) + return + } + for (_, file) in importer.files.sorted(by: { $0.key < $1.key }) { + storage.copyFile(at: file.url, fileId: file.name) + // TODO: Store alt text for image and videos + } + var missingPages: [String] = [] + for (pageId, page) in importer.pages.sorted(by: { $0.key < $1.key }) { + storage.save(pageMetadata: page.page, for: pageId) + + if FileManager.default.fileExists(atPath: page.deContentUrl.path()) { + storage.copyPageContent(from: page.deContentUrl, for: pageId, language: .german) + } else { + missingPages.append(pageId + " (DE)") + } + + if FileManager.default.fileExists(atPath: page.enContentUrl.path()) { + storage.copyPageContent(from: page.enContentUrl, for: pageId, language: .english) + } else { + missingPages.append(pageId + " (EN)") + } + } + + for (tagId, tag) in importer.tags { + storage.save(tagMetadata: tag, for: tagId) + } + + for (postId, post) in importer.posts { + storage.save(post: post, for: postId) + } + + let ignoredFiles = importer.ignoredFiles + .map { $0.path() } + .sorted() + + print("Ignored files:") + for file in ignoredFiles { + print(file) + } + + print("Missing pages:") + for page in missingPages { + print(page) + } + + do { + try loadFromDisk() + } catch { + print("Failed to load from disk: \(error)") + } + } +} diff --git a/CHDataManagement/Model/Content.swift b/CHDataManagement/Model/Content.swift index d7a58eb..a8cb471 100644 --- a/CHDataManagement/Model/Content.swift +++ b/CHDataManagement/Model/Content.swift @@ -79,103 +79,6 @@ final class Content: ObservableObject { .store(in: &cancellables) } - func generateFeed(for language: ContentLanguage, bookmarkKey: String) { - let posts = posts.map { $0.feedEntry(for: language) } - DispatchQueue.global(qos: .userInitiated).async { - - let navigationItems: [FeedNavigationLink] = [ - .init(text: .init(en: "Projects", de: "Projekte"), - url: .init(en: "/projects", de: "/projekte")), - .init(text: .init(en: "Adventures", de: "Abenteuer"), - url: .init(en: "/adventures", de: "/abenteuer")), - .init(text: .init(en: "Services", de: "Dienste"), - url: .init(en: "/services", de: "/dienste")), - .init(text: .init(en: "Tags", de: "Kategorien"), - url: .init(en: "/tags", de: "/kategorien")), - ] - - let feed = Feed( - language: language, - title: .init(en: "Blog | CH", de: "Blog | CH"), - description: .init(en: "The latests posts, projects and adventures", - de: "Die neusten Beiträge, Projekte und Abenteuer"), - iconDescription: .init(en: "An icon consisting of the letters C and H in blue and orange", - de: "Ein Logo aus den Buchstaben C und H in Blau und Orange"), - navigationItems: navigationItems, - posts: posts) - let fileContent = feed.content - Content.accessFolderFromBookmark(key: bookmarkKey) { folder in - let outputFile = folder.appendingPathComponent("feed.html", isDirectory: false) - do { - try fileContent - .data(using: .utf8)! - .write(to: outputFile) - } catch { - print("Failed to save: \(error)") - } - } - } - } - - func importOldContent() { - - let importer = Importer() - do { - try importer.importContent() - } catch { - print(error) - return - } - for (_, file) in importer.files.sorted(by: { $0.key < $1.key }) { - storage.copyFile(at: file.url, fileId: file.name) - // TODO: Store alt text for image and videos - } - var missingPages: [String] = [] - for (pageId, page) in importer.pages.sorted(by: { $0.key < $1.key }) { - storage.save(pageMetadata: page.page, for: pageId) - - if FileManager.default.fileExists(atPath: page.deContentUrl.path()) { - storage.copyPageContent(from: page.deContentUrl, for: pageId, language: .german) - } else { - missingPages.append(pageId + " (DE)") - } - - if FileManager.default.fileExists(atPath: page.enContentUrl.path()) { - storage.copyPageContent(from: page.enContentUrl, for: pageId, language: .english) - } else { - missingPages.append(pageId + " (EN)") - } - } - - for (tagId, tag) in importer.tags { - storage.save(tagMetadata: tag, for: tagId) - } - - for (postId, post) in importer.posts { - storage.save(post: post, for: postId) - } - - let ignoredFiles = importer.ignoredFiles - .map { $0.path() } - .sorted() - - print("Ignored files:") - for file in ignoredFiles { - print(file) - } - - print("Missing pages:") - for page in missingPages { - print(page) - } - - do { - try loadFromDisk() - } catch { - print("Failed to load from disk: \(error)") - } - } - private func convert(_ tag: LocalizedTagFile) -> LocalizedTag { LocalizedTag( urlComponent: tag.urlComponent, @@ -186,6 +89,31 @@ final class Content: ObservableObject { originalUrl: tag.originalURL) } + private func convert(_ post: LocalizedPostFile, images: [String : ImageResource]) -> LocalizedPost { + LocalizedPost( + title: post.title, + content: post.content, + lastModified: post.lastModifiedDate, + images: post.images.compactMap { images[$0] }, + linkPreviewImage: post.linkPreviewImage.map { images[$0] }, + linkPreviewTitle: post.linkPreviewTitle, + linkPreviewDescription: post.linkPreviewDescription) + } + + private func convert(_ page: LocalizedPageFile) -> LocalizedPage { + LocalizedPage( + urlString: page.url, + title: page.title, + lastModified: page.lastModifiedDate, + originalUrl: page.originalURL, + files: Set(page.files), + externalFiles: Set(page.externalFiles), + requiredFiles: Set(page.requiredFiles), + linkPreviewImage: page.linkPreviewImage, + linkPreviewTitle: page.linkPreviewTitle, + linkPreviewDescription: page.linkPreviewDescription) + } + func loadFromDisk() throws { let storage = Storage(baseFolder: URL(filePath: contentPath)) @@ -220,27 +148,8 @@ final class Content: ObservableObject { let posts = postsData.map { postId, post in let linkedPage = post.linkedPageId.map { pages[$0] } - - - let germanData = post.german - let german = LocalizedPost( - title: germanData.title, - content: germanData.content, - lastModified: germanData.lastModifiedDate, - images: germanData.images.compactMap { images[$0] }, - linkPreviewImage: germanData.linkPreviewImage.map { images[$0] }, - linkPreviewTitle: germanData.linkPreviewTitle, - linkPreviewDescription: germanData.linkPreviewDescription) - - let englishData = post.english - let english = LocalizedPost( - title: englishData.title, - content: englishData.content, - lastModified: englishData.lastModifiedDate, - images: englishData.images.compactMap { images[$0] }, - linkPreviewImage: englishData.linkPreviewImage.map { images[$0] }, - linkPreviewTitle: englishData.linkPreviewTitle, - linkPreviewDescription: englishData.linkPreviewDescription) + let german = convert(post.german, images: images) + let english = convert(post.english, images: images) return Post( id: postId, @@ -264,40 +173,14 @@ final class Content: ObservableObject { private func loadPages(_ pagesData: [String : PageFile], tags: [String : Tag]) -> [String : Page] { pagesData.reduce(into: [:]) { pages, data in let (pageId, page) = data - let german = page.german - let germanPage = LocalizedPage( - urlString: german.url, - title: german.title, - lastModified: german.lastModifiedDate, - originalUrl: german.originalURL, - files: Set(german.files), - externalFiles: Set(german.externalFiles), - requiredFiles: Set(german.requiredFiles), - linkPreviewImage: german.linkPreviewImage, - linkPreviewTitle: german.linkPreviewTitle, - linkPreviewDescription: german.linkPreviewDescription) - - let english = page.english - let englishPage = LocalizedPage( - urlString: english.url, - title: english.title, - lastModified: english.lastModifiedDate, - originalUrl: english.originalURL, - files: Set(english.files), - externalFiles: Set(english.externalFiles), - requiredFiles: Set(english.requiredFiles), - linkPreviewImage: english.linkPreviewImage, - linkPreviewTitle: english.linkPreviewTitle, - linkPreviewDescription: english.linkPreviewDescription) - pages[pageId] = Page( id: pageId, isDraft: page.isDraft, createdDate: page.createdDate, startDate: page.startDate, endDate: page.endDate, - german: germanPage, - english: englishPage, + german: convert(page.german), + english: convert(page.english), tags: page.tags.map { tags[$0]! }) } }