Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c22b52fda | ||
|
|
01504fb120 | ||
|
|
b25a5839b0 | ||
|
|
c4609f4370 |
Generated
+1
-1
@@ -2948,7 +2948,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "portal"
|
||||
version = "0.3.28"
|
||||
version = "0.3.31"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arc-swap",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "portal"
|
||||
version = "0.3.30"
|
||||
version = "0.3.32"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
+10
-1
@@ -1,5 +1,5 @@
|
||||
use leptos::prelude::*;
|
||||
use leptos_meta::{provide_meta_context, HashedStylesheet, Html, MetaTags, Stylesheet, Title};
|
||||
use leptos_meta::{provide_meta_context, HashedStylesheet, Html, Link, MetaTags, Stylesheet, Title};
|
||||
use leptos_router::{
|
||||
components::{Route, Router, Routes},
|
||||
hooks::{use_location, use_navigate, use_query_map},
|
||||
@@ -89,10 +89,19 @@ pub fn App() -> impl IntoView {
|
||||
.and_then(|s| s.stylesheet)
|
||||
.map(|p| format!("/site/{p}"))
|
||||
});
|
||||
// A content-shipped favicon overrides the built-in one; injected
|
||||
// into the head after the static defaults, so it wins.
|
||||
let favicon = Memo::new(move |_| {
|
||||
site.get()
|
||||
.and_then(|r| r.ok())
|
||||
.and_then(|s| s.favicon)
|
||||
.map(|p| format!("/site/{p}"))
|
||||
});
|
||||
|
||||
view! {
|
||||
<Html attr:lang=move || lang.get()/>
|
||||
{move || custom_css.get().map(|href| view! { <Stylesheet id="site-custom" href=href/> })}
|
||||
{move || favicon.get().map(|href| view! { <Link rel="icon" href=href/> })}
|
||||
<Suspense fallback=|| ()>
|
||||
{move || {
|
||||
site.get()
|
||||
|
||||
@@ -191,6 +191,7 @@ pub fn render_inline_markdown(text: &str) -> String {
|
||||
Event::Start(Tag::Link { dest_url, .. })
|
||||
if !(dest_url.starts_with("https://")
|
||||
|| dest_url.starts_with("mailto:")
|
||||
|| dest_url.starts_with("tel:")
|
||||
|| dest_url.starts_with('/')) =>
|
||||
{
|
||||
in_link += 1;
|
||||
@@ -610,6 +611,10 @@ pub struct SiteConfig {
|
||||
/// same-origin at `/site/<path>`; plain path only.
|
||||
#[serde(default)]
|
||||
pub stylesheet: Option<String>,
|
||||
/// A content-repo-relative favicon (svg/png/ico), served at
|
||||
/// `/site/<path>` and used in place of the built-in one.
|
||||
#[serde(default)]
|
||||
pub favicon: Option<String>,
|
||||
#[serde(default)]
|
||||
pub hero: HeroConfig,
|
||||
}
|
||||
@@ -672,6 +677,14 @@ impl SiteConfig {
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(icon) = &self.favicon {
|
||||
let ok = [".svg", ".png", ".ico"].iter().any(|e| icon.ends_with(e));
|
||||
if !is_safe_site_path(icon) || !ok {
|
||||
anyhow::bail!(
|
||||
"site.yaml: favicon {icon:?} must be a plain repo-relative .svg/.png/.ico path"
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1536,6 +1549,7 @@ pub async fn site_asset_handler(
|
||||
Some("png") => "image/png",
|
||||
Some("webp") => "image/webp",
|
||||
Some("avif") => "image/avif",
|
||||
Some("ico") => "image/x-icon",
|
||||
Some("woff2") => "font/woff2",
|
||||
_ => "application/octet-stream",
|
||||
};
|
||||
@@ -2146,6 +2160,10 @@ alternatives:
|
||||
fn markdown_drops_html_and_unsafe_links() {
|
||||
assert_eq!(render_inline_markdown("x <script>y</script> z"), "x y z");
|
||||
assert_eq!(render_inline_markdown("[bad](javascript:alert(1))"), "bad");
|
||||
assert_eq!(
|
||||
render_inline_markdown("[ring](tel:+4791180485)"),
|
||||
"<a href=\"tel:+4791180485\">ring</a>"
|
||||
);
|
||||
assert_eq!(
|
||||
render_inline_markdown(""),
|
||||
"<img src=\"https://x.no/a.jpg\" alt=\"site\" />"
|
||||
|
||||
Reference in New Issue
Block a user