181 lines
5.3 KiB
Swift
181 lines
5.3 KiB
Swift
import Foundation
|
|
|
|
extension Content {
|
|
|
|
func generateFeed() -> Bool {
|
|
#warning("Implement feed generation")
|
|
return false
|
|
}
|
|
|
|
func generateAllPages() -> Bool {
|
|
guard startGenerating() else { return false }
|
|
defer { endGenerating() }
|
|
|
|
for page in pages {
|
|
for language in ContentLanguage.allCases {
|
|
guard generateInternal(page, in: language) else {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
let failedAssetCopies = results.values
|
|
.reduce(Set()) { $0.union($1.assets) }
|
|
.filter { !$0.isExternallyStored }
|
|
.filter { !storage.copy(file: $0.id, to: $0.assetUrl) }
|
|
|
|
let failedFileCopies = results.values
|
|
.reduce(Set()) { $0.union($1.files) }
|
|
.filter { !$0.isExternallyStored }
|
|
.filter { !storage.copy(file: $0.id, to: $0.absoluteUrl) }
|
|
|
|
|
|
guard imageGenerator.runJobs(callback: { _ in }) else {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
guard imageGenerator.runJobs(callback: { _ in }) else {
|
|
return false
|
|
}
|
|
|
|
let failedAssetCopies = results.values
|
|
.reduce(Set()) { $0.union($1.assets) }
|
|
.filter { !$0.isExternallyStored }
|
|
.filter { !storage.copy(file: $0.id, to: $0.assetUrl) }
|
|
|
|
let failedFileCopies = results.values
|
|
.reduce(Set()) { $0.union($1.files) }
|
|
.filter { !$0.isExternallyStored }
|
|
.filter { !storage.copy(file: $0.id, to: $0.absoluteUrl) }
|
|
|
|
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<HeaderElement> {
|
|
var result: Set<HeaderElement> = [.charset, .viewport]
|
|
if let defaultCss = settings.posts.defaultCssFile {
|
|
result.insert(.css(file: defaultCss, order: HeaderElement.defaultCssFileOrder))
|
|
}
|
|
return result
|
|
}
|
|
|
|
// MARK: Images
|
|
|
|
func recalculateGeneratedImages() {
|
|
let images = Set(self.images.map { $0.id })
|
|
imageGenerator.recalculateGeneratedImages(by: images)
|
|
}
|
|
|
|
// 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 pageGenerator = PageGenerator(
|
|
content: self,
|
|
imageGenerator: imageGenerator)
|
|
|
|
guard let (content, results) = pageGenerator.generate(page: page, language: language) else {
|
|
print("Failed to generate page \(page.id) in language \(language)")
|
|
return false
|
|
}
|
|
|
|
DispatchQueue.main.async {
|
|
let id = ItemId(itemId: page.id, language: language, itemType: .page)
|
|
self.results[id] = results
|
|
}
|
|
|
|
let path = page.absoluteUrl(in: language) + ".html"
|
|
guard storage.write(content, to: path) else {
|
|
print("Failed to save page \(page.id)")
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
prefix operator ~>
|
|
|
|
prefix func ~> (operation: () throws -> Void) -> Bool {
|
|
do {
|
|
try operation()
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|