Generate first tag pages
This commit is contained in:
@ -33,7 +33,12 @@ final class ImageGenerator {
|
||||
init(storage: Storage, relativeImageOutputPath: String) {
|
||||
self.storage = storage
|
||||
self.relativeImageOutputPath = relativeImageOutputPath
|
||||
self.generatedImages = storage.loadListOfGeneratedImages()
|
||||
do {
|
||||
self.generatedImages = try storage.loadListOfGeneratedImages()
|
||||
} catch {
|
||||
print("Failed to load list of previously generated images: \(error)")
|
||||
self.generatedImages = [:]
|
||||
}
|
||||
}
|
||||
|
||||
func prepareForGeneration() -> Bool {
|
||||
@ -60,7 +65,13 @@ final class ImageGenerator {
|
||||
}
|
||||
|
||||
func save() -> Bool {
|
||||
storage.save(listOfGeneratedImages: generatedImages)
|
||||
do {
|
||||
try storage.save(listOfGeneratedImages: generatedImages)
|
||||
return true
|
||||
} catch {
|
||||
print("Failed to save list of generated images: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func versionFileName(image: String, type: ImageFileType, width: CGFloat, height: CGFloat) -> String {
|
||||
|
@ -14,7 +14,7 @@ final class PageGenerator {
|
||||
self.navigationBarData = navigationBarData
|
||||
}
|
||||
|
||||
func generate(page: Page, language: ContentLanguage) -> String {
|
||||
func generate(page: Page, language: ContentLanguage) throws -> String {
|
||||
let contentGenerator = PageContentParser(
|
||||
page: page,
|
||||
content: content,
|
||||
@ -22,22 +22,26 @@ final class PageGenerator {
|
||||
results: results,
|
||||
imageGenerator: imageGenerator)
|
||||
|
||||
let rawPageContent = content.storage.pageContent(for: page.id, language: language)
|
||||
let rawPageContent = try content.storage.pageContent(for: page.id, language: language)
|
||||
|
||||
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: content.tagLink(tag, language: language))
|
||||
}
|
||||
|
||||
return ContentPage(
|
||||
language: language,
|
||||
dateString: page.dateText(in: language),
|
||||
title: localized.title,
|
||||
tags: page.tags.map { $0.data(in: language) },
|
||||
tags: tags,
|
||||
linkTitle: localized.linkPreviewTitle ?? localized.title,
|
||||
description: localized.linkPreviewDescription ?? "",
|
||||
navigationBarData: navigationBarData,
|
||||
pageContent: pageContent)
|
||||
.content
|
||||
}
|
||||
|
||||
}
|
||||
|
118
CHDataManagement/Generator/PostListPageGenerator.swift
Normal file
118
CHDataManagement/Generator/PostListPageGenerator.swift
Normal file
@ -0,0 +1,118 @@
|
||||
import Foundation
|
||||
|
||||
final class PostListPageGenerator {
|
||||
|
||||
private let language: ContentLanguage
|
||||
|
||||
private let content: Content
|
||||
|
||||
private let imageGenerator: ImageGenerator
|
||||
|
||||
private let navigationBarData: NavigationBarData
|
||||
|
||||
private let showTitle: Bool
|
||||
|
||||
private let pageTitle: String
|
||||
|
||||
private let pageDescription: String
|
||||
|
||||
/// The url of the page, excluding the extension
|
||||
private let pageUrlPrefix: String
|
||||
|
||||
init(language: ContentLanguage, content: Content, imageGenerator: ImageGenerator, navigationBarData: NavigationBarData, showTitle: Bool, pageTitle: String, pageDescription: String, pageUrlPrefix: String) {
|
||||
self.language = language
|
||||
self.content = content
|
||||
self.imageGenerator = imageGenerator
|
||||
self.navigationBarData = navigationBarData
|
||||
self.showTitle = showTitle
|
||||
self.pageTitle = pageTitle
|
||||
self.pageDescription = pageDescription
|
||||
self.pageUrlPrefix = pageUrlPrefix
|
||||
}
|
||||
|
||||
private var mainContentMaximumWidth: CGFloat {
|
||||
CGFloat(content.settings.posts.contentWidth)
|
||||
}
|
||||
|
||||
private var postsPerPage: Int {
|
||||
content.settings.posts.postsPerPage
|
||||
}
|
||||
|
||||
func createPages(for posts: [Post]) -> Bool {
|
||||
let totalCount = posts.count
|
||||
guard totalCount > 0 else {
|
||||
return true
|
||||
}
|
||||
|
||||
let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
|
||||
for pageIndex in 1...numberOfPages {
|
||||
let startIndex = (pageIndex - 1) * postsPerPage
|
||||
let endIndex = min(pageIndex * postsPerPage, totalCount)
|
||||
let postsOnPage = posts[startIndex..<endIndex]
|
||||
guard createPostFeedPage(pageIndex, pageCount: numberOfPages, posts: postsOnPage, bar: navigationBarData) else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>, bar: NavigationBarData) -> Bool {
|
||||
let posts: [FeedEntryData] = posts.map { post in
|
||||
let localized: LocalizedPost = post.localized(in: language)
|
||||
|
||||
let linkUrl = post.linkedPage.map {
|
||||
FeedEntryData.Link(
|
||||
url: content.pageLink($0, language: language),
|
||||
text: language == .english ? "View" : "Anzeigen") // TODO: Add to settings
|
||||
}
|
||||
|
||||
let tags: [FeedEntryData.Tag] = post.tags.map { tag in
|
||||
.init(name: tag.localized(in: language).name,
|
||||
url: content.tagLink(tag, language: language))
|
||||
}
|
||||
|
||||
return FeedEntryData(
|
||||
entryId: "\(post.id)",
|
||||
title: localized.title,
|
||||
textAboveTitle: post.dateText(in: language),
|
||||
link: linkUrl,
|
||||
tags: tags,
|
||||
text: [localized.content], // TODO: Convert from markdown to html
|
||||
images: localized.images.map(createImageSet))
|
||||
}
|
||||
|
||||
let feed = PageInFeed(
|
||||
language: language,
|
||||
title: pageTitle,
|
||||
showTitle: showTitle,
|
||||
description: pageDescription,
|
||||
navigationBarData: bar,
|
||||
pageNumber: pageIndex,
|
||||
totalPages: pageCount,
|
||||
posts: posts)
|
||||
let fileContent = feed.content
|
||||
if pageIndex == 1 {
|
||||
return save(fileContent, to: "\(pageUrlPrefix).html")
|
||||
} else {
|
||||
return save(fileContent, to: "\(pageUrlPrefix)-\(pageIndex).html")
|
||||
}
|
||||
}
|
||||
|
||||
private func createImageSet(for image: FileResource) -> FeedEntryData.Image {
|
||||
imageGenerator.generateImageSet(
|
||||
for: image.id,
|
||||
maxWidth: mainContentMaximumWidth,
|
||||
maxHeight: mainContentMaximumWidth,
|
||||
altText: image.getDescription(for: language))
|
||||
}
|
||||
|
||||
private func save(_ content: String, to relativePath: String) -> Bool {
|
||||
do {
|
||||
try self.content.storage.write(content: content, to: relativePath)
|
||||
return true
|
||||
} catch {
|
||||
print("Failed to write page \(relativePath)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
@ -14,18 +14,6 @@ final class WebsiteGenerator {
|
||||
content.settings.posts.postsPerPage
|
||||
}
|
||||
|
||||
private var postFeedTitle: String {
|
||||
localizedSettings.posts.title
|
||||
}
|
||||
|
||||
private var postFeedDescription: String {
|
||||
localizedSettings.posts.description
|
||||
}
|
||||
|
||||
private var postFeedUrlPrefix: String {
|
||||
localizedSettings.posts.feedUrlPrefix
|
||||
}
|
||||
|
||||
private var navigationIconPath: String {
|
||||
content.settings.navigationBar.iconPath
|
||||
}
|
||||
@ -57,7 +45,10 @@ final class WebsiteGenerator {
|
||||
guard imageGenerator.prepareForGeneration() else {
|
||||
return false
|
||||
}
|
||||
guard createPostFeedPages() else {
|
||||
guard createMainPostFeedPages() else {
|
||||
return false
|
||||
}
|
||||
guard generateTagPages() else {
|
||||
return false
|
||||
}
|
||||
guard imageGenerator.runJobs(callback: callback) else {
|
||||
@ -66,18 +57,37 @@ final class WebsiteGenerator {
|
||||
return imageGenerator.save()
|
||||
}
|
||||
|
||||
private func createPostFeedPages() -> Bool {
|
||||
let totalCount = content.posts.count
|
||||
guard totalCount > 0 else {
|
||||
return true
|
||||
}
|
||||
private func createMainPostFeedPages() -> Bool {
|
||||
let generator = PostListPageGenerator(
|
||||
language: language,
|
||||
content: content,
|
||||
imageGenerator: imageGenerator,
|
||||
navigationBarData: navigationBarData,
|
||||
showTitle: false,
|
||||
pageTitle: localizedSettings.posts.title,
|
||||
pageDescription: localizedSettings.posts.description,
|
||||
pageUrlPrefix: localizedSettings.posts.feedUrlPrefix)
|
||||
return generator.createPages(for: content.posts)
|
||||
}
|
||||
|
||||
let numberOfPages = (totalCount + postsPerPage - 1) / postsPerPage // Round up
|
||||
for pageIndex in 1...numberOfPages {
|
||||
let startIndex = (pageIndex - 1) * postsPerPage
|
||||
let endIndex = min(pageIndex * postsPerPage, totalCount)
|
||||
let postsOnPage = content.posts[startIndex..<endIndex]
|
||||
guard createPostFeedPage(pageIndex, pageCount: numberOfPages, posts: postsOnPage, bar: navigationBarData) else {
|
||||
private func generateTagPages() -> Bool {
|
||||
for tag in content.tags {
|
||||
let posts = content.posts.filter { $0.tags.contains(tag) }
|
||||
guard posts.count > 0 else { continue }
|
||||
|
||||
let localized = tag.localized(in: language)
|
||||
|
||||
#warning("Get tag url prefix from settings")
|
||||
let generator = PostListPageGenerator(
|
||||
language: language,
|
||||
content: content,
|
||||
imageGenerator: imageGenerator,
|
||||
navigationBarData: navigationBarData,
|
||||
showTitle: true,
|
||||
pageTitle: localized.name,
|
||||
pageDescription: localized.description ?? "",
|
||||
pageUrlPrefix: "tags/\(localized.urlComponent)")
|
||||
guard generator.createPages(for: posts) else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -95,50 +105,6 @@ final class WebsiteGenerator {
|
||||
navigationItems: navigationItems)
|
||||
}
|
||||
|
||||
private func createImageSet(for image: FileResource) -> FeedEntryData.Image {
|
||||
imageGenerator.generateImageSet(
|
||||
for: image.id,
|
||||
maxWidth: mainContentMaximumWidth,
|
||||
maxHeight: mainContentMaximumWidth,
|
||||
altText: image.getDescription(for: language))
|
||||
}
|
||||
|
||||
private func createPostFeedPage(_ pageIndex: Int, pageCount: Int, posts: ArraySlice<Post>, bar: NavigationBarData) -> Bool {
|
||||
let posts: [FeedEntryData] = posts.map { post in
|
||||
let localized: LocalizedPost = post.localized(in: language)
|
||||
|
||||
let linkUrl = post.linkedPage.map {
|
||||
FeedEntryData.Link(
|
||||
url: content.pageLink($0, language: language),
|
||||
text: language == .english ? "View" : "Anzeigen") // TODO: Add to settings
|
||||
}
|
||||
|
||||
return FeedEntryData(
|
||||
entryId: "\(post.id)",
|
||||
title: localized.title,
|
||||
textAboveTitle: post.dateText(in: language),
|
||||
link: linkUrl,
|
||||
tags: post.tags.map { $0.data(in: language) },
|
||||
text: [localized.content], // TODO: Convert from markdown to html
|
||||
images: localized.images.map(createImageSet))
|
||||
}
|
||||
|
||||
let feed = PageInFeed(
|
||||
language: language,
|
||||
title: postFeedTitle,
|
||||
description: postFeedDescription,
|
||||
navigationBarData: bar,
|
||||
pageNumber: pageIndex,
|
||||
totalPages: pageCount,
|
||||
posts: posts)
|
||||
let fileContent = feed.content
|
||||
if pageIndex == 1 {
|
||||
return save(fileContent, to: "\(postFeedUrlPrefix).html")
|
||||
} else {
|
||||
return save(fileContent, to: "\(postFeedUrlPrefix)-\(pageIndex).html")
|
||||
}
|
||||
}
|
||||
|
||||
private func generatePagesFolderIfNeeded() -> Bool {
|
||||
let relativePath = content.settings.pages.pageUrlPrefix
|
||||
|
||||
@ -159,7 +125,14 @@ final class WebsiteGenerator {
|
||||
return false
|
||||
}
|
||||
let pageGenerator = PageGenerator(content: content, imageGenerator: imageGenerator, navigationBarData: navigationBarData)
|
||||
let content = pageGenerator.generate(page: page, language: language)
|
||||
|
||||
let content: String
|
||||
do {
|
||||
content = try pageGenerator.generate(page: page, language: language)
|
||||
} catch {
|
||||
print("Failed to generate page \(page.id) in language \(language): \(error)")
|
||||
return false
|
||||
}
|
||||
|
||||
let path = self.content.pageLink(page, language: language) + ".html"
|
||||
guard save(content, to: path) else {
|
||||
@ -181,8 +154,10 @@ final class WebsiteGenerator {
|
||||
guard let outputPath = content.pathToFile(fileId) else {
|
||||
return false
|
||||
}
|
||||
guard content.storage.copy(file: fileId, to: outputPath) else {
|
||||
print("Failed to copy video file to output folder")
|
||||
do {
|
||||
try content.storage.copy(file: fileId, to: outputPath)
|
||||
} catch {
|
||||
print("Failed to copy video file: \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -190,23 +165,12 @@ final class WebsiteGenerator {
|
||||
}
|
||||
|
||||
private func save(_ content: String, to relativePath: String) -> Bool {
|
||||
guard let data = content.data(using: .utf8) else {
|
||||
print("Failed to create data for \(relativePath)")
|
||||
do {
|
||||
try self.content.storage.write(content: content, to: relativePath)
|
||||
return true
|
||||
} catch {
|
||||
print("Failed to write page \(relativePath)")
|
||||
return false
|
||||
}
|
||||
return save(data, to: relativePath)
|
||||
}
|
||||
|
||||
private func save(_ data: Data, to relativePath: String) -> Bool {
|
||||
self.content.storage.write(in: .outputPath) { folder in
|
||||
let outputFile = folder.appendingPathComponent(relativePath, isDirectory: false)
|
||||
do {
|
||||
try data.write(to: outputFile)
|
||||
return true
|
||||
} catch {
|
||||
print("Failed to save \(outputFile.path()): \(error)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user