CHGenerator/Sources/Generator/Content/GenericMetadata.swift

244 lines
7.1 KiB
Swift
Raw Normal View History

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>?
2022-09-08 13:01:32 +02:00
/**
Additional images required by the element.
These images are specified as: `source_name destination_name width (height)`.
*/
let images: Set<String>?
/**
The path to the thumbnail file.
This property is optional, and defaults to ``Element.defaultThumbnailName``.
Note: The generator first looks for localized versions of the thumbnail by appending `-[lang]` to the file name,
e.g. `customThumb-en.jpg`. If no file is found, then the specified file is tried.
*/
let thumbnailPath: String?
2022-08-19 18:05:06 +02:00
/**
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-09-04 20:36:43 +02:00
Indicate the header type to be generated automatically.
2022-09-04 20:36:43 +02:00
If this option is set to `none`, then custom header code should be present in the page source files
- Note: If not specified, this property defaults to `left`.
- Note: Overview pages are always using `center`.
*/
2022-09-04 20:36:43 +02:00
let headerType: String?
2022-12-01 15:39:39 +01:00
/**
Indicate that the overview section should contain a `Newest Content` section before the other sections.
- Note: If not specified, this property defaults to `false`
*/
let showMostRecentSection: Bool?
/**
Indicate that the overview section should contain a `Featured Content` section before the other sections.
The elements are the page ids of the elements contained in the feature.
- Note: If not specified, this property defaults to `false`
*/
let featuredPages: [String]?
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,
2022-09-08 13:01:32 +02:00
.images,
.thumbnailPath,
2022-08-19 18:05:06 +02:00
.thumbnailStyle,
.useManualSorting,
.overviewItemCount,
2022-09-04 20:36:43 +02:00
.headerType,
2022-12-01 15:39:39 +01:00
.showMostRecentSection,
.featuredPages,
.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.
- Parameter data: The binary data of the metadata file.
- 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.
- Note: Uses global objects
2022-08-19 18:05:06 +02:00
*/
init?(source: String, log: MetadataInfoLogger) {
guard let data = log.readPotentialMetadata(atPath: source, source: source) else {
2022-08-19 18:05:06 +02:00
return nil
}
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) {
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) {
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 {
log.failedToDecodeMetadata(source: 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: [],
2022-09-08 13:01:32 +02:00
images: [],
thumbnailPath: "",
2022-08-29 13:34:00 +02:00
thumbnailStyle: "",
useManualSorting: false,
overviewItemCount: 6,
2022-09-04 20:36:43 +02:00
headerType: "left",
2022-12-01 15:39:39 +01:00
showMostRecentSection: false,
featuredPages: [],
2022-08-29 13:34:00 +02:00
languages: [.full])
}
}