parent
047ce32efd
commit
2c681f3439
|
@ -6,12 +6,13 @@
|
|||
- Support `output_dir` in `config.toml`
|
||||
- Allow sections to be drafted
|
||||
- Allow specifying default language in filenames
|
||||
- Render emoji in Markdown content if the option is enabled
|
||||
- Render emoji in Markdown content if the `render_emoji` option is enabled
|
||||
- Enable YouTube privacy mode for the YouTube shortcode
|
||||
- Add language as class to the `<code>` block
|
||||
- Add bibtex to `load_data`
|
||||
- Add a `[markdown]` section to `config.toml` to configure rendering
|
||||
- Add `highlight_code` and `highlight_theme` to a `[markdown]` section in `config.toml`
|
||||
- Add `external_links_target_blank`, `external_links_no_follow` and `external_links_no_referrer`
|
||||
|
||||
## 0.12.2 (2020-09-28)
|
||||
|
||||
|
|
|
@ -12,6 +12,46 @@ pub struct Markdown {
|
|||
pub highlight_theme: String,
|
||||
/// Whether to render emoji aliases (e.g.: :smile: => 😄) in the markdown files
|
||||
pub render_emoji: bool,
|
||||
/// Whether external links are to be opened in a new tab
|
||||
/// If this is true, a `rel="noopener"` will always automatically be added for security reasons
|
||||
pub external_links_target_blank: bool,
|
||||
/// Whether to set rel="nofollow" for all external links
|
||||
pub external_links_no_follow: bool,
|
||||
/// Whether to set rel="noreferrer" for all external links
|
||||
pub external_links_no_referrer: bool,
|
||||
}
|
||||
|
||||
impl Markdown {
|
||||
pub fn has_external_link_tweaks(&self) -> bool {
|
||||
self.external_links_target_blank
|
||||
|| self.external_links_no_follow
|
||||
|| self.external_links_no_referrer
|
||||
}
|
||||
|
||||
pub fn construct_external_link_tag(&self, url: &str, title: &str) -> String {
|
||||
let mut rel_opts = Vec::new();
|
||||
let mut target = "".to_owned();
|
||||
let title = if title == "" { "".to_owned() } else { format!("title=\"{}\" ", title) };
|
||||
|
||||
if self.external_links_target_blank {
|
||||
// Security risk otherwise
|
||||
rel_opts.push("noopener");
|
||||
target = "target=\"_blank\" ".to_owned();
|
||||
}
|
||||
if self.external_links_no_follow {
|
||||
rel_opts.push("nofollow");
|
||||
}
|
||||
if self.external_links_no_referrer {
|
||||
rel_opts.push("noreferrer");
|
||||
}
|
||||
let rel = if rel_opts.is_empty() {
|
||||
"".to_owned()
|
||||
} else {
|
||||
format!("rel=\"{}\" ", rel_opts.join(" "))
|
||||
};
|
||||
|
||||
format!("<a {}{}{}href=\"{}\">", rel, target, title, url)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Markdown {
|
||||
|
@ -20,6 +60,9 @@ impl Default for Markdown {
|
|||
highlight_code: false,
|
||||
highlight_theme: DEFAULT_HIGHLIGHT_THEME.to_owned(),
|
||||
render_emoji: false,
|
||||
external_links_target_blank: false,
|
||||
external_links_no_follow: false,
|
||||
external_links_no_referrer: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ use utils::slugs::slugify_anchors;
|
|||
use utils::vec::InsertMany;
|
||||
|
||||
use self::cmark::{Event, LinkType, Options, Parser, Tag};
|
||||
use pulldown_cmark::CodeBlockKind;
|
||||
|
||||
mod codeblock;
|
||||
mod fence;
|
||||
|
@ -101,11 +100,6 @@ fn fix_link(
|
|||
return Ok(link.to_string());
|
||||
}
|
||||
|
||||
// TODO: remove me in a few versions when people have upgraded
|
||||
if link.starts_with("./") && link.contains(".md") {
|
||||
println!("It looks like the link `{}` is using the previous syntax for internal links: start with @/ instead", link);
|
||||
}
|
||||
|
||||
// A few situations here:
|
||||
// - it could be a relative link (starting with `@/`)
|
||||
// - it could be a link to a co-located asset
|
||||
|
@ -211,7 +205,7 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
}
|
||||
Event::Start(Tag::CodeBlock(ref kind)) => {
|
||||
let language = match kind {
|
||||
CodeBlockKind::Fenced(fence_info) => {
|
||||
cmark::CodeBlockKind::Fenced(fence_info) => {
|
||||
let fence_info = fence::FenceSettings::new(fence_info);
|
||||
fence_info.language
|
||||
}
|
||||
|
@ -228,8 +222,8 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
|
||||
let theme = &THEME_SET.themes[context.config.highlight_theme()];
|
||||
match kind {
|
||||
CodeBlockKind::Indented => (),
|
||||
CodeBlockKind::Fenced(fence_info) => {
|
||||
cmark::CodeBlockKind::Indented => (),
|
||||
cmark::CodeBlockKind::Fenced(fence_info) => {
|
||||
// This selects the background color the same way that
|
||||
// start_coloured_html_snippet does
|
||||
let color = theme
|
||||
|
@ -289,8 +283,22 @@ pub fn markdown_to_html(content: &str, context: &RenderContext) -> Result<Render
|
|||
return Event::Html("".into());
|
||||
}
|
||||
};
|
||||
|
||||
Event::Start(Tag::Link(link_type, fixed_link.into(), title))
|
||||
if is_external_link(&link)
|
||||
&& context.config.markdown.has_external_link_tweaks()
|
||||
{
|
||||
let mut escaped = String::new();
|
||||
// write_str can fail but here there are no reasons it should (afaik?)
|
||||
cmark::escape::escape_href(&mut escaped, &link).expect("Could not write to buffer");
|
||||
Event::Html(
|
||||
context
|
||||
.config
|
||||
.markdown
|
||||
.construct_external_link_tag(&escaped, &title)
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
Event::Start(Tag::Link(link_type, fixed_link.into(), title))
|
||||
}
|
||||
}
|
||||
Event::Html(ref markup) => {
|
||||
if markup.contains("<!-- more -->") {
|
||||
|
|
|
@ -1058,3 +1058,61 @@ fn emoji_aliases_are_ignored_when_disabled_in_config() {
|
|||
let res = render_content("Hello, World! :smile:", &context).unwrap();
|
||||
assert_eq!(res.body, "<p>Hello, World! :smile:</p>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn basic_external_links_unchanged() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let config = Config::default();
|
||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content("<https://google.com>", &context).unwrap();
|
||||
assert_eq!(res.body, "<p><a href=\"https://google.com\">https://google.com</a></p>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_set_target_blank_for_external_link() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.markdown.external_links_target_blank = true;
|
||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content("<https://google.com>", &context).unwrap();
|
||||
assert_eq!(res.body, "<p><a rel=\"noopener\" target=\"_blank\" href=\"https://google.com\">https://google.com</a></p>\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_set_nofollow_for_external_link() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.markdown.external_links_no_follow = true;
|
||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
// Testing href escaping while we're there
|
||||
let res = render_content("<https://google.com/éllo>", &context).unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
"<p><a rel=\"nofollow\" href=\"https://google.com/%C3%A9llo\">https://google.com/éllo</a></p>\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_set_noreferrer_for_external_link() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.markdown.external_links_no_referrer = true;
|
||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content("<https://google.com>", &context).unwrap();
|
||||
assert_eq!(
|
||||
res.body,
|
||||
"<p><a rel=\"noreferrer\" href=\"https://google.com\">https://google.com</a></p>\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_set_all_options_for_external_link() {
|
||||
let permalinks_ctx = HashMap::new();
|
||||
let mut config = Config::default();
|
||||
config.markdown.external_links_target_blank = true;
|
||||
config.markdown.external_links_no_follow = true;
|
||||
config.markdown.external_links_no_referrer = true;
|
||||
let context = RenderContext::new(&ZOLA_TERA, &config, "", &permalinks_ctx, InsertAnchor::None);
|
||||
let res = render_content("<https://google.com>", &context).unwrap();
|
||||
assert_eq!(res.body, "<p><a rel=\"noopener nofollow noreferrer\" target=\"_blank\" href=\"https://google.com\">https://google.com</a></p>\n");
|
||||
}
|
||||
|
|
|
@ -95,6 +95,7 @@ extra_syntaxes = []
|
|||
# You can override the default output directory `public` by setting an another value.
|
||||
# output_dir = "docs"
|
||||
|
||||
# Configuration of the Markdown rendering
|
||||
[markdown]
|
||||
# When set to "true", all code blocks are highlighted.
|
||||
highlight_code = false
|
||||
|
@ -107,6 +108,16 @@ highlight_theme = "base16-ocean-dark"
|
|||
# Unicode emoji equivalent in the rendered Markdown files. (e.g.: :smile: => 😄)
|
||||
render_emoji = false
|
||||
|
||||
# Whether external links are to be opened in a new tab
|
||||
# If this is true, a `rel="noopener"` will always automatically be added for security reasons
|
||||
external_links_target_blank = false
|
||||
|
||||
# Whether to set rel="nofollow" for all external links
|
||||
external_links_no_follow = false
|
||||
|
||||
# Whether to set rel="noreferrer" for all external links
|
||||
external_links_no_referrer = false
|
||||
|
||||
# Configuration of the link checker.
|
||||
[link_checker]
|
||||
# Skip link checking for external URLs that start with these prefixes
|
||||
|
|
Loading…
Reference in a new issue