200 lines
6.0 KiB
Swift
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() }
|
||
|
}
|
||
|
}
|