mirror of
https://github.com/getzola/zola
synced 2024-11-10 06:14:19 +00:00
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
10 changed files with 165 additions and 42 deletions
|
@ -58,6 +58,8 @@ pub struct Config {
|
|||
/// If set, files from static/ will be hardlinked instead of copied to the output dir.
|
||||
pub hard_link_static: bool,
|
||||
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
|
||||
pub compile_sass: bool,
|
||||
|
@ -103,6 +105,7 @@ pub struct SerializedConfig<'a> {
|
|||
generate_feed: bool,
|
||||
feed_filename: &'a str,
|
||||
taxonomies: &'a [taxonomies::TaxonomyConfig],
|
||||
author: &'a Option<String>,
|
||||
build_search_index: bool,
|
||||
extra: &'a HashMap<String, Toml>,
|
||||
markdown: &'a markup::Markdown,
|
||||
|
@ -324,6 +327,7 @@ impl Config {
|
|||
generate_feed: options.generate_feed,
|
||||
feed_filename: &options.feed_filename,
|
||||
taxonomies: &options.taxonomies,
|
||||
author: &self.author,
|
||||
build_search_index: options.build_search_index,
|
||||
extra: &self.extra,
|
||||
markdown: &self.markdown,
|
||||
|
@ -373,6 +377,7 @@ impl Default for Config {
|
|||
feed_filename: "atom.xml".to_string(),
|
||||
hard_link_static: false,
|
||||
taxonomies: Vec::new(),
|
||||
author: None,
|
||||
compile_sass: false,
|
||||
minify_html: false,
|
||||
mode: Mode::Build,
|
||||
|
@ -858,4 +863,15 @@ highlight_theme = "css"
|
|||
let serialised = config.serialize(&config.default_language);
|
||||
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>>,
|
||||
/// Integer to use to order content. Highest is at the bottom, lowest first
|
||||
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
|
||||
/// redirect to this
|
||||
#[serde(skip_serializing)]
|
||||
|
@ -153,6 +155,7 @@ impl Default for PageFrontMatter {
|
|||
path: None,
|
||||
taxonomies: HashMap::new(),
|
||||
weight: None,
|
||||
authors: Vec::new(),
|
||||
aliases: Vec::new(),
|
||||
template: None,
|
||||
extra: Map::new(),
|
||||
|
@ -502,4 +505,27 @@ taxonomies:
|
|||
println!("{:?}", res);
|
||||
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());
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn test_can_make_url_from_sections_and_slug() {
|
||||
let content = r#"
|
||||
|
|
|
@ -55,6 +55,7 @@ pub struct SerializingPage<'a> {
|
|||
month: Option<u8>,
|
||||
day: Option<u8>,
|
||||
taxonomies: &'a HashMap<String, Vec<String>>,
|
||||
authors: &'a [String],
|
||||
extra: &'a Map<String, Value>,
|
||||
path: &'a str,
|
||||
components: &'a [String],
|
||||
|
@ -119,6 +120,7 @@ impl<'a> SerializingPage<'a> {
|
|||
month,
|
||||
day,
|
||||
taxonomies: &page.meta.taxonomies,
|
||||
authors: &page.meta.authors,
|
||||
path: &page.path,
|
||||
components: &page.components,
|
||||
summary: &page.summary,
|
||||
|
|
|
@ -836,6 +836,31 @@ fn panics_on_invalid_external_domain() {
|
|||
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
|
||||
fn current_path(path: &str) -> String {
|
||||
format!("[current_path]({})", path)
|
||||
|
|
|
@ -1,32 +1,43 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}">
|
||||
<title>{{ config.title }}
|
||||
{%- if term %} - {{ term.name }}
|
||||
<title>{{ config.title }}
|
||||
{%- if term %} - {{ term.name }}
|
||||
{%- elif section.title %} - {{ section.title }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
{%- if config.description %}
|
||||
<subtitle>{{ config.description }}</subtitle>
|
||||
{%- endif %}
|
||||
<link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
|
||||
<link href="
|
||||
{%- endif -%}
|
||||
</title>
|
||||
{%- if config.description %}
|
||||
<subtitle>{{ config.description }}</subtitle>
|
||||
{%- endif %}
|
||||
<link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/>
|
||||
<link href="
|
||||
{%- if section -%}
|
||||
{{ section.permalink | escape_xml | safe }}
|
||||
{%- else -%}
|
||||
{{ config.base_url | escape_xml | safe }}
|
||||
{%- endif -%}
|
||||
"/>
|
||||
<generator uri="https://www.getzola.org/">Zola</generator>
|
||||
<updated>{{ last_updated | date(format="%+") }}</updated>
|
||||
<id>{{ feed_url | safe }}</id>
|
||||
{%- for page in pages %}
|
||||
<entry xml:lang="{{ page.lang }}">
|
||||
<title>{{ page.title }}</title>
|
||||
<published>{{ page.date | date(format="%+") }}</published>
|
||||
<updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
|
||||
<link rel="alternate" href="{{ page.permalink | safe }}" type="text/html"/>
|
||||
<id>{{ page.permalink | safe }}</id>
|
||||
<content type="html">{{ page.content }}</content>
|
||||
</entry>
|
||||
{%- endfor %}
|
||||
<generator uri="https://www.getzola.org/">Zola</generator>
|
||||
<updated>{{ last_updated | date(format="%+") }}</updated>
|
||||
<id>{{ feed_url | safe }}</id>
|
||||
{%- for page in pages %}
|
||||
<entry xml:lang="{{ page.lang }}">
|
||||
<title>{{ page.title }}</title>
|
||||
<published>{{ page.date | date(format="%+") }}</published>
|
||||
<updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated>
|
||||
<author>
|
||||
<name>
|
||||
{%- if page.authors -%}
|
||||
{{ page.authors[0] }}
|
||||
{%- 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>
|
||||
|
|
|
@ -6,25 +6,35 @@
|
|||
{%- elif section.title %} - {{ section.title }}
|
||||
{%- endif -%}
|
||||
</title>
|
||||
<link>{%- if section -%}
|
||||
{{ section.permalink | escape_xml | safe }}
|
||||
{%- else -%}
|
||||
{{ config.base_url | escape_xml | safe }}
|
||||
{%- endif -%}
|
||||
</link>
|
||||
<description>{{ config.description }}</description>
|
||||
<generator>Zola</generator>
|
||||
<language>{{ lang }}</language>
|
||||
<atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
|
||||
<lastBuildDate>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
|
||||
{%- for page in pages %}
|
||||
<item>
|
||||
<title>{{ page.title }}</title>
|
||||
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
|
||||
<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 %}
|
||||
<link>
|
||||
{%- if section -%}
|
||||
{{ section.permalink | escape_xml | safe }}
|
||||
{%- else -%}
|
||||
{{ config.base_url | escape_xml | safe }}
|
||||
{%- endif -%}
|
||||
</link>
|
||||
<description>{{ config.description }}</description>
|
||||
<generator>Zola</generator>
|
||||
<language>{{ lang }}</language>
|
||||
<atom:link href="{{ feed_url | safe }}" rel="self" type="application/rss+xml"/>
|
||||
<lastBuildDate>{{ last_updated | date(format="%a, %d %b %Y %H:%M:%S %z") }}</lastBuildDate>
|
||||
{%- for page in pages %}
|
||||
<item>
|
||||
<title>{{ page.title }}</title>
|
||||
<pubDate>{{ page.date | date(format="%a, %d %b %Y %H:%M:%S %z") }}</pubDate>
|
||||
<author>
|
||||
{%- if page.authors -%}
|
||||
{{ page.authors[0] }}
|
||||
{%- 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>
|
||||
</rss>
|
||||
|
|
|
@ -126,6 +126,10 @@ path = ""
|
|||
# current one. This takes an array of paths, not URLs.
|
||||
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
|
||||
# `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.
|
||||
|
|
|
@ -11,6 +11,8 @@ taxonomies = [
|
|||
|
||||
ignored_content = ["*/ignored.md"]
|
||||
|
||||
author = "config@example.com (Config Author)"
|
||||
|
||||
[markdown]
|
||||
highlight_code = true
|
||||
highlight_theme = "custom_gruvbox"
|
||||
|
|
|
@ -2,4 +2,5 @@
|
|||
title = "A transparent page"
|
||||
description = ""
|
||||
date = 2018-10-10
|
||||
authors = ["page@example.com (Page Author)"]
|
||||
+++
|
||||
|
|
Loading…
Reference in a new issue