2025-02-24 19:12:15 +01:00

239 lines
6.0 KiB
Swift

import Foundation
final class Page: Item, DateItem, LocalizedItem {
override var itemType: ItemType { .page }
/**
The external link this page points to.
If this value is not `nil`, then the page has no content
and many other features are disabled.
*/
@Published
var externalLink: String?
@Published
var isDraft: Bool
@Published
var createdDate: Date
@Published
var hideDate: Bool
@Published
var startDate: Date
@Published
var hasEndDate: Bool
@Published
var potentialEndDate: Date
@Published
var german: LocalizedPage
@Published
var english: LocalizedPage
@Published
var tags: [Tag]
var savedData: Data?
init(content: Content,
id: String,
externalLink: String?,
isDraft: Bool,
createdDate: Date,
hideDate: Bool,
startDate: Date,
endDate: Date?,
german: LocalizedPage,
english: LocalizedPage,
tags: [Tag]) {
self.externalLink = externalLink
self.isDraft = isDraft
self.createdDate = createdDate
self.hideDate = hideDate
self.startDate = startDate
self.hasEndDate = endDate != nil
self.potentialEndDate = endDate ?? startDate
self.german = german
self.english = english
self.tags = tags
super.init(content: content, id: id)
}
func isValid(id: String) -> Bool {
!id.isEmpty &&
content.isValidIdForTagOrPageOrPost(id) &&
content.isNewIdForPage(id)
}
@discardableResult
func update(id newId: String) -> Bool {
guard content.storage.move(page: id, to: newId) else {
print("Failed to move files of page \(id)")
return false
}
id = newId
return true
}
var isExternalUrl: Bool {
externalLink != nil
}
// MARK: Tags
/**
Check if a tag is associated with this page
*/
func contains(_ tag: Tag) -> Bool {
tags.contains(tag)
}
func toggle(_ tag: Tag) {
guard let index = tags.firstIndex(of: tag) else {
tags.append(tag)
return
}
tags.remove(at: index)
}
func remove(_ tag: Tag) {
tags.remove(tag)
}
func associate(_ tag: Tag) {
guard !tags.contains(tag) else {
return
}
tags.append(tag)
}
// MARK: Paths
override func absoluteUrl(in language: ContentLanguage) -> String {
if let url = externalLink {
return url
}
// TODO: Record link to trace connections between pages
return makeCleanAbsolutePath(internalPath(for: language))
}
override func title(in language: ContentLanguage) -> String {
localized(in: language).title
}
func filePathRelativeToOutputFolder(for language: ContentLanguage) -> String {
makeCleanRelativePath(internalPath(for: language))
}
private func internalPath(for language: ContentLanguage) -> String {
content.settings.paths.pagesOutputFolderPath + "/" + localized(in: language).urlString
}
override var itemReference: ItemReference {
.page(self)
}
func contains(urlComponent: String) -> Bool {
english.urlString == urlComponent || german.urlString == urlComponent
}
func pageContent(in language: ContentLanguage) -> String? {
content.storage.pageContent(for: id, language: language)
}
func removeContent(in language: ContentLanguage) -> Bool {
guard content.storage.remove(pageContent: id, in: language) else {
return false
}
localized(in: language).hasContent = false
return true
}
func save(pageContent: String, in language: ContentLanguage) -> Bool {
guard content.storage.save(pageContent: pageContent, for: id, in: language) else {
return false
}
localized(in: language).hasContent = true
return true
}
/**
Update the `hasContent` property of all localized pages.
*/
func updateContentExistence() {
for language in ContentLanguage.allCases {
localized(in: language).hasContent = content.storage.hasPageContent(for: id, language: language)
}
}
/// All languages for which the page has no content
var missingContentLanguages: [ContentLanguage] {
ContentLanguage.allCases.filter { !localized(in: $0).hasContent }
}
func remove(_ file: FileResource) {
english.linkPreview.remove(file)
german.linkPreview.remove(file)
}
}
// MARK: Storage
extension Page: StorageItem {
convenience init(context: LoadingContext, id: String, data: Data) {
self.init(
content: context.content,
id: id,
externalLink: data.externalLink,
isDraft: data.isDraft,
createdDate: data.createdDate,
hideDate: data.hideDate ?? false,
startDate: data.startDate,
endDate: data.endDate,
german: .init(context: context, data: data.german),
english: .init(context: context, data: data.english),
tags: data.tags.compactMap(context.tag))
savedData = data
}
/// The structure to store the metadata of a page on disk
struct Data: Codable, Equatable {
let isDraft: Bool
let externalLink: String?
let tags: [String]
let hideDate: Bool?
let createdDate: Date
let startDate: Date
let endDate: Date?
let german: LocalizedPage.Data
let english: LocalizedPage.Data
}
var data: Data {
.init(
isDraft: isDraft,
externalLink: externalLink,
tags: tags.map { $0.id },
hideDate: hideDate ? true : nil,
createdDate: createdDate,
startDate: startDate,
endDate: endDate,
german: german.data,
english: english.data)
}
func saveToDisk(_ data: Data) -> Bool {
content.storage.save(page: data, for: id)
}
}