The W3C feed validator fails to validate RSS 2.0 and Atom 1.0 feed elements that do not contain a valid author. This change adds an `authors: Vec<String>` to pages, as well as an `author: Option<String>` to Config that will act as a default to use in RSS and Atom templates if no page-level authors are specified.
This commit is contained in:
parent
b2f8a94b8d
commit
f4a1e99b98
@ -58,6 +58,8 @@ pub struct Config {
|
|||||||
/// If set, files from static/ will be hardlinked instead of copied to the output dir.
|
/// If set, files from static/ will be hardlinked instead of copied to the output dir.
|
||||||
pub hard_link_static: bool,
|
pub hard_link_static: bool,
|
||||||
pub taxonomies: Vec<taxonomies::TaxonomyConfig>,
|
pub taxonomies: Vec<taxonomies::TaxonomyConfig>,
|
||||||
|
/// The default author for pages.
|
||||||
|
pub author: Option<String>,
|
||||||
|
|
||||||
/// Whether to compile the `sass` directory and output the css files into the static folder
|
/// Whether to compile the `sass` directory and output the css files into the static folder
|
||||||
pub compile_sass: bool,
|
pub compile_sass: bool,
|
||||||
@ -103,6 +105,7 @@ pub struct SerializedConfig<'a> {
|
|||||||
generate_feed: bool,
|
generate_feed: bool,
|
||||||
feed_filename: &'a str,
|
feed_filename: &'a str,
|
||||||
taxonomies: &'a [taxonomies::TaxonomyConfig],
|
taxonomies: &'a [taxonomies::TaxonomyConfig],
|
||||||
|
author: &'a Option<String>,
|
||||||
build_search_index: bool,
|
build_search_index: bool,
|
||||||
extra: &'a HashMap<String, Toml>,
|
extra: &'a HashMap<String, Toml>,
|
||||||
markdown: &'a markup::Markdown,
|
markdown: &'a markup::Markdown,
|
||||||
@ -324,6 +327,7 @@ impl Config {
|
|||||||
generate_feed: options.generate_feed,
|
generate_feed: options.generate_feed,
|
||||||
feed_filename: &options.feed_filename,
|
feed_filename: &options.feed_filename,
|
||||||
taxonomies: &options.taxonomies,
|
taxonomies: &options.taxonomies,
|
||||||
|
author: &self.author,
|
||||||
build_search_index: options.build_search_index,
|
build_search_index: options.build_search_index,
|
||||||
extra: &self.extra,
|
extra: &self.extra,
|
||||||
markdown: &self.markdown,
|
markdown: &self.markdown,
|
||||||
@ -373,6 +377,7 @@ impl Default for Config {
|
|||||||
feed_filename: "atom.xml".to_string(),
|
feed_filename: "atom.xml".to_string(),
|
||||||
hard_link_static: false,
|
hard_link_static: false,
|
||||||
taxonomies: Vec::new(),
|
taxonomies: Vec::new(),
|
||||||
|
author: None,
|
||||||
compile_sass: false,
|
compile_sass: false,
|
||||||
minify_html: false,
|
minify_html: false,
|
||||||
mode: Mode::Build,
|
mode: Mode::Build,
|
||||||
@ -858,4 +863,15 @@ highlight_theme = "css"
|
|||||||
let serialised = config.serialize(&config.default_language);
|
let serialised = config.serialize(&config.default_language);
|
||||||
assert_eq!(serialised.markdown.highlight_theme, config.markdown.highlight_theme);
|
assert_eq!(serialised.markdown.highlight_theme, config.markdown.highlight_theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sets_default_author_if_present() {
|
||||||
|
let config = r#"
|
||||||
|
title = "My Site"
|
||||||
|
base_url = "example.com"
|
||||||
|
author = "person@example.com (Some Person)"
|
||||||
|
"#;
|
||||||
|
let config = Config::parse(config).unwrap();
|
||||||
|
assert_eq!(config.author, Some("person@example.com (Some Person)".to_owned()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,8 @@ pub struct PageFrontMatter {
|
|||||||
pub taxonomies: HashMap<String, Vec<String>>,
|
pub taxonomies: HashMap<String, Vec<String>>,
|
||||||
/// Integer to use to order content. Highest is at the bottom, lowest first
|
/// Integer to use to order content. Highest is at the bottom, lowest first
|
||||||
pub weight: Option<usize>,
|
pub weight: Option<usize>,
|
||||||
|
/// The authors of the page.
|
||||||
|
pub authors: Vec<String>,
|
||||||
/// All aliases for that page. Zola will create HTML templates that will
|
/// All aliases for that page. Zola will create HTML templates that will
|
||||||
/// redirect to this
|
/// redirect to this
|
||||||
#[serde(skip_serializing)]
|
#[serde(skip_serializing)]
|
||||||
@ -153,6 +155,7 @@ impl Default for PageFrontMatter {
|
|||||||
path: None,
|
path: None,
|
||||||
taxonomies: HashMap::new(),
|
taxonomies: HashMap::new(),
|
||||||
weight: None,
|
weight: None,
|
||||||
|
authors: Vec::new(),
|
||||||
aliases: Vec::new(),
|
aliases: Vec::new(),
|
||||||
template: None,
|
template: None,
|
||||||
extra: Map::new(),
|
extra: Map::new(),
|
||||||
@ -502,4 +505,27 @@ taxonomies:
|
|||||||
println!("{:?}", res);
|
println!("{:?}", res);
|
||||||
assert!(res.is_err());
|
assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(&RawFrontMatter::Toml(r#"
|
||||||
|
authors = ["person1@example.com (Person One)", "person2@example.com (Person Two)"]
|
||||||
|
"#); "toml")]
|
||||||
|
#[test_case(&RawFrontMatter::Yaml(r#"
|
||||||
|
title: Hello World
|
||||||
|
authors:
|
||||||
|
- person1@example.com (Person One)
|
||||||
|
- person2@example.com (Person Two)
|
||||||
|
"#); "yaml")]
|
||||||
|
fn can_parse_authors(content: &RawFrontMatter) {
|
||||||
|
let res = PageFrontMatter::parse(content);
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let res2 = res.unwrap();
|
||||||
|
assert_eq!(res2.authors.len(), 2);
|
||||||
|
assert_eq!(
|
||||||
|
vec!(
|
||||||
|
"person1@example.com (Person One)".to_owned(),
|
||||||
|
"person2@example.com (Person Two)".to_owned()
|
||||||
|
),
|
||||||
|
res2.authors
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,6 +345,32 @@ Hello world"#;
|
|||||||
assert_eq!(page.content, "<p>Hello world</p>\n".to_string());
|
assert_eq!(page.content, "<p>Hello world</p>\n".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_parse_author() {
|
||||||
|
let config = Config::default_for_test();
|
||||||
|
let content = r#"
|
||||||
|
+++
|
||||||
|
title = "Hello"
|
||||||
|
description = "hey there"
|
||||||
|
authors = ["person@example.com (A. Person)"]
|
||||||
|
+++
|
||||||
|
Hello world"#;
|
||||||
|
let res = Page::parse(Path::new("post.md"), content, &config, &PathBuf::new());
|
||||||
|
assert!(res.is_ok());
|
||||||
|
let mut page = res.unwrap();
|
||||||
|
page.render_markdown(
|
||||||
|
&HashMap::default(),
|
||||||
|
&Tera::default(),
|
||||||
|
&config,
|
||||||
|
InsertAnchor::None,
|
||||||
|
&HashMap::new(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(1, page.meta.authors.len());
|
||||||
|
assert_eq!("person@example.com (A. Person)", page.meta.authors.get(0).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_can_make_url_from_sections_and_slug() {
|
fn test_can_make_url_from_sections_and_slug() {
|
||||||
let content = r#"
|
let content = r#"
|
||||||
|
@ -55,6 +55,7 @@ pub struct SerializingPage<'a> {
|
|||||||
month: Option<u8>,
|
month: Option<u8>,
|
||||||
day: Option<u8>,
|
day: Option<u8>,
|
||||||
taxonomies: &'a HashMap<String, Vec<String>>,
|
taxonomies: &'a HashMap<String, Vec<String>>,
|
||||||
|
authors: &'a [String],
|
||||||
extra: &'a Map<String, Value>,
|
extra: &'a Map<String, Value>,
|
||||||
path: &'a str,
|
path: &'a str,
|
||||||
components: &'a [String],
|
components: &'a [String],
|
||||||
@ -119,6 +120,7 @@ impl<'a> SerializingPage<'a> {
|
|||||||
month,
|
month,
|
||||||
day,
|
day,
|
||||||
taxonomies: &page.meta.taxonomies,
|
taxonomies: &page.meta.taxonomies,
|
||||||
|
authors: &page.meta.authors,
|
||||||
path: &page.path,
|
path: &page.path,
|
||||||
components: &page.components,
|
components: &page.components,
|
||||||
summary: &page.summary,
|
summary: &page.summary,
|
||||||
|
@ -836,6 +836,31 @@ fn panics_on_invalid_external_domain() {
|
|||||||
site.load().expect("link check test_site");
|
site.load().expect("link check test_site");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn can_find_site_and_page_authors() {
|
||||||
|
let mut path = env::current_dir().unwrap().parent().unwrap().parent().unwrap().to_path_buf();
|
||||||
|
path.push("test_site");
|
||||||
|
let config_file = path.join("config.toml");
|
||||||
|
let mut site = Site::new(&path, config_file).unwrap();
|
||||||
|
site.load().unwrap();
|
||||||
|
let library = site.library.read().unwrap();
|
||||||
|
|
||||||
|
// The config has a global default author set.
|
||||||
|
let author = site.config.author;
|
||||||
|
assert_eq!(Some("config@example.com (Config Author)".to_string()), author);
|
||||||
|
|
||||||
|
let posts_path = path.join("content").join("posts");
|
||||||
|
let posts_section = library.sections.get(&posts_path.join("_index.md")).unwrap();
|
||||||
|
|
||||||
|
let p1 = &library.pages[&posts_section.pages[0]];
|
||||||
|
let p2 = &library.pages[&posts_section.pages[1]];
|
||||||
|
|
||||||
|
// Only the first page has had an author added.
|
||||||
|
assert_eq!(1, p1.meta.authors.len());
|
||||||
|
assert_eq!("page@example.com (Page Author)", p1.meta.authors.get(0).unwrap());
|
||||||
|
assert_eq!(0, p2.meta.authors.len());
|
||||||
|
}
|
||||||
|
|
||||||
// Follows test_site/themes/sample/templates/current_path.html
|
// Follows test_site/themes/sample/templates/current_path.html
|
||||||
fn current_path(path: &str) -> String {
|
fn current_path(path: &str) -> String {
|
||||||
format!("[current_path]({})", path)
|
format!("[current_path]({})", path)
|
||||||
|
@ -1,32 +1,43 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
|
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
|
||||||
<title>{{ config.title }}
|
<title>{{ config.title }}
|
||||||
{%- if term %} - {{ term.name }}
|
{%- if term %} - {{ term.name }}
|
||||||
{%- elif section.title %} - {{ section.title }}
|
{%- elif section.title %} - {{ section.title }}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</title>
|
</title>
|
||||||
{%- if config.description %}
|
{%- if config.description %}
|
||||||
<subtitle>{{ config.description }}</subtitle>
|
<subtitle>{{ config.description }}</subtitle>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
<link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
|
<link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
|
||||||
<link href="
|
<link href="
|
||||||
{%- if section -%}
|
{%- if section -%}
|
||||||
{{ section.permalink | escape_xml | safe }}
|
{{ section.permalink | escape_xml | safe }}
|
||||||
{%- else -%}
|
{%- else -%}
|
||||||
{{ config.base_url | escape_xml | safe }}
|
{{ config.base_url | escape_xml | safe }}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
"/>
|
"/>
|
||||||
<generator uri="https://www.getzola.org/">Zola</generator>
|
<generator uri="https://www.getzola.org/">Zola</generator>
|
||||||
<updated>{{ last_updated | date(format="%+") }}</updated>
|
<updated>{{ last_updated | date(format="%+") }}</updated>
|
||||||
<id>{{ feed_url | safe }}</id>
|
<id>{{ feed_url | safe }}</id>
|
||||||
{%- for page in pages %}
|
{%- for page in pages %}
|
||||||
<entry xml:lang="{{ page.lang }}">
|
<entry xml:lang="{{ page.lang }}">
|
||||||
<title>{{ page.title }}</title>
|
<title>{{ page.title }}</title>
|
||||||
<published>{{ page.date | date(format="%+") }}</published>
|
<published>{{ page.date | date(format="%+") }}</published>
|
||||||
<updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
|
<updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
|
||||||
<link rel="alternate" href="{{ page.permalink | safe }}" type="text/html"/>
|
<author>
|
||||||
<id>{{ page.permalink | safe }}</id>
|
<name>
|
||||||
<content type="html">{{ page.content }}</content>
|
{%- if page.authors -%}
|
||||||
</entry>
|
{{ page.authors[0] }}
|
||||||
{%- endfor %}
|
{%- elif config.author -%}
|
||||||
|
{{ config.author }}
|
||||||
|
{%- else -%}
|
||||||
|
Unknown
|
||||||
|
{%- endif -%}
|
||||||
|
</name>
|
||||||
|
</author>
|
||||||
|
<link rel="alternate" href="{{ page.permalink | safe }}" type="text/html"/>
|
||||||
|
<id>{{ page.permalink | safe }}</id>
|
||||||
|
<content type="html">{{ page.content }}</content>
|
||||||
|
</entry>
|
||||||
|
{%- endfor %}
|
||||||
</feed>
|
</feed>
|
||||||
|
@ -6,25 +6,35 @@
|
|||||||
{%- elif section.title %} - {{ section.title }}
|
{%- elif section.title %} - {{ section.title }}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</title>
|
</title>
|
||||||
<link>{%- if section -%}
|
<link>
|
||||||
{{ section.permalink | escape_xml | safe }}
|
{%- if section -%}
|
||||||
{%- else -%}
|
{{ section.permalink | escape_xml | safe }}
|
||||||
{{ config.base_url | escape_xml | safe }}
|
{%- else -%}
|
||||||
{%- endif -%}
|
{{ config.base_url | escape_xml | safe }}
|
||||||
</link>
|
{%- endif -%}
|
||||||
<description>{{ config.description }}</description>
|
</link>
|
||||||
<generator>Zola</generator>
|
<description>{{ config.description }}</description>
|
||||||
<language>{{ lang }}</language>
|
<generator>Zola</generator>
|
||||||
<atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
|
<language>{{ lang }}</language>
|
||||||
<lastBuildDate>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
|
<atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
|
||||||
{%- for page in pages %}
|
<lastBuildDate>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
|
||||||
<item>
|
{%- for page in pages %}
|
||||||
<title>{{ page.title }}</title>
|
<item>
|
||||||
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
|
<title>{{ page.title }}</title>
|
||||||
<link>{{ page.permalink | escape_xml | safe }}</link>
|
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
|
||||||
<guid>{{ page.permalink | escape_xml | safe }}</guid>
|
<author>
|
||||||
<description>{% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %}</description>
|
{%- if page.authors -%}
|
||||||
</item>
|
{{ page.authors[0] }}
|
||||||
{%- endfor %}
|
{%- elif config.author -%}
|
||||||
|
{{ config.author }}
|
||||||
|
{%- else -%}
|
||||||
|
Unknown
|
||||||
|
{%- endif -%}
|
||||||
|
</author>
|
||||||
|
<link>{{ page.permalink | escape_xml | safe }}</link>
|
||||||
|
<guid>{{ page.permalink | escape_xml | safe }}</guid>
|
||||||
|
<description>{% if page.summary %}{{ page.summary }}{% else %}{{ page.content }}{% endif %}</description>
|
||||||
|
</item>
|
||||||
|
{%- endfor %}
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
@ -126,6 +126,10 @@ path = ""
|
|||||||
# current one. This takes an array of paths, not URLs.
|
# current one. This takes an array of paths, not URLs.
|
||||||
aliases = []
|
aliases = []
|
||||||
|
|
||||||
|
# A list of page authors. If a site feed is enabled, the first author (if any)
|
||||||
|
# will be used as the page's author in the default feed template.
|
||||||
|
authors = []
|
||||||
|
|
||||||
# When set to "true", the page will be in the search index. This is only used if
|
# When set to "true", the page will be in the search index. This is only used if
|
||||||
# `build_search_index` is set to "true" in the Zola configuration and the parent section
|
# `build_search_index` is set to "true" in the Zola configuration and the parent section
|
||||||
# hasn't set `in_search_index` to "false" in its front matter.
|
# hasn't set `in_search_index` to "false" in its front matter.
|
||||||
|
@ -11,6 +11,8 @@ taxonomies = [
|
|||||||
|
|
||||||
ignored_content = ["*/ignored.md"]
|
ignored_content = ["*/ignored.md"]
|
||||||
|
|
||||||
|
author = "config@example.com (Config Author)"
|
||||||
|
|
||||||
[markdown]
|
[markdown]
|
||||||
highlight_code = true
|
highlight_code = true
|
||||||
highlight_theme = "custom_gruvbox"
|
highlight_theme = "custom_gruvbox"
|
||||||
|
@ -2,4 +2,5 @@
|
|||||||
title = "A transparent page"
|
title = "A transparent page"
|
||||||
description = ""
|
description = ""
|
||||||
date = 2018-10-10
|
date = 2018-10-10
|
||||||
|
authors = ["page@example.com (Page Author)"]
|
||||||
+++
|
+++
|
||||||
|
Loading…
Reference in New Issue
Block a user