2022-08-19 18:05:06 +02:00
|
|
|
import Foundation
|
|
|
|
|
|
|
|
/**
|
|
|
|
The metadata for all site elements.
|
|
|
|
*/
|
|
|
|
struct GenericMetadata {
|
|
|
|
|
|
|
|
/**
|
|
|
|
The name of the metadata file contained in the folder of each site element.
|
|
|
|
*/
|
|
|
|
static let metadataFileName = "metadata.json"
|
|
|
|
|
2022-08-30 20:09:12 +02:00
|
|
|
/**
|
|
|
|
A custom id to uniquely identify the element on the site.
|
|
|
|
|
|
|
|
The id is used for short-hand links to pages, in the form of `![page](page_id)`
|
|
|
|
for thumbnail previews or `[text](page:page_id)` for simple links.
|
|
|
|
|
|
|
|
If no custom id is set, then the name of the element folder is used.
|
|
|
|
*/
|
|
|
|
let customId: String?
|
|
|
|
|
2022-08-19 18:05:06 +02:00
|
|
|
/**
|
|
|
|
The author of the content.
|
|
|
|
|
|
|
|
If no author is set, then the author from the parent element is used.
|
|
|
|
*/
|
|
|
|
let author: String?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The title used in the top bar of the website, next to the logo.
|
|
|
|
|
|
|
|
This title can be HTML content, and only the root level value is used.
|
|
|
|
*/
|
|
|
|
let topBarTitle: String?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The (start) date of the element.
|
|
|
|
|
|
|
|
The date is printed on content pages and may also used for sorting elements,
|
|
|
|
depending on the `useManualSorting` property of the parent.
|
|
|
|
*/
|
|
|
|
let date: String?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The end date of the element.
|
|
|
|
|
|
|
|
This property can be used to specify a date range for a content page.
|
|
|
|
*/
|
|
|
|
let endDate: String?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The deployment state of the page.
|
|
|
|
|
|
|
|
- Note: This property defaults to ``PageState.standard`
|
|
|
|
*/
|
|
|
|
let state: String?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The sort index of the page for manual sorting.
|
|
|
|
|
|
|
|
- Note: This property is only used (and must be set) if `useManualSorting` option of the parent is set.
|
|
|
|
*/
|
|
|
|
let sortIndex: Int?
|
|
|
|
|
|
|
|
/**
|
|
|
|
All files which may occur in content but is stored externally.
|
|
|
|
|
|
|
|
Missing files which would otherwise produce a warning are ignored when included here.
|
|
|
|
- Note: This property defaults to an empty set.
|
|
|
|
*/
|
|
|
|
let externalFiles: Set<String>?
|
|
|
|
|
|
|
|
/**
|
|
|
|
Specifies additional files which should be copied to the destination when generating the content.
|
|
|
|
- Note: This property defaults to an empty set.
|
|
|
|
*/
|
|
|
|
let requiredFiles: Set<String>?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The style of thumbnail to use when generating overviews.
|
|
|
|
|
|
|
|
- Note: This property is only relevant for sections.
|
|
|
|
- Note: This property is inherited from the parent if not specified.
|
|
|
|
*/
|
2022-08-25 00:12:05 +02:00
|
|
|
let thumbnailStyle: String?
|
2022-08-19 18:05:06 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
Sort the child elements by their `sortIndex` property when generating overviews, instead of using the `date`.
|
|
|
|
|
|
|
|
- Note: This property is only relevant for sections.
|
|
|
|
- Note: This property defaults to `false`
|
|
|
|
*/
|
|
|
|
let useManualSorting: Bool?
|
|
|
|
|
|
|
|
/**
|
|
|
|
The number of items to show when generating overviews of this element.
|
|
|
|
- Note: This property is only relevant for sections.
|
|
|
|
- Note: This property is inherited from the parent if not specified.
|
|
|
|
*/
|
|
|
|
let overviewItemCount: Int?
|
|
|
|
|
2022-08-26 17:40:51 +02:00
|
|
|
/**
|
|
|
|
Indicate that no header should be generated automatically.
|
|
|
|
|
|
|
|
This option assumes that custom header code is present in the page source files
|
|
|
|
- Note: If not specified, this property defaults to `false`.
|
|
|
|
*/
|
|
|
|
let useCustomHeader: Bool?
|
|
|
|
|
2022-08-19 18:05:06 +02:00
|
|
|
/**
|
|
|
|
The localized metadata for each language.
|
|
|
|
*/
|
|
|
|
let languages: [LocalizedMetadata]?
|
|
|
|
}
|
|
|
|
|
|
|
|
extension GenericMetadata: Codable {
|
|
|
|
|
|
|
|
private static var knownKeyList: [CodingKeys] {
|
|
|
|
[
|
2022-08-30 20:09:12 +02:00
|
|
|
.customId,
|
2022-08-19 18:05:06 +02:00
|
|
|
.author,
|
|
|
|
.topBarTitle,
|
|
|
|
.date,
|
|
|
|
.endDate,
|
|
|
|
.state,
|
|
|
|
.sortIndex,
|
|
|
|
.externalFiles,
|
|
|
|
.requiredFiles,
|
|
|
|
.thumbnailStyle,
|
|
|
|
.useManualSorting,
|
|
|
|
.overviewItemCount,
|
2022-08-26 17:40:51 +02:00
|
|
|
.useCustomHeader,
|
|
|
|
.languages,
|
2022-08-19 18:05:06 +02:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
static var knownKeys: Set<String> {
|
|
|
|
Set(knownKeyList.map { $0.stringValue })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension GenericMetadata {
|
|
|
|
|
|
|
|
/**
|
|
|
|
Decode metadata in a folder.
|
|
|
|
|
2022-08-26 17:40:51 +02:00
|
|
|
- Parameter data: The binary data of the metadata file.
|
2022-08-25 00:09:39 +02:00
|
|
|
- Parameter source: The path to the metadata file, relative to the source root
|
2022-08-19 18:05:06 +02:00
|
|
|
- Note: The decoding routine also checks for unknown properties, and writes them to the output.
|
2022-08-26 17:40:51 +02:00
|
|
|
- Note: Uses global objects
|
2022-08-19 18:05:06 +02:00
|
|
|
*/
|
2022-08-26 17:40:51 +02:00
|
|
|
init?(source: String) {
|
|
|
|
guard let data = files.dataOfOptionalFile(atPath: source, source: source) else {
|
2022-08-19 18:05:06 +02:00
|
|
|
return nil
|
|
|
|
}
|
2022-08-26 17:40:51 +02:00
|
|
|
|
2022-08-19 18:05:06 +02:00
|
|
|
let decoder = JSONDecoder()
|
|
|
|
|
|
|
|
let knownKeys = GenericMetadata.knownKeys
|
|
|
|
let knownLocalizedKeys = LocalizedMetadata.knownKeys
|
|
|
|
decoder.keyDecodingStrategy = .custom { keys in
|
|
|
|
let key = keys.last!
|
|
|
|
// Only one key means we are decoding the generic metadata
|
|
|
|
guard keys.count > 1 else {
|
|
|
|
if !knownKeys.contains(key.stringValue) {
|
2022-08-26 17:40:51 +02:00
|
|
|
log.unknown(property: key.stringValue, source: source)
|
2022-08-19 18:05:06 +02:00
|
|
|
}
|
|
|
|
return key
|
|
|
|
}
|
|
|
|
// Two levels means we're decoding the localized metadata
|
|
|
|
if !knownLocalizedKeys.contains(key.stringValue) {
|
2022-08-26 17:40:51 +02:00
|
|
|
log.unknown(property: key.stringValue, source: source)
|
2022-08-19 18:05:06 +02:00
|
|
|
}
|
|
|
|
return key
|
|
|
|
}
|
|
|
|
do {
|
|
|
|
self = try decoder.decode(from: data)
|
|
|
|
} catch {
|
2022-08-26 17:40:51 +02:00
|
|
|
print("Here \(data)")
|
|
|
|
log.failedToOpen(GenericMetadata.metadataFileName, requiredBy: source, error: error)
|
2022-08-19 18:05:06 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-29 13:34:00 +02:00
|
|
|
|
|
|
|
extension GenericMetadata {
|
|
|
|
|
|
|
|
static var full: GenericMetadata {
|
|
|
|
.init(
|
2022-08-30 20:09:12 +02:00
|
|
|
customId: "",
|
2022-08-29 13:34:00 +02:00
|
|
|
author: "",
|
|
|
|
topBarTitle: "",
|
|
|
|
date: "",
|
|
|
|
endDate: "",
|
|
|
|
state: "",
|
|
|
|
sortIndex: 1,
|
|
|
|
externalFiles: [],
|
|
|
|
requiredFiles: [],
|
|
|
|
thumbnailStyle: "",
|
|
|
|
useManualSorting: false,
|
|
|
|
overviewItemCount: 6,
|
|
|
|
useCustomHeader: false,
|
|
|
|
languages: [.full])
|
|
|
|
}
|
|
|
|
}
|