Item views (#7)

* Add Earmark dependency

For markdown to HTML conversion

* Display items by slug: articles, pages

* Handle 401 Forbidden responses in Directus

* Add item controller as pokemon route

* Strip out generated layouts

* Plan future routes

* Look for item in order: article, link, event, album

* Add item templates
This commit is contained in:
Anders Englöf Ytterström 2024-09-06 22:41:12 +02:00 committed by GitHub
parent e907e347c9
commit 8bbe8a1b24
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 264 additions and 70 deletions

View file

@ -7,12 +7,16 @@ defmodule Mse25.Directus do
params = params =
[ [
"sort=-pubDate", "sort=-pubDate",
"fields=" <> Enum.join([ "fields=" <>
"slug", Enum.join(
"title", [
"date_updated", "slug",
"pubDate" "title",
], ",") "date_updated",
"pubDate"
],
","
)
] ]
|> query_params_string(options) |> query_params_string(options)
@ -41,18 +45,22 @@ defmodule Mse25.Directus do
params = params =
[ [
"sort=-started_at", "sort=-started_at",
#"filter={\"upcoming\":{\"_eq\":true}}", # "filter={\"upcoming\":{\"_eq\":true}}",
"fields=" <> Enum.join([ "fields=" <>
"started_at", Enum.join(
"ended_at", [
"title", "started_at",
"lead", "ended_at",
"poster.filename_download", "title",
"poster.width", "lead",
"poster.height", "poster.filename_download",
"bands.artists_id.name", "poster.width",
"mia.artists_id.name" "poster.height",
], ",") "bands.artists_id.name",
"mia.artists_id.name"
],
","
)
] ]
|> query_params_string(options) |> query_params_string(options)
@ -67,15 +75,19 @@ defmodule Mse25.Directus do
params = params =
[ [
"sort=-pubDate", "sort=-pubDate",
"fields=" <> Enum.join([ "fields=" <>
"slug", Enum.join(
"title", [
"date_updated", "slug",
"pubDate", "title",
"h1", "date_updated",
"source", "pubDate",
"contents" "h1",
], ",") "source",
"contents"
],
","
)
] ]
|> query_params_string(options) |> query_params_string(options)
@ -99,11 +111,16 @@ defmodule Mse25.Directus do
[base_url: base_url, token: token] = Application.fetch_env!(:mse25, :directus) [base_url: base_url, token: token] = Application.fetch_env!(:mse25, :directus)
req = Req.new(base_url: base_url <> "/items") req = Req.new(base_url: base_url <> "/items")
Req.get!(req, url: resource, auth: {:bearer, token}) case Req.get!(req, url: resource, auth: {:bearer, token})
|> payload |> payload do
{:ok, payload} -> payload
{:forbidden, message} -> message
end
end end
defp payload(%Req.Response{body: %{"data" => payload}}), do: payload defp payload(%Req.Response{status: 200, body: %{"data" => payload}}), do: {:ok, payload}
defp payload(%Req.Response{status: 401}), do: {:forbidden, "Invalid Directus credentials"}
defp query_params_string(params, options), defp query_params_string(params, options),
do: do:

View file

@ -1,32 +1,32 @@
<header class="px-4 sm:px-6 lg:px-8"> <b>madr</b><br>
<div class="flex items-center justify-between border-b border-zinc-100 py-3 text-sm"> |&gt; Hårdrock<br>
<div class="flex items-center gap-4"> |&gt; Programmering<br>
<a href="/"> |&gt; Ljudteknik<br>
<img src={~p"/images/logo.svg"} width="36" /> |&gt; Åsikter
</a>
<p class="bg-brand/5 text-brand rounded-full px-2 font-medium leading-6"> <nav>
v<%= Application.spec(:phoenix, :vsn) %> <ul>
</p> <li><a href="/om">Om mig</a></li>
</div> <li><a href="/webblogg">Webblogg</a></li>
<div class="flex items-center gap-4 font-semibold leading-6 text-zinc-900"> <li><a href="/delningar">Delningar</a></li>
<a href="https://twitter.com/elixirphoenix" class="hover:text-zinc-700"> <li><a href="/evenemang">Evenemang</a></li>
@elixirphoenix <li><a href="/vad-jag-gor">Vad jag gör</a></li>
</a> </ul>
<a href="https://github.com/phoenixframework/phoenix" class="hover:text-zinc-700"> </nav>
GitHub
</a> <form metod="get" action="/search">
<a <label>Search site <input type="search" name="q"></label>
href="https://hexdocs.pm/phoenix/overview.html" </form>
class="rounded-lg bg-zinc-100 px-2 py-1 hover:bg-zinc-200/80"
> <ul>
Get Started <span aria-hidden="true">&rarr;</span> <li><a href="https://github.com/madr" rel="external">Github</a></li>
</a> <li><a href="https://linkedin.com/anders-ytterstrom" rel="external">LinkedIn</a></li>
</div> <li><a href="https://discogs.com/madr" rel="external">Discogs</a></li>
</div> <li><a href="https://songkick.com/madr" rel="external">Songkick</a></li>
</header> </ul>
<main class="px-4 py-20 sm:px-6 lg:px-8">
<div class="mx-auto max-w-2xl"> <%= @inner_content %>
<.flash_group flash={@flash} />
<%= @inner_content %> <footer>
</div> <p><a href="https://madr.se" rel="home">madr.se</a> av Anders Englöf Ytterström, sedan 2006. <a href="/colophon">Kolofon</a>.</p>
</main> </footer>

View file

@ -1,15 +1,13 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="[scrollbar-gutter:stable]"> <html lang="sv">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content={get_csrf_token()} /> <meta name="csrf-token" content={get_csrf_token()}>
<.live_title suffix=" · Phoenix Framework"> <.live_title suffix=" · Phoenix Framework">
<%= assigns[:page_title] || "Mse25" %> <%= assigns[:page_title] || "Mse25" %>
</.live_title> </.live_title>
<link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} /> <link rel="stylesheet" href={~p"/assets/app.css"}>
<script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
</script>
</head> </head>
<body class="bg-white"> <body class="bg-white">
<%= @inner_content %> <%= @inner_content %>

View file

@ -0,0 +1,100 @@
defmodule Mse25Web.ItemController do
use Mse25Web, :controller
alias Mse25.Directus
def index(conn, _params) do
case conn.path_info |> fetch do
{:ok, item_type, item_data} ->
render(conn, item_type, assigns(item_type, item_data))
{:not_found, message} ->
render(conn, message)
end
end
defp fetch([year, slug], :article) do
case Directus.get_article(year <> "/" <> slug) do
{:ok, response} -> {:ok, :article, response}
_ -> fetch([year, slug], :link)
end
end
defp fetch([year, slug], :link) do
case Directus.get_link(year <> "/" <> slug) do
{:ok, response} -> {:ok, :link, response}
_ -> fetch([year, slug], :event)
end
end
defp fetch([year, slug], :event) do
case Directus.get_event(year <> "/" <> slug) do
{:ok, response} -> {:ok, :event, response}
not_found -> not_found
end
end
defp fetch([year, slug]) do
fetch([year, slug], :article)
end
defp fetch([slug]) do
case Directus.get_page(slug) do
{:ok, response} -> {:ok, :page, response}
not_found -> not_found
end
end
defp assigns(:article, %{
"title" => heading,
"contents" => contents,
"pubDate" => published_at,
"date_updated" => updated_at
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at,
updated_at: updated_at
]
end
defp assigns(:event, %{
"title" => heading,
"contents" => contents,
"started_at" => published_at
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at
]
end
defp assigns(:link, %{
"title" => heading,
"contents" => contents,
"pubDate" => published_at,
"source" => url,
"h1" => title
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
published_at: published_at,
url: url,
title: title
]
end
defp assigns(:page, %{
"title" => heading,
"contents" => contents,
"date_updated" => updated_at
}) do
[
heading: heading,
contents: Earmark.as_html!(contents),
updated_at: updated_at
]
end
end

View file

@ -0,0 +1,5 @@
defmodule Mse25Web.ItemHTML do
use Mse25Web, :html
embed_templates "item_html/*"
end

View file

@ -0,0 +1,12 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<footer>
<p>Skribent: Anders Englöf Ytterström. Publicerad <%= @published_at %> och senast uppdaterad <%= @updated_at %>.</p>
</footer>
</article>

View file

@ -0,0 +1,9 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
</article>

View file

@ -0,0 +1,14 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<p>
Källa: <a href={@url} rel="external"><%= @title %></a>
</p>
<footer>
<p>Skribent: Anders Englöf Ytterström. Publicerad <%= @published_at %>.</p>
</footer>
</article>

View file

@ -0,0 +1,11 @@
<article>
<header>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<footer>
<p>Skribent: Anders Englöf Ytterström. Senast uppdaterad <%= @updated_at %>.</p>
</footer>
</article>

View file

@ -0,0 +1,12 @@
<article>
<header>
<date><%= @published_at %></date>
<h1><%= @heading %></h1>
</header>
<%= raw @contents %>
<footer>
<p>Skribent: Anders Englöf Ytterström. Publicerad <%= @published_at %> och senast uppdaterad <%= @updated_at %>.</p>
</footer>
</article>

View file

@ -18,6 +18,20 @@ defmodule Mse25Web.Router do
pipe_through :browser pipe_through :browser
get "/", PageController, :home get "/", PageController, :home
# get "/evenemang", EventController, :index
# get "/kommande-evenemang.ics", EventController, :calendar
# get "/event-map.js", EventController, :interactive_map
# get "/webblogg", ArticleController, :index
# get "/webblogg/prenumerera.xml", ArticleController, :feed
# get "/delningar", ShareController, :index
# get "/delningar/prenumerera.xml", ShareController, :feed
# get "/:year", TimelineController, :annual
# get "/prenumerera.xml", TimelineController, :feed
get "/*path", ItemController, :index
end end
# Other scopes may use custom stacks. # Other scopes may use custom stacks.

View file

@ -45,7 +45,8 @@ defmodule Mse25.MixProject do
{:jason, "~> 1.2"}, {:jason, "~> 1.2"},
{:dns_cluster, "~> 0.1.1"}, {:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.5"}, {:bandit, "~> 1.5"},
{:req, "~> 0.5.0"} {:req, "~> 0.5.0"},
{:earmark, "~> 1.4"}
] ]
end end

View file

@ -2,6 +2,7 @@
"bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"}, "bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"}, "castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"}, "dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
"esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"}, "esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"},
"expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"}, "expo": {:hex, :expo, "1.0.1", "f9e2f984f5b8d195815d52d0ba264798c12c8d2f2606f76fa4c60e8ebe39474d", [:mix], [], "hexpm", "f250b33274e3e56513644858c116f255d35c767c2b8e96a512fe7839ef9306a1"},
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},