import Foundation final class PageGenerator { private let content: Content init(content: Content) { self.content = content } private func makeHeaders(requiredItems: Set, results: PageGenerationResults) -> Set { var result = content.contentPageHeaders for item in requiredItems { guard let header = item.header(content: content) else { results.warning("Header \(item) not configured in settings") continue } result.insert(header) } return result } private func makeEmptyPageContent(in language: ContentLanguage) -> String { let settings = content.settings.pages.localized(in: language) return ContentBox( title: settings.emptyPageTitle, text: settings.emptyPageText).content } func generate(page: Page, language: ContentLanguage, results: PageGenerationResults, pageUrl: String) -> String? { let contentGenerator = PageContentParser( content: content, language: language, results: results) let rawPageContent: String if let existing = content.storage.pageContent(for: page.id, language: language) { rawPageContent = existing } else { rawPageContent = makeEmptyPageContent(in: language) // Only mark non-draft pages as empty if !page.isDraft { results.markPageAsEmpty() } } let pageContent = contentGenerator.generatePage(from: rawPageContent) let localized = page.localized(in: language) let tags: [FeedEntryData.Tag] = page.tags.map { tag in .init(name: tag.localized(in: language).name, url: tag.absoluteUrl(in: language)) } let requiredHeaders = DispatchQueue.main.sync { results.requiredHeaders } let headers = makeHeaders(requiredItems: requiredHeaders, results: results) results.require(files: headers.compactMap { $0.requiredFile }) let iconUrl = content.settings.navigation.localized(in: language).rootUrl let languageUrl = page.absoluteUrl(in: language.next) let languageButton = NavigationBar.Link( text: language.next.rawValue, url: languageUrl) let imageUrl = localized.linkPreview.image?.linkPreviewImage(results: results) let icons = DispatchQueue.main.sync { results.requiredIcons } let pageHeader = PageHeader( language: language, title: localized.linkPreview.title ?? localized.title, description: localized.linkPreview.description, image: imageUrl, pageUrl: pageUrl, iconUrl: iconUrl, languageButton: languageButton, links: content.navigationBar(in: language), headers: headers, icons: icons) let footers = DispatchQueue.main.sync { results.requiredFooters } let fullPage = GenericPage( header: pageHeader, additionalFooter: footers.sorted().joined()) { content in content += "
" if !localized.hideTitle { if !page.hideDate { content += "

\(page.dateText(in: language))

" } content += "

\(localized.title)

" content += TagList(tags: tags).content } content += pageContent content += "
" } return fullPage.content } }