Improve HTML for robot consumtion (#24)

* Fix icalendar validation errors

* Add RSS feed to documents

* Add stuff to meta: opengraph, canonical

* Add SEO robots meta elements

* Fix correct page titles

* Add more semantics to HTML

* Remove breadcrumbs from templates

* Render breadcrumbs in layout

Each controller should provide their own breadcrumb
trail as a list of tuples, where each tuple is the
pair of a slugified key and a human readable label.

Example:

[{"blog", "Webblogg"}]
[{"blog", "Webblogg"}, "2024", "2024"]

* Add CSS util class to show content only to screen readers

* Load interactive event map only on events page

* Decrease home logo size

* Use correct HTML element for time

* Improve Home page HTML semantics

* Add Person RFDa to footer

* Add RDFa to articles: annual, item, articles

* Enrich links semantics using RDFa

* Enrich Page semantics using RDFa

* Enrich Album semantics using RFDa

* Enrich Event semantics with RDFa
This commit is contained in:
Anders Englöf Ytterström 2024-10-16 15:40:53 +02:00 committed by GitHub
parent ef937ca0eb
commit 57e935ec00
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 648 additions and 520 deletions

View file

@ -81,6 +81,11 @@ section {
}
}
.sr-only {
position: absolute;
left: -999em;
}
.flx {
display: flex;
justify-content: space-between;

View file

@ -43,7 +43,7 @@ defmodule Mse25.Directus do
end
def get_album(externalId) do
get_item(
case get_item(
:albums,
externalId,
[
@ -52,7 +52,21 @@ defmodule Mse25.Directus do
"songs.artist.name"
]
|> Enum.join(",")
)
) do
{:ok,
data = %{
"album" => album,
"year" => year,
"songs" => [%{"artist" => %{"name" => artist}} | _]
}} ->
{:ok,
data
|> Map.put("artist", artist)
|> Map.put("summary", "#{artist} - #{album} (#{to_string(year)})")}
not_found ->
not_found
end
end
def get_albums!(options \\ []) do
@ -120,6 +134,7 @@ defmodule Mse25.Directus do
"fields=" <>
Enum.join(
[
"id",
"title",
"lead",
"slug",
@ -128,6 +143,7 @@ defmodule Mse25.Directus do
"started_at",
"ended_at",
"contents",
"date_created",
"bands.artists_id.name",
"mia.artists_id.name",
"location.*"

View file

@ -1,11 +1,4 @@
defmodule Mse25.EventHelpers do
def bandlist(bands) do
bands
|> Enum.map(fn b -> b["artists_id"]["name"] end)
|> Enum.join(", ")
|> String.replace(~r/, ([^,]+?)$/, " och \\1")
end
def hilights?(%{"bands" => bands, "category" => category}) do
_festival_band?(bands, category)
end
@ -30,4 +23,18 @@ defmodule Mse25.EventHelpers do
def _festival_band?(_b, _c) do
false
end
def bandlist(bands) do
bands
|> Enum.map(fn b -> b["artists_id"]["name"] end)
|> Enum.join(", ")
|> String.replace(~r/, ([^,]+?)$/, " och \\1")
end
def rdfa_bandlist(bands) do
bands
|> Enum.map(fn b -> "<span property=\"performer\">#{b["artists_id"]["name"]}</span>" end)
|> Enum.join(", ")
|> String.replace(~r/, ([^,]+?)$/, " och \\1")
end
end

View file

@ -1,14 +1,114 @@
defmodule Mse25Web.Layouts do
@moduledoc """
This module holds different layouts used by your application.
See the `layouts` directory for all templates available.
The "root" layout is a skeleton rendered as part of the
application router. The "app" layout is set as the default
layout on both `use Mse25Web, :controller` and
`use Mse25Web, :live_view`.
"""
use Mse25Web, :html
@url "https://madr.se"
@list_views ["webblogg", "delningar", "evenemang"]
embed_templates "layouts/*"
def canonical(%{year: _, conn: %{path_info: path}}) do
~s"""
<link rel="canonical" href="#{@url}/#{Enum.join(path, "/")}" />
"""
end
def canonical(_) do
""
end
def opengraph(%{heading: title, lead: lead, conn: %{path_info: path}}) do
~s"""
<meta property="og:title" content="#{title}" />
<meta property="og:description" content="#{lead}" />
<meta property="og:type" content="event" />
<meta property="og:url" content="#{@url}/#{Enum.join(path, "/")}" />
<meta property="og:site_name" content="madr.se" />
"""
end
def opengraph(%{heading: title, conn: %{path_info: path}}) do
~s"""
<meta property="og:title" content="#{title}" />
<meta property="og:type" content="article" />
<meta property="og:url" content="#{@url}/#{Enum.join(path, "/")}" />
<meta property="og:site_name" content="madr.se" />
"""
end
def opengraph(%{page_title: title, conn: %{path_info: path}}) do
~s"""
<meta property="og:title" content="#{title}" />
<meta property="og:type" content="page" />
<meta property="og:url" content="#{@url}/#{Enum.join(path, "/")}" />
<meta property="og:site_name" content="madr.se" />
"""
end
def robots(%{conn: %{path_info: [first | []]}}) do
case Integer.parse(first) do
:error ->
case Enum.member?(@list_views, first) do
true ->
~s"""
<meta name="robots" content="noindex,follow" />
"""
false ->
~s"""
<meta name="robots" content="index,follow" />
"""
end
{_i, _d} ->
~s"""
<meta name="robots" content="noindex,follow" />
"""
end
end
def robots(%{conn: %{path_info: [_p, _c]}}) do
~s"""
<meta name="robots" content="index,follow" />
"""
end
def robots(_) do
~s"""
<meta name="robots" content="noindex,follow" />
"""
end
def breadcrumbs(nodes) do
breadcrumbs([], "", 1, nodes)
end
def breadcrumbs(seen, _path, _index, []) do
Enum.reverse(seen)
end
def breadcrumbs(seen, path, index, [{slug, name} | nodes]) do
breadcrumbs(
[{index + 1, {path <> "/" <> to_string(slug), name}} | seen],
path <> "/" <> to_string(slug),
index + 1,
nodes
)
end
def breadcrumbs(seen, path, index, [{slug, name, custom_prefix} | nodes]) do
breadcrumbs(
[{index + 1, {custom_prefix <> "/" <> to_string(slug), name}} | seen],
path <> "/" <> to_string(slug),
index + 1,
nodes
)
end
def show_interactive_event_map?(assigns) do
Map.has_key?(assigns, :events)
end
def show_footer?(%{heading: "Kolofon"}), do: false
def show_footer?(%{}), do: true
end

View file

@ -1,3 +1,39 @@
<main>
<a href="#content" class="skiplink">Hoppa till innehållet</a>
<nav role="menu">
<div>
<span class="sr-only">Du är här:</span>
<span class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<span itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</span>
<%= for {index, {parent_slug, parent_name}} <- breadcrumbs(@breadcrumbs) do %>
<span class="sr-only">&gt;</span>
<span itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href={parent_slug}>
<span itemprop="name"><%= parent_name %></span>
</a>
<meta itemprop="position" content={index} />
</span>
<% end %>
</span>
</div>
</nav>
<main id="content" role="main">
<%= @inner_content %>
</main>
<%= if show_footer?(assigns) do %>
<footer role="contentinfo">
<p vocab="https://schema.org/" typeof="Person">
<a href="https://madr.se" property="url">madr.se</a>
är <span property="name">Anders Englöf Ytterström</span>s hemsida. Anders är <span property="jobTitle">webbutvecklare</span>, linuxentusiast, ljudtekniker och hårdrockare, bosatt i <span
property="address"
typeof="PostalAddress"
><span property="addressLocality">Borlänge</span> (<span property="addressRegion">Dalarna</span>)</span>.
Läs <a href="/colophon">kolofonen</a>.
</p>
</footer>
<% end %>

View file

@ -4,12 +4,25 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="csrf-token" content={get_csrf_token()} />
<title><%= assigns[:page_title] || "Anders Englöf Ytterström" %> | madr.se</title>
<title><%= assigns.page_title || "Anders Englöf Ytterström" %> | madr.se</title>
<link rel="stylesheet" href={~p"/assets/app.css"} />
<link
href="/prenumerera.xml"
type="application/rss+xml"
rel="alternate"
title="madr.se: inlägg, evenemang, delningar"
/>
<%= canonical(assigns) |> raw %>
<%= opengraph(assigns) |> raw %>
<%= robots(assigns) |> raw %>
</head>
<body class="bg-white">
<body>
<%= @inner_content %>
<script src={~p"/assets/app.js"}>
</script>
<%= if show_interactive_event_map?(assigns) do %>
<script src="/event-map.js">
</script>
<% end %>
</body>
</html>

View file

@ -19,10 +19,12 @@ defmodule Mse25Web.FeedController do
conn |> put_resp_content_type("text/calendar"),
Directus.get_events!(upcoming: true, limit: 9999)
|> Enum.map(fn %{
"id" => id,
"title" => title,
"lead" => lead,
"started_at" => starts_at,
"ended_at" => ends_at,
"date_created" => created_at,
"location" => %{
"name" => venue,
"address" => region,
@ -32,6 +34,7 @@ defmodule Mse25Web.FeedController do
}
} ->
%{
id: id,
title: title,
lead: lead,
region: region,
@ -39,6 +42,8 @@ defmodule Mse25Web.FeedController do
latitude: lat,
longitude: lng,
all_day?: true,
updated_at: created_at |> String.slice(0..18) |> String.replace(~r/[-:]/, ""),
created_at: created_at |> String.slice(0..18) |> String.replace(~r/[-:]/, ""),
starts_at: String.replace(starts_at, "-", ""),
ends_at: String.replace(ends_at, "-", "")
}

View file

@ -23,14 +23,31 @@ defmodule Mse25Web.FeedView do
~s"""
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//https://madr.se//kommande-evenemang
PRODID:-//https://madr.se//kommande-evenemang.ics//SE
CALSCALE:GREGORIAN
X-ORIGINAL-URL:https://madr.se
X-WR-CALDESC: Kommande evenemang, madr.se
METHOD:PUBLISH
#{upcoming |> Enum.map(fn %{title: title, starts_at: starts_at, ends_at: ends_at, longitude: longitude, latitude: latitude, lead: lead, venue: venue, region: region} -> ~s"""
REFRESH-INTERVAL;VALUE=DURATION:PT1H
X-Robots-Tag:noindex
X-PUBLISHED-TTL:PT1H
BEGIN:VTIMEZONE
TZID:CEST
BEGIN:STANDARD
TZOFFSETFROM:+0200
TZOFFSETTO:+0200
TZNAME:CEST
DTSTART:20000630T000000
END:STANDARD
END:VTIMEZONE
#{upcoming |> Enum.map(fn %{id: id, title: title, created_at: created_at, starts_at: starts_at, ends_at: ends_at, longitude: longitude, latitude: latitude, lead: lead, venue: venue, region: region} -> ~s"""
BEGIN:VEVENT
UID:#{title}.#{starts_at}@madr.se
DTSTAMP:#{starts_at}T000000
DTSTART;VALUE=DATE:#{starts_at}
DTEND;VALUE=DATE:#{ends_at}
UID:#{starts_at}.#{id}@madr.se
DTSTAMP:#{created_at}
CREATED:#{created_at}
LAST-MODIFIED:#{created_at}
DTSTART;TZID=CEST:#{starts_at}T060606
DTEND;TZID=CEST:#{ends_at}T060606
SUMMARY:#{title}
DESCRIPTION:#{lead}
LOCATION:#{venue}\, #{region}

View file

@ -73,6 +73,7 @@ defmodule Mse25Web.ItemController do
do: [
year: year,
page_title: "Innehåll från " <> to_string(year),
breadcrumbs: [{year, year}],
timeline: timeline,
brutal_legends_count: Map.get(counts, :albums, 0),
article_count: Map.get(counts, :articles, 0),
@ -86,8 +87,11 @@ defmodule Mse25Web.ItemController do
"pubDate" => published_at,
"date_updated" => updated_at
}) do
year = String.slice(published_at, 0..3)
[
page_title: heading,
breadcrumbs: [{"webblogg", "Webblogg"}, {year, year, ""}],
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at,
@ -96,7 +100,7 @@ defmodule Mse25Web.ItemController do
nil -> published_at
ua -> String.slice(ua, 0..9)
end,
year: String.slice(published_at, 0..3)
year: year
]
end
@ -104,22 +108,28 @@ defmodule Mse25Web.ItemController do
"title" => heading,
"contents" => contents,
"started_at" => started_at,
"ended_at" => ended_at,
"lead" => lead,
"poster" => poster,
"bands" => bands,
"mia" => mia,
"category" => category
}) do
year = String.slice(started_at, 0..3)
[
page_title: heading,
breadcrumbs: [{"evenemang", "Evenemang"}, {year, year, ""}],
heading: heading,
contents: Earmark.as_html!(contents),
lead: lead,
year: String.slice(started_at, 0..3),
year: year,
poster: poster,
bands: bands,
mia: mia,
category: category
category: category,
started_at: started_at,
ended_at: ended_at
]
end
@ -131,14 +141,17 @@ defmodule Mse25Web.ItemController do
"source" => url,
"h1" => title
}) do
year = String.slice(published_at, 0..3)
[
page_title: heading,
breadcrumbs: [{"delningar", "Delningar"}, {year, year, ""}],
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at,
url: url,
title: title,
year: String.slice(published_at, 0..3),
year: year,
updated_at:
case updated_at do
nil -> published_at
@ -153,6 +166,8 @@ defmodule Mse25Web.ItemController do
"date_updated" => updated_at
}) do
[
page_title: heading,
breadcrumbs: [],
heading: heading,
contents: Earmark.as_html!(contents),
updated_at: String.slice(updated_at, 0..9)
@ -166,18 +181,24 @@ defmodule Mse25Web.ItemController do
"cover" => cover,
"purchased_at" => purchased_at,
"externalId" => count,
"songs" => songs
"songs" => songs,
"summary" => summary,
"artist" => artist
}) do
purchase_year = String.slice(purchased_at, 0..3)
[
page_title: summary,
breadcrumbs: [{purchase_year, purchase_year}],
count: count,
page_title: album,
album: album,
cover: cover,
year: to_string(year),
purchase_year: String.slice(purchased_at, 0..3),
purchase_year: purchase_year,
contents: Earmark.as_html!(contents),
songs: Enum.map(songs, fn %{"title" => name} -> "\"" <> name <> "\"" end),
artist: List.first(songs) |> Map.get("artist") |> Map.get("name")
artist: artist,
summary: summary
]
end
end

View file

@ -1,21 +1,7 @@
<article class="article">
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href={"/" <> @purchase_year}>
<span itemprop="name"><%= @purchase_year %></span>
</a>
<meta itemprop="position" content="2" />
</li>
</ol>
<h1><%= @artist <> " - " <> @album <> " (" <> @year <> ")" %></h1>
</header>
<article class="album" vocab="https://schema.org/" typeof="MusicAlbum Review">
<h1>
<%= @summary %>
</h1>
<p>
Som jag tidigare skrivit om har jag ett roligt projekt pågående: att äga alla låtar i spelet Brütal Legend äldre än 1990 på vinyl, där det är möjligt.
@ -23,18 +9,23 @@
<div class="brutal-legend">
<p>
#<%= @count %>: <%= @artist %> - <%= csl(@songs) %>, från <%= @album %> (<%= @year %>)
#<%= @count %>: <span property="byArtist"><%= @artist %></span>
- <%= csl(@songs) %>, från <span property="name"><%= @album %></span>
(<span property="copyrightYear"><%= @year %></span>)
</p>
<%= if @cover do %>
<img
src={"https://n.madr.se/assets/" <> @cover <> "?key=rectangular"}
property="image"
src={"https://n.madr.se/assets/" <> @cover <> "?key=cover"}
alt="Skivomslag"
loading="lazy"
height="75"
width="75"
height="333"
width="333"
/>
<% end %>
</div>
<div property="reviewBody">
<%= raw(@contents) %>
</div>
</article>

View file

@ -1,12 +1,3 @@
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
</ol>
<h1><%= @page_title %></h1>
<ul>
<li><a href={"/" <> to_string(@year - 1)}>Tillbaka till <%= @year - 1 %></a></li>
@ -38,44 +29,55 @@
<section id={"m" <> month}>
<h2><%= month_name(month) <> ", " <> to_string(@year) %></h2>
<%= for item = %{t: t} <- items do %>
<article>
<%= if t == :articles do %>
<h3>
<article class="article" vocab="https://schema.org/" typeof="Article">
<h3 property="name">
<a href={"/" <> item["slug"]}>
<%= item["title"] %>
</a>
</h3>
<time property="datePublished"><%= item["pubDate"] %></time>
</article>
<% end %>
<%= if t == :events do %>
<article class="event" vocab="https://schema.org/" typeof="Event">
<h3>
<a href={"/" <> item["slug"]}>
<a property="name" href={"/" <> item["slug"]}>
<%= item["title"] %>
</a>
</h3>
<p><%= item["lead"] %></p>
<p property="description"><%= item["lead"] %></p>
<%= if item["poster"] do %>
<img
property="thumbnail"
src={ "https://n.madr.se/assets/" <> item["poster"] <> "?key=poster"}
loading="lazy"
alt="Affisch"
width="200"
/>
<% end %>
</article>
<% end %>
<%= if t == :links do %>
<article vocab="https://schema.org/" typeof="WebContent Review" class="bookmark">
<h3>
<%= item["title"] %>
<span property="name"><%= item["title"] %></span>
<a class="permalink" href={"/" <> item["slug"]} title="Permalänk">#</a>
</h3>
<p><%= raw(Earmark.as_html!(item["contents"])) %></p>
Källa:
<a href={"/" <> item["source"]}>
<%= item["h1"] %>
</a>
<div property="reviewBody">
<%= item["contents"] |> Earmark.as_html!() |> raw %>
</div>
<div class="source">
Källa: <a href={item["source"]} rel="external"><%= item["h1"] %></a>
</div>
</article>
<% end %>
<%= if t == :albums do %>
<article class="album" vocab="https://schema.org/" typeof="MusicAlbum">
<h3>
<%= item["artist"] <>
" - " <> item["album"] <> " (" <> to_string(item["year"]) <> ")" %>
<span property="byArtist"><%= @artist %></span>
- <%= csl(@songs) %>, från <span property="name"><%= @album %></span>
(<span property="copyrightYear"><%= @year %></span>)
<a
class="permalink"
href={"/" <> to_string(@year) <> "/brutal-legend-" <> item["externalId"]}
@ -83,9 +85,6 @@
#
</a>
</h3>
<%= if item["contents"] do %>
<p><%= raw(Earmark.as_html!(item["contents"])) %></p>
<% end %>
<ul>
<%= for song <- item["songs"] do %>
<li><%= song["artist"]["name"] <> " - " <> song["title"] %></li>
@ -93,6 +92,7 @@
</ul>
<%= if item["cover"] do %>
<img
property="thumbnail"
src={"https://n.madr.se/assets/" <> item["cover"] <> "?key=rectangular"}
alt="Skivomslag"
loading="lazy"
@ -100,9 +100,8 @@
width="75"
/>
<% end %>
<% end %>
</article>
<% end %>
<% end %>
</section>
<% end %>
</header>

View file

@ -1,33 +1,15 @@
<article class="article">
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/webblogg">
<span itemprop="name">Webblogg</span>
</a>
<meta itemprop="position" content="2" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href={"/" <> @year}>
<span itemprop="name"><%= @year %></span>
</a>
<meta itemprop="position" content="3" />
</li>
</ol>
<h1><%= @heading %></h1>
</header>
<article class="article" vocab="https://schema.org/" typeof="Article">
<h1 property="name"><%= @heading %></h1>
<div property="articleBody">
<%= raw(@contents) %>
</div>
<footer>
<p>
Publicerad <%= @published_at %> <br />och senast uppdaterad <%= @updated_at %>
Publicerad <time property="datePublished"><%= @published_at %></time>
av <span property="publisher">Anders Englöf Ytterström</span>, <br /> senast uppdaterad
<time property="dateModified"><%= @updated_at %></time>
</p>
</footer>
</article>

View file

@ -1,45 +1,38 @@
<article class="article">
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/evenemang">
<span itemprop="name">Evenemang</span>
</a>
<meta itemprop="position" content="2" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href={"/" <> @year}>
<span itemprop="name"><%= @year %></span>
</a>
<meta itemprop="position" content="3" />
</li>
</ol>
<h1><%= @heading %></h1>
</header>
<article class="event" vocab="https://schema.org/" typeof="Event Review">
<h1 property="name"><%= @heading %></h1>
<ul>
<li><%= @lead %></li>
<li property="description"><%= @lead %></li>
<li class="sr-only">
<span property="startDate"><%= @started_at %></span><span property="endDate"><%= @ended_at %></span>
</li>
<%= if opening_acts?(%{"bands" => @bands, "category" => @category}) do %>
<li>Huvudakt: <%= @bands |> List.first() |> Map.get("artists_id") |> Map.get("name") %></li>
<li>Förband: <%= @bands |> Enum.drop(1) |> bandlist() %></li>
<li>
Huvudakt:
<span property="performer">
<%= @bands |> List.first() |> Map.get("artists_id") |> Map.get("name") %>
</span>
</li>
<li>Förband: <%= @bands |> Enum.drop(1) |> rdfa_bandlist() |> raw %></li>
<% end %>
<%= if hilights?(%{"bands" => @bands, "category" => @category}) do %>
<li>Personliga höjdpunkter: <%= @bands |> bandlist() %></li>
<li>Personliga höjdpunkter: <%= @bands |> rdfa_bandlist() |> raw %></li>
<% end %>
<%= if missed?(%{"mia" => @mia, "category" => @category}) do %>
<li>Band jag missade: <%= @mia |> bandlist() %></li>
<% end %>
</ul>
<div property="reviewBody">
<%= raw(@contents) %>
</div>
<%= if @poster do %>
<img src={"https://n.madr.se/assets/" <> @poster} alt="affisch" loading="lazy" />
<img
property="image"
src={"https://n.madr.se/assets/" <> @poster <> "?key=poster"}
alt="affisch"
loading="lazy"
/>
<% end %>
</article>

View file

@ -1,35 +1,16 @@
<article class="article">
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/delningar">
<span itemprop="name">Delningar</span>
</a>
<meta itemprop="position" content="2" />
</li>
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href={"/" <> @year}>
<span itemprop="name"><%= @year %></span>
</a>
<meta itemprop="position" content="3" />
</li>
</ol>
<h1><%= @heading %></h1>
</header>
<article vocab="https://schema.org/" typeof="WebContent Review" class="bookmark">
<h1 property="name"><%= @heading %></h1>
<div property="reviewBody">
<%= raw(@contents) %>
<p>
Källa: <a href={@url} rel="external"><%= @title %></a>
</p>
</div>
<div class="source">
Källa: <a href={@url} property="url"><span property="headline"><%= @title %></span></a>
</div>
<footer>
<p>
Publicerad <%= @published_at %> <br />och senast uppdaterad <%= @updated_at %>
Publicerad <time property="datePublished archivedAt"><%= @published_at %></time>
av <span property="author">Anders Englöf Ytterström</span>,<br />Senast uppdaterad
<time property="dateModified"><%= @updated_at %></time>
</p>
</footer>
</article>

View file

@ -1,19 +1,13 @@
<article>
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
</ol>
<h1><%= @heading %></h1>
</header>
<article class="article" vocab="https://schema.org/" typeof="Article">
<h1 property="name"><%= @heading %></h1>
<div property="articleBody">
<%= raw(@contents) %>
</div>
<footer>
<p>Senast uppdaterad <%= @updated_at %></p>
<p>
Senast uppdaterad <time property="dateModified"><%= @updated_at %></time>
</p>
</footer>
</article>

View file

@ -38,6 +38,7 @@ defmodule Mse25Web.PageController do
render(conn, :search,
q: query,
breadcrumbs: [],
page_title: scount <> " sökresultat för \"" <> query <> "\"",
results: results
)
@ -48,19 +49,20 @@ defmodule Mse25Web.PageController do
end
def articles(conn, params) do
articles =
{articles, page_title} =
case params do
%{"q" => query_string} ->
Directus.get_articles!(limit: @almost_infinity, query: query_string)
{Directus.get_articles!(limit: @almost_infinity, query: query_string),
"Webblogg: \"#{query_string}\""}
_ ->
Directus.get_articles!(limit: @almost_infinity)
{Directus.get_articles!(limit: @almost_infinity), "Webblogg"}
end
|> group_annually
render(conn, :articles,
page_title: "Webblogg",
articles: articles,
page_title: page_title,
breadcrumbs: [{"webblogg", "Webblogg"}],
articles: group_annually(articles),
q: params["q"],
nosearch?: params["q"] == nil or params["q"] == ""
)
@ -81,6 +83,8 @@ defmodule Mse25Web.PageController do
render(conn, :events,
page_title: title,
breadcrumbs: [],
show_interactive_event_map?: true,
contents: Earmark.as_html!(contents),
events: events,
q: params["q"],
@ -93,6 +97,7 @@ defmodule Mse25Web.PageController do
render(conn, :links,
page_title: "Delningar",
breadcrumbs: [],
links: links
)
end

View file

@ -1,13 +1,6 @@
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
</ol>
<h1>Webblogg</h1>
<h1>
<%= @page_title %>
</h1>
<p>
Inlägg skrivna sedan 2006.
<%= if @nosearch? do %>
@ -36,14 +29,13 @@
<h2 class="sticky"><%= year %></h2>
<div class="articles">
<%= for article <- articles do %>
<article>
<h2>
<article class="article" vocab="https://schema.org/" typeof="Article">
<h2 property="name">
<a href={"/" <> article["slug"]}><%= article["title"] %></a>
</h2>
<date><%= article["pubDate"] %></date>
<time><%= article["pubDate"] %></time>
</article>
<% end %>
</div>
</section>
<% end %>
</header>

View file

@ -1,14 +1,5 @@
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
</ol>
<h1><%= @page_title %></h1>
</header>
<%= raw(@contents) %>
<section id="map">
<h2>Geografisk utspridning</h2>
@ -43,14 +34,14 @@
<h2 class="sticky"><%= year %></h2>
<div class="events">
<%= for event <- events do %>
<article>
<article class="event" vocab="https://schema.org/" typeof="Event">
<h2>
<a href={"/" <> event["slug"]}><%= event["title"] %></a>
<a property="name" href={"/" <> event["slug"]}><%= event["title"] %></a>
</h2>
<p><%= event["lead"] %></p>
<p property="description"><%= event["lead"] %></p>
<%= if hilights?(event) do %>
<p>
Personliga höjdpunkter: <%= bandlist(event["bands"]) %>
Personliga höjdpunkter: <%= rdfa_bandlist(event["bands"]) |> raw %>
</p>
<% end %>
<%= if missed?(event) do %>
@ -60,13 +51,14 @@
<% end %>
<%= if opening_acts?(event) do %>
<p>
Förband: <%= event["bands"] |> Enum.drop(1) |> bandlist() %>
Förband: <%= event["bands"] |> Enum.drop(1) |> rdfa_bandlist() |> raw %>
</p>
<% end %>
<%= if event["poster"] do %>
<img
property="thumbnail"
loading="lazy"
src={"https://n.madr.se/assets/" <> event["poster"]}
src={"https://n.madr.se/assets/" <> event["poster"] <> "?key=poster"}
alt="affisch"
width="150"
/>
@ -76,5 +68,3 @@
</div>
</section>
<% end %>
<script src="/event-map.js">
</script>

View file

@ -1,72 +1,72 @@
<main class="landing">
<img src={~p"/images/aey.svg"} width="300" alt="Anders Englöf Ytterström" />
<img src={~p"/images/aey.svg"} width="120" alt="Anders Englöf Ytterström" />
<form metod="get" action="/sok">
<label for="q">Sök innehåll</label>: <input size="9" type="search" id="q" name="q" />
<button>Sök</button>
</form>
<div class="tree">
<div>
Senast skrivet (<date><%= @recent_article["pubDate"] %></date>):<br />
<article class="article">
Senast skrivet (<time><%= @recent_article["pubDate"] %></time>):<br />
<a href={"/" <> @recent_article["slug"]}>
<%= @recent_article["title"] %>
</a>
</div>
<div>
Dessförinnan (<date><%= @older_article["pubDate"] %></date>):<br />
</article>
<article class="article">
Dessförinnan (<time><%= @older_article["pubDate"] %></time>):<br />
<a href={"/" <> @older_article["slug"]}>
<%= @older_article["title"] %>
</a>
</div>
<div>
</article>
<article class="page">
<a href="/webblogg">Webbloggen</a>
</div>
</article>
<%= for event <- @upcoming do %>
<div>
<article class="event">
Kommande: <a href={event["slug"]}><%= event["title"] %><br /><%= event["lead"] %></a>
</div>
</article>
<% end %>
<%= for event <- @recent_event do %>
<div>
<article class="event">
Upplevt: <a href={event["slug"]}><%= event["title"] %><br /><%= event["lead"] %></a>
</div>
</article>
<% end %>
<div>
<article class="feed events page">
<a href="/evenemang">Evenemangstidslinje</a>
</div>
<div>
</article>
<article class="feed ics">
<a href="/kommande-evenemang.ics">Kommande evenemang</a> (vcalendar)
</div>
<div>
</article>
<article class="feed links">
<a href="/delningar">
Delningar
</a>
</div>
</article>
<%= for legend <- @brutal_legends do %>
<div>
<article class="album">
Införskaffat (<%= legend["purchased_at"] %>):<br />
<a href={"/" <> legend["purchase_year"] <> "/brutal-legend-" <> legend["externalId"]}>
<%= legend["artist"] %> - <%= legend["album"] %> (<%= legend["year"] %>)
</a>
</div>
</article>
<% end %>
<div>
<article class="page">
<a href="/vad-jag-gor">
Vad jag gör
</a>
</div>
<div>
</article>
<article class="page">
Mer om:
<a href="/om">
Anders, 39, Hårdrockare
</a>
</div>
<div>
</article>
<article class="feed rss">
<a href="/prenumerera.xml">Prenumerera</a> (RSS 2.0)
</div>
<div>
</article>
<article class="page meta">
<a href="/colophon">
Kontakt &amp; Kolofon
</a>
</div>
</article>
</div>
</main>

View file

@ -1,12 +1,3 @@
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
</ol>
<h1>Delningar</h1>
<p>
Länkar som är värda att uppmärksammas och lämna åsikt om.
@ -56,12 +47,14 @@
|> String.replace(~r/ 0/, " ") %>
</h2>
<%= for link <- links do %>
<article>
<article vocab="https://schema.org/" typeof="WebContent Review" class="bookmark">
<h3>
<%= link["title"] %>
<span property="name"><%= link["title"] %></span>
<a class="permalink" href={"/" <> link["slug"]} title="Permalänk">#</a>
</h3>
<div property="reviewBody">
<%= link["contents"] |> Earmark.as_html!() |> raw %>
</div>
<div class="source">
Källa: <a href={link["source"]} rel="external"><%= link["h1"] %></a>
</div>
@ -70,4 +63,3 @@
</div>
</section>
<% end %>
</header>

View file

@ -1,15 +1,5 @@
<header>
<ol class="breadcrumbs" itemscope itemtype="https://schema.org/BreadcrumbList">
<li itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem">
<a href="/" rel="home">
<span itemprop="name">madr.se</span>
</a>
<meta itemprop="position" content="1" />
</li>
</ol>
<form metod="get" action="/sok">
<label for="q">Sök innehåll</label>:
<input size="9" type="search" id="q" name="q" value={@q} />
<label for="q">Sök innehåll</label>: <input size="9" type="search" id="q" name="q" value={@q} />
<button>Sök</button>
</form>
<h1><%= @page_title %></h1>
@ -79,4 +69,3 @@
<% end %>
</article>
<% end %>
</header>