Generate open graph meta tags
This commit is contained in:
@ -36,6 +36,14 @@ enum HeaderElement {
|
||||
|
||||
case description(String)
|
||||
|
||||
case ogTitle(String)
|
||||
|
||||
case ogDescription(String)
|
||||
|
||||
case ogImage(String)
|
||||
|
||||
case ogUrl(String)
|
||||
|
||||
case charset
|
||||
|
||||
case viewport
|
||||
@ -44,30 +52,24 @@ enum HeaderElement {
|
||||
|
||||
var order: Int {
|
||||
switch self {
|
||||
case .charset:
|
||||
return 1
|
||||
case .robots:
|
||||
return 2
|
||||
case .viewport:
|
||||
return 3
|
||||
case .icon:
|
||||
return 10
|
||||
case .css(_, let order):
|
||||
return order
|
||||
case .js:
|
||||
return 20
|
||||
case .jsModule:
|
||||
return 30
|
||||
case .author:
|
||||
return 100
|
||||
case .title:
|
||||
return 101
|
||||
case .description:
|
||||
return 102
|
||||
case .charset: 1
|
||||
case .robots: 2
|
||||
case .viewport: 3
|
||||
case .icon: 10
|
||||
case .css(_, let order): order
|
||||
case .js: 20
|
||||
case .jsModule: 30
|
||||
case .author: 100
|
||||
case .title: 101
|
||||
case .description: 102
|
||||
case .ogTitle: 103
|
||||
case .ogDescription: 104
|
||||
case .ogImage: 105
|
||||
case .ogUrl: 106
|
||||
}
|
||||
}
|
||||
|
||||
var file: FileResource? {
|
||||
var requiredFile: FileResource? {
|
||||
switch self {
|
||||
case .icon(let file, _, _):
|
||||
return file
|
||||
@ -113,6 +115,14 @@ extension HeaderElement {
|
||||
return "<title>\(title)</title>"
|
||||
case .description(let description):
|
||||
return "<meta name='description' content=\"\(description)\">"
|
||||
case .ogTitle(let title):
|
||||
return "<meta property='og:title' content='\(title)'>"
|
||||
case .ogDescription(let description):
|
||||
return "<meta property='og:description' content='\(description)'>"
|
||||
case .ogImage(let image):
|
||||
return "<meta property='og:image' content='\(image)'>"
|
||||
case .ogUrl(let url):
|
||||
return "<meta property='og:url' content='\(url)'>"
|
||||
case .charset:
|
||||
return "<meta charset='utf-8' />"
|
||||
case .viewport:
|
||||
@ -141,6 +151,14 @@ extension HeaderElement: CustomStringConvertible {
|
||||
return "title"
|
||||
case .description:
|
||||
return "description"
|
||||
case .ogTitle:
|
||||
return "og:title"
|
||||
case .ogDescription:
|
||||
return "og:description"
|
||||
case .ogImage:
|
||||
return "og:image"
|
||||
case .ogUrl:
|
||||
return "og:url"
|
||||
case .charset:
|
||||
return "charset"
|
||||
case .viewport:
|
||||
|
@ -24,9 +24,11 @@ final class FeedPageGenerator {
|
||||
|
||||
func generatePage(language: ContentLanguage,
|
||||
posts: [FeedEntryData],
|
||||
title: String,
|
||||
title: String?,
|
||||
description: String?,
|
||||
showTitle: Bool,
|
||||
image: FileResource?,
|
||||
pageUrl: String,
|
||||
pageTitle: String?,
|
||||
pageNumber: Int,
|
||||
totalPages: Int,
|
||||
languageButtonUrl: String,
|
||||
@ -44,10 +46,14 @@ final class FeedPageGenerator {
|
||||
text: language.next.rawValue,
|
||||
url: languageButtonUrl)
|
||||
|
||||
let imageUrl = image?.linkPreviewImage(results: results)
|
||||
|
||||
let pageHeader = PageHeader(
|
||||
language: language,
|
||||
title: title,
|
||||
title: title ?? pageTitle,
|
||||
description: description,
|
||||
image: imageUrl,
|
||||
pageUrl: pageUrl,
|
||||
iconUrl: iconUrl,
|
||||
languageButton: languageButton,
|
||||
links: content.navigationBar(in: language),
|
||||
@ -57,8 +63,8 @@ final class FeedPageGenerator {
|
||||
let page = GenericPage(
|
||||
header: pageHeader,
|
||||
additionalFooter: footer) { content in
|
||||
if showTitle {
|
||||
content += "<h1 class='separated-headline'>\(title)</h1>"
|
||||
if let pageTitle {
|
||||
content += "<h1 class='separated-headline'>\(pageTitle)</h1>"
|
||||
}
|
||||
for post in posts {
|
||||
content += FeedEntry(data: post).content
|
||||
|
@ -25,7 +25,7 @@ final class PageGenerator {
|
||||
text: settings.emptyPageText).content
|
||||
}
|
||||
|
||||
func generate(page: Page, language: ContentLanguage, results: PageGenerationResults) -> String? {
|
||||
func generate(page: Page, language: ContentLanguage, results: PageGenerationResults, pageUrl: String) -> String? {
|
||||
let contentGenerator = PageContentParser(
|
||||
content: content,
|
||||
language: language, results: results)
|
||||
@ -51,7 +51,7 @@ final class PageGenerator {
|
||||
}
|
||||
|
||||
let headers = makeHeaders(requiredItems: results.requiredHeaders, results: results)
|
||||
results.require(files: headers.compactMap { $0.file })
|
||||
results.require(files: headers.compactMap { $0.requiredFile })
|
||||
|
||||
let iconUrl = content.settings.navigation.localized(in: language).rootUrl
|
||||
let languageUrl = page.absoluteUrl(in: language.next)
|
||||
@ -59,10 +59,14 @@ final class PageGenerator {
|
||||
text: language.next.rawValue,
|
||||
url: languageUrl)
|
||||
|
||||
let imageUrl = localized.linkPreview.image?.linkPreviewImage(results: results)
|
||||
|
||||
let pageHeader = PageHeader(
|
||||
language: language,
|
||||
title: localized.linkPreview.title ?? localized.title,
|
||||
description: localized.linkPreview.description,
|
||||
image: imageUrl,
|
||||
pageUrl: pageUrl,
|
||||
iconUrl: iconUrl,
|
||||
languageButton: languageButton,
|
||||
links: content.navigationBar(in: language),
|
||||
|
@ -34,22 +34,45 @@ private struct TagHeaderContent {
|
||||
|
||||
let description: String?
|
||||
|
||||
let image: String?
|
||||
|
||||
let iconUrl: String
|
||||
|
||||
let links: [NavigationBar.Link]
|
||||
|
||||
let headers: Set<HeaderElement>
|
||||
|
||||
let baseUrl: String
|
||||
let basePath: String
|
||||
|
||||
let websiteUrl: String
|
||||
|
||||
let localizedBaseUrl: String
|
||||
|
||||
private func url(pageNumber: Int) -> String {
|
||||
baseUrl + "/\(pageNumber)"
|
||||
/**
|
||||
The path (relative to the output folder) for the html file
|
||||
*/
|
||||
func filePath(pageNumber: Int) -> String {
|
||||
basePath + "/\(pageNumber).html"
|
||||
}
|
||||
|
||||
func fileUrl(pageNumber: Int) -> String {
|
||||
url(pageNumber: pageNumber) + ".html"
|
||||
/**
|
||||
The absolute url (without domain) for the language button of the page
|
||||
*/
|
||||
private func languageButtonUrl(page: Int) -> String {
|
||||
guard page > 1 else {
|
||||
return localizedBaseUrl
|
||||
}
|
||||
return localizedBaseUrl + "/\(page)"
|
||||
}
|
||||
|
||||
/**
|
||||
The full url (including domain) to the page.
|
||||
*/
|
||||
private func pageUrl(page: Int) -> String {
|
||||
guard page > 1 else {
|
||||
return websiteUrl + basePath
|
||||
}
|
||||
return websiteUrl + basePath + "/\(page)"
|
||||
}
|
||||
|
||||
func pageHeader(pageNumber: Int) -> PageHeader {
|
||||
@ -57,10 +80,12 @@ private struct TagHeaderContent {
|
||||
language: language,
|
||||
title: title,
|
||||
description: description,
|
||||
image: image,
|
||||
pageUrl: pageUrl(page: pageNumber),
|
||||
iconUrl: iconUrl,
|
||||
languageButton: .init(
|
||||
text: language.next.rawValue,
|
||||
url: localizedBaseUrl + "/\(pageNumber)"),
|
||||
url: languageButtonUrl(page: pageNumber)),
|
||||
links: links,
|
||||
headers: headers,
|
||||
icons: [])
|
||||
@ -83,14 +108,19 @@ final class TagOverviewGenerator {
|
||||
|
||||
func generatePages(tags: [Tag], overview: Tag) {
|
||||
let localized = overview.localized(in: language)
|
||||
|
||||
let imageUrl = localized.linkPreview.image?.linkPreviewImage(results: results)
|
||||
|
||||
let header = TagHeaderContent(
|
||||
language: language,
|
||||
title: localized.linkPreview.title ?? localized.title,
|
||||
description: localized.linkPreview.description,
|
||||
image: imageUrl,
|
||||
iconUrl: content.settings.navigation.localized(in: language).rootUrl,
|
||||
links: content.navigationBar(in: language),
|
||||
headers: content.postPageHeaders,
|
||||
baseUrl: overview.absoluteUrl(in: language),
|
||||
basePath: overview.absoluteUrl(in: language),
|
||||
websiteUrl: content.settings.general.url,
|
||||
localizedBaseUrl: overview.absoluteUrl(in: language.next))
|
||||
|
||||
// Sort tags by title
|
||||
@ -140,16 +170,16 @@ final class TagOverviewGenerator {
|
||||
}
|
||||
if totalPages > 1 {
|
||||
content += PostFeedPageNavigation(
|
||||
linkPrefix: header.baseUrl,
|
||||
linkPrefix: header.basePath,
|
||||
currentPage: pageNumber,
|
||||
numberOfPages: totalPages).content
|
||||
}
|
||||
}
|
||||
let fileContent = page.content
|
||||
let url = header.fileUrl(pageNumber: pageNumber)
|
||||
let filePath = header.filePath(pageNumber: pageNumber)
|
||||
|
||||
guard content.storage.write(fileContent, to: url) else {
|
||||
results.unsavedOutput(url, source: .tagOverview)
|
||||
guard content.storage.write(fileContent, to: filePath) else {
|
||||
results.unsavedOutput(filePath, source: .tagOverview)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -7,20 +7,28 @@ struct FeedGeneratorSource: PostListPageGeneratorSource {
|
||||
|
||||
let results: PageGenerationResults
|
||||
|
||||
var showTitle: Bool {
|
||||
false
|
||||
}
|
||||
|
||||
var postsPerPage: Int {
|
||||
content.settings.posts.postsPerPage
|
||||
}
|
||||
|
||||
var pageTitle: String {
|
||||
content.settings.posts.localized(in: language).title
|
||||
var pageTitle: String? {
|
||||
nil // Don't show title in page
|
||||
}
|
||||
|
||||
var pageDescription: String {
|
||||
content.settings.posts.localized(in: language).description
|
||||
private var linkPreview: LinkPreview {
|
||||
content.settings.posts.localized(in: language).linkPreview
|
||||
}
|
||||
|
||||
var linkTitle: String? {
|
||||
linkPreview.title
|
||||
}
|
||||
|
||||
var linkDescription: String? {
|
||||
linkPreview.description
|
||||
}
|
||||
|
||||
var linkImage: FileResource? {
|
||||
linkPreview.image
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,15 @@ final class PostListPageGenerator {
|
||||
}
|
||||
|
||||
private func pageUrl(in language: ContentLanguage, pageNumber: Int) -> String {
|
||||
"\(source.pageUrlPrefix(for: language))/\(pageNumber)"
|
||||
let base = source.content.settings.general.url + source.pageUrlPrefix(for: language)
|
||||
guard pageNumber > 1 else {
|
||||
return base
|
||||
}
|
||||
return base + "/\(pageNumber)"
|
||||
}
|
||||
|
||||
private func filePath(in language: ContentLanguage, pageNumber: Int) -> String {
|
||||
"\(source.pageUrlPrefix(for: language))/\(pageNumber).html"
|
||||
}
|
||||
|
||||
func createPages(for posts: [Post]) {
|
||||
@ -82,18 +90,23 @@ final class PostListPageGenerator {
|
||||
// Includes leading slash
|
||||
let languageButtonUrl = pageUrl(in: language.next, pageNumber: pageIndex)
|
||||
|
||||
// Includes leading slash
|
||||
let pageUrl = pageUrl(in: language, pageNumber: pageIndex)
|
||||
|
||||
let fileContent = feedPageGenerator.generatePage(
|
||||
language: language,
|
||||
posts: posts,
|
||||
title: source.pageTitle,
|
||||
description: source.pageDescription,
|
||||
showTitle: source.showTitle,
|
||||
title: source.linkTitle,
|
||||
description: source.linkDescription,
|
||||
image: source.linkImage,
|
||||
pageUrl: pageUrl,
|
||||
pageTitle: source.pageTitle,
|
||||
pageNumber: pageIndex,
|
||||
totalPages: pageCount,
|
||||
languageButtonUrl: languageButtonUrl,
|
||||
linkPrefix: source.pageUrlPrefix(for: language))
|
||||
// Includes leading slash
|
||||
let filePath = pageUrl(in: language, pageNumber: pageIndex) + ".html"
|
||||
|
||||
let filePath = self.filePath(in: language, pageNumber: pageIndex)
|
||||
guard save(fileContent, to: filePath) else {
|
||||
source.results.unsavedOutput(filePath, source: .feed)
|
||||
return
|
||||
|
@ -7,11 +7,13 @@ protocol PostListPageGeneratorSource {
|
||||
|
||||
var results: PageGenerationResults { get }
|
||||
|
||||
var showTitle: Bool { get }
|
||||
var pageTitle: String? { get }
|
||||
|
||||
var pageTitle: String { get }
|
||||
var linkTitle: String? { get }
|
||||
|
||||
var pageDescription: String { get }
|
||||
var linkDescription: String? { get }
|
||||
|
||||
var linkImage: FileResource? { get }
|
||||
|
||||
/**
|
||||
The url to the page, including a leading slash
|
||||
|
@ -9,20 +9,28 @@ struct TagPageGeneratorSource: PostListPageGeneratorSource {
|
||||
|
||||
let tag: Tag
|
||||
|
||||
var showTitle: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
var postsPerPage: Int {
|
||||
content.settings.posts.postsPerPage
|
||||
}
|
||||
|
||||
var pageTitle: String {
|
||||
tag.localized(in: language).name
|
||||
var pageTitle: String? {
|
||||
linkPreview.title ?? tag.localized(in: language).name
|
||||
}
|
||||
|
||||
var pageDescription: String {
|
||||
tag.localized(in: language).linkPreview.description ?? ""
|
||||
private var linkPreview: LinkPreview {
|
||||
tag.localized(in: language).linkPreview
|
||||
}
|
||||
|
||||
var linkTitle: String? {
|
||||
linkPreview.title
|
||||
}
|
||||
|
||||
var linkDescription: String? {
|
||||
linkPreview.description
|
||||
}
|
||||
|
||||
var linkImage: FileResource? {
|
||||
linkPreview.image
|
||||
}
|
||||
|
||||
func pageUrlPrefix(for language: ContentLanguage) -> String {
|
||||
|
Reference in New Issue
Block a user