CHGenerator/WebsiteGenerator/Content/SiteElement.swift
Christoph Hagen 14b935249f First version
2022-08-16 10:39:05 +02:00

200 lines
6.0 KiB
Swift

import Foundation
protocol SiteElement {
/**
The sort index for the element when manual sorting is specified for the parent.
- Note: Elements are sorted in ascending order.
*/
var sortIndex: Int? { get }
/**
The date used for sorting of the element, if automatic sorting is specified by the parent.
- Note: Elements are sorted by newest first.
*/
var sortDate: Date? { get }
/**
The path to the element's folder in the source hierarchy (without a leading slash).
*/
var path: String { get }
/**
The url of the element's folder in the source hierarchy.
- Note: This property is essentially the root folder of the site, appended with the value of the ``path`` property.
*/
var inputFolder: URL { get }
/**
The localized title of the element.
This title is used as large text in overview pages, or as the `<h1>` title on pages. If no separate link preview title is specified using a localized `linkPreview.title`, then this value is also used for link previews.
*/
func title(for language: String) -> String
/**
The optional text to display in a thumbnail corner.
- Note: This text is only displayed for large thumbnails.
*/
func cornerText(for language: String) -> String?
/**
The url to the element in the given language.
If the `externalUrl` property is not set for the page metadata in the given language, then the standard path is returned.
- If this value starts with a slash, it is considered an absolute url to the same domain
- If the value starts with `http://` or `https://` it is considered an external url
- Otherwise the value is treated as a path from the root of the site.
*/
func fullPageUrl(for language: String) -> String
/**
All elements contained within the element.
If the element is a section, then this property contains the pages within.
*/
var elements: [SiteElement] { get }
func backLinkText(for language: String) throws -> String?
}
extension SiteElement {
func fullPageUrl(for language: String) -> String {
localizedPath(for: language)
}
}
extension SiteElement {
/**
The id of the section to which this element contains.
This property is used to highlight the active section in the top bar.
The section id is the folder name of the top-level section
*/
var sectionId: String {
path.components(separatedBy: "/").first!
}
static var defaultThumbnailFileName: String { "thumbnail.jpg" }
static func thumbnailFileNameLocalized(for language: String) -> String {
defaultThumbnailFileName.insert("-\(language)", beforeLast: ".")
}
var containedFolder: String {
inputFolder.lastPathComponent
}
var containsElements: Bool {
!elements.isEmpty
}
var hasNestingElements: Bool {
elements.contains { $0.containsElements }
}
/**
Get the full path of the thumbnail image for the language (relative to the root folder).
*/
func thumbnailFilePath(for language: String) -> String {
let specificImageName = Self.thumbnailFileNameLocalized(for: language)
let specificImageUrl = inputFolder.appendingPathComponent(specificImageName)
guard specificImageUrl.exists else {
return "\(path)/\(Self.defaultThumbnailFileName)"
}
return "\(path)/\(specificImageName)"
}
/**
Gets the thumbnail image for the element.
If a localized thumbnail exists, then this image name is returned.
*/
func thumbnailName(for language: String) -> String {
let specificImageName = "thumbnail-\(language).jpg"
let specificImageUrl = inputFolder.appendingPathComponent(specificImageName)
guard specificImageUrl.exists else {
return "\(inputFolder.lastPathComponent)/thumbnail.jpg"
}
return "\(inputFolder.lastPathComponent)/\(specificImageName)"
}
/**
Create an absolute path (relative to the root directory) for a file contained in the elements folder.
This function is used to copy required input files and to generate images
*/
func pathRelativeToRootForContainedInputFile(_ filePath: String) -> String {
guard !filePath.hasSuffix("/") && !filePath.hasSuffix("http") else {
return filePath
}
return "\(path)/\(filePath)"
}
func backLinkText(for language: String) throws -> String? { nil }
/**
Returns the full path (relative to the site root for a page of the element in the given language.
*/
func localizedPath(for language: String) -> String {
path != "" ? "\(path)/\(language).html" : "\(language).html"
}
func relativePathToFileWithPath(_ filePath: String) -> String {
guard path != "" else {
return filePath
}
guard filePath.hasPrefix(path) else {
return filePath
}
return filePath.replacingOccurrences(of: path + "/", with: "")
}
private var additionalHeadContentUrl: URL {
inputFolder.appendingPathComponent("head.html")
}
var hasAdditionalHeadContent: Bool {
additionalHeadContentUrl.exists
}
func customHeadContent() throws -> String? {
let url = additionalHeadContentUrl
guard url.exists else {
return nil
}
return try wrap(.failedToOpenFile(url.path)) {
try String(contentsOf: url)
}
}
private var additionalFooterContentUrl: URL {
inputFolder.appendingPathComponent("footer.html")
}
var hasAdditionalFooterContent: Bool {
additionalFooterContentUrl.exists
}
func customFooterContent() throws -> String? {
let url = additionalFooterContentUrl
guard url.exists else {
return nil
}
return try wrap(.failedToOpenFile(url.path)) {
try String(contentsOf: url)
}
}
}
extension SiteElement {
func printContents() {
print(path)
elements.forEach { $0.printContents() }
}
}