Merge pull request 'Implement tags' (#4) from yocaml2-tags into main

Reviewed-on: robur/blog.robur.coop#4
This commit is contained in:
Reynir Björnsson 2024-10-16 09:45:46 +00:00
commit 33ef4926e2
6 changed files with 122 additions and 23 deletions

View file

@ -1,5 +1,7 @@
open Yocaml open Yocaml
module SM = Map.Make(String)
let is_empty_list = function [] -> true | _ -> false let is_empty_list = function [] -> true | _ -> false
module Date = struct module Date = struct
@ -414,6 +416,69 @@ module Articles = struct
] ]
end end
module Tag = struct
type t = {
name : string;
articles : (Path.t * Article.t) list;
}
let make ~name ~articles =
{ name; articles }
let normalize_article (ident, article) =
let open Data in
record (("url", string @@ Path.to_string ident) :: Article.normalize article)
let normalize { name; articles } =
let open Data in
[
("name", string name);
("articles", (list_of normalize_article) articles);
]
end
module Tags = struct
class type t = object ('self)
inherit Articles.t
method tags : Tag.t list
end
class tags ?title ?description articles =
object
inherit Articles.articles ?title ?description articles as super
method tags =
let tags =
let update article sm tag =
SM.update tag
(function
| None -> Some [article]
| Some urls -> Some (article :: urls))
sm
in
List.fold_left
(fun sm (url, article) ->
List.fold_left (update (url, article)) sm article#tags)
SM.empty
super#articles
|> SM.bindings
in
List.map (fun (tag, articles) ->
Tag.make ~name:tag ~articles)
tags
end
let of_articles articles =
new tags ?title:articles#title ?description:articles#description articles#articles
let normalize_tag tag =
let open Data in
record (Tag.normalize tag)
let normalize tags =
let open Data in
("all_tags", (list_of normalize_tag tags#tags)) :: Articles.normalize tags
end
module Make_with_target (S : sig module Make_with_target (S : sig
val source : Path.t val source : Path.t
val target : Path.t val target : Path.t
@ -427,6 +492,7 @@ struct
let images = Path.(source_root / "images") let images = Path.(source_root / "images")
let articles = Path.(source_root / "articles") let articles = Path.(source_root / "articles")
let index = Path.(source_root / "pages" / "index.md") let index = Path.(source_root / "pages" / "index.md")
let tags = Path.(source_root / "pages" / "tags.md")
let templates = Path.(source_root / "templates") let templates = Path.(source_root / "templates")
let template file = Path.(templates / file) let template file = Path.(templates / file)
let binary = Path.rel [ Sys.argv.(0) ] let binary = Path.rel [ Sys.argv.(0) ]
@ -507,6 +573,35 @@ struct
>>> drop_first () >>> drop_first ()
end end
let process_tags ~host =
let file = Source.tags in
let file_target = Target.(as_html pages file) in
let open Task in
let compute_index =
Articles.compute_index
(module Yocaml_yaml)
~where:(Path.has_extension "md")
~compute_link:(Target.as_html @@ Path.abs [ "articles" ])
Source.articles
in
Action.write_static_file file_target
begin
Pipeline.track_files [ Source.binary; Source.articles ]
>>> Yocaml_yaml.Pipeline.read_file_with_metadata (module Page) file
>>> Yocaml_cmarkit.content_to_html ()
>>> first compute_index
>>* (fun (obj, str) -> Eff.return (Tags.of_articles (obj#with_host host), str))
>>> Yocaml_jingoo.Pipeline.as_template ~strict:true
(module Tags)
(Source.template "tags.html")
>>> Yocaml_jingoo.Pipeline.as_template ~strict:true
(module Tags)
(Source.template "layout.html")
>>> drop_first ()
end
let feed_title = "The Robur's blog" let feed_title = "The Robur's blog"
let site_url = "https://blog.robur.coop/" let site_url = "https://blog.robur.coop/"
let feed_description = "The Robur cooperative blog" let feed_description = "The Robur cooperative blog"
@ -624,6 +719,7 @@ struct
let open Eff in let open Eff in
Action.restore_cache ~on:`Source Source.cache Action.restore_cache ~on:`Source Source.cache
>>= process_css_files >>= process_js_files >>= process_images_files >>= process_css_files >>= process_js_files >>= process_images_files
>>= process_tags ~host
>>= process_articles ~host >>= process_index ~host >>= rss1 >>= rss2 >>= atom >>= process_articles ~host >>= process_index ~host >>= rss1 >>= rss2 >>= atom
>>= Action.store_cache ~on:`Source Source.cache >>= Action.store_cache ~on:`Source Source.cache
end end

View file

@ -197,6 +197,10 @@ article code {
color: #fff; color: #fff;
} }
.tag-box:target > h3 > span {
background-color: #c2410c;
}
.tag-box > h3 > span::before { .tag-box > h3 > span::before {
content: "#"; content: "#";
} }

View file

@ -4,7 +4,7 @@
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
<ul class="tags-list"> <ul class="tags-list">
{%- for tag in tags -%} {%- for tag in tags -%}
<li>{{ tag }}</li> <li><a href="{{ host }}/tags.html#tag-{{ tag }}">{{ tag }}</a></li>
{%- endfor -%} {%- endfor -%}
</ul> </ul>
{%- autoescape false -%} {%- autoescape false -%}

View file

@ -26,7 +26,7 @@
<div class="bottom"> <div class="bottom">
<ul class="tags-list"> <ul class="tags-list">
{%- for tag in article.tags -%} {%- for tag in article.tags -%}
<li>{{ tag }}</li> <li><a href="{{ host }}/tags.html#tag-{{ tag }}">{{ tag }}</a></li>
{%- endfor -%} {%- endfor -%}
</ul> </ul>
</div> </div>

View file

@ -1,21 +0,0 @@
<a href="/index.html">Back to index</a>
<ul class="tags-list aeration">
{%- for tag in tags -%}
<li><a href="/{{ tag.link }}">{{ tag.name }} ({{ tag.number }})</a></li>
{%- endfor -%}
</ul>
<div class="tag-box" id="tag-{{ tag }}">
{%- set nb_tags = length (articles) -%}
<h3>
<span>{{ tag }}</span>
{{ nb_tags }}
{%- if nb_tags > 1 %} entries{%- else %} entry{%- endif -%}
</h3>
<ul>
{%- for article in articles -%}
<li><a href="/{{ article.url }}">{{ article.metadata.title }}</a></li>
{%- endfor -%}
</ul>
</div>

20
templates/tags.html Normal file
View file

@ -0,0 +1,20 @@
<a href="/index.html">Back to index</a>
<ul class="tags-list aeration">
{%- for tag in all_tags -%}
<li><a href="#tag-{{ tag.name }}">{{ tag.name }}</a></li>
{%- endfor -%}
</ul>
{%- for tag in all_tags -%}
<div class="tag-box" id="tag-{{ tag.name }}">
<h3>
<span>{{ tag.name }}</span>
</h3>
<ul>
{%- for article in tag.articles -%}
<li><a href="{{ host }}{{ article.url }}">{{ article.title }}</a></li>
{%- endfor -%}
</ul>
</div>
{%- endfor -%}