import Foundation extension Content { func generatePage(_ page: Page) -> Bool { guard startGenerating() else { return false } defer { endGenerating() } for language in ContentLanguage.allCases { guard generateInternal(page, in: language) else { return false } } return true } func generatePage(_ page: Page, in language: ContentLanguage) -> Bool { guard startGenerating() else { return false } defer { endGenerating() } return generateInternal(page, in: language) } // MARK: Paths to items private func makeCleanAbsolutePath(_ path: String) -> String { ("/" + path).replacingOccurrences(of: "//", with: "/") } func absoluteUrlPrefixForTag(_ tag: Tag, language: ContentLanguage) -> String { makeCleanAbsolutePath(settings.paths.tagsOutputFolderPath + "/" + tag.localized(in: language).urlComponent) } func absoluteUrlToTag(_ tag: Tag, language: ContentLanguage) -> String { absoluteUrlPrefixForTag(tag, language: language) + ".html" } // MARK: Find items by id func page(_ pageId: String) -> Page? { pages.first { $0.id == pageId } } func image(_ imageId: String) -> FileResource? { files.first { $0.id == imageId && $0.type.isImage } } func video(_ videoId: String) -> FileResource? { files.first { $0.id == videoId && $0.type.isVideo } } func file(_ fileId: String) -> FileResource? { files.first { $0.id == fileId } } func tag(_ tagId: String) -> Tag? { tags.first { $0.id == tagId } } // MARK: Generation input func navigationBar(in language: ContentLanguage) -> [NavigationBar.Link] { settings.navigationItems.map { .init(text: $0.title(in: language), url: $0.absoluteUrl(in: language)) } } var defaultPageHeaders: Set { var result: Set = [.charset, .viewport] if let defaultCss = settings.posts.defaultCssFile { result.insert(.css(file: defaultCss, order: HeaderElement.defaultCssFileOrder)) } return result } // MARK: Generation private func startGenerating() -> Bool { guard !isGeneratingWebsite else { return false } // TODO: Fix bug where multiple generating operations can be started // due to dispatch of locking property on main queue self.set(isGenerating: true) return true } private func endGenerating() { set(isGenerating: false) } private func generateInternal(_ page: Page, in language: ContentLanguage) -> Bool { let pagesFolder = settings.paths.pagesOutputFolderPath guard storage.create(folder: pagesFolder, in: .outputPath) else { print("Failed to generate output folder") return false } let imageGenerator = ImageGenerator( storage: storage, settings: settings) let pageGenerator = PageGenerator( content: self, imageGenerator: imageGenerator) let content: String let results: PageGenerationResults do { (content, results) = try pageGenerator.generate(page: page, language: language) } catch { print("Failed to generate page \(page.id) in language \(language): \(error)") return false } guard !content.trimmed.isEmpty else { #warning("Generate page with placeholder content") return true } let path = page.absoluteUrl(in: language) + ".html" do { try storage.write(content: content, to: path) } catch { print("Failed to save page \(page.id): \(error)") return false } guard imageGenerator.runJobs(callback: { _ in }) else { return false } guard copy(requiredFiles: results.files) else { return false } return true return true } private func copy(requiredFiles: Set) -> Bool { //print("Copying \(requiredVideoFiles.count) files...") for file in requiredFiles { guard !file.isExternallyStored else { continue } do { try storage.copy(file: file.id, to: file.absoluteUrl) } catch { print("Failed to copy file \(file.id): \(error)") return false } } return true } }