source: code/trunk/cli/html.ml@ 14

Last change on this file since 14 was 5, checked in by fox, 3 years ago

Change output date format & use <ul> for latest

File size: 7.2 KB
RevLine 
[3]1type templates_t = { header: string option; footer: string option }
2type t = { templates : templates_t }
[2]3
[3]4let ext = ".htm"
5let empty_templates = { header = None; footer = None }
6let default_opts = { templates = empty_templates }
7
8let init kv =
9 let open Logarion in
10 let header = match Store.KV.find "HTM-header" kv with
11 | fname -> Some (File_store.to_string fname)
12 | exception Not_found -> None in
13 let footer = match Store.KV.find "HTM-footer" kv with
14 | fname -> Some (File_store.to_string fname)
15 | exception Not_found -> None in
16 { templates = { header; footer} }
17
18let wrap c htm text_title body =
19 let site_title = try Logarion.Store.KV.find "Title" c.Conversion.kv
20 with Not_found -> "" in
21 let replace x = let open Str in
22 global_replace (regexp "{{archive-title}}") site_title x
23 |> global_replace (regexp "{{text-title}}") text_title
24 in
25 let header = match htm.templates.header with
26 | Some x -> replace x
27 | None -> "<header><a href='.'>" ^ site_title ^
28 "</a><nav><a href='feed.atom' id='feed'>feed</a></nav></header>"
29 in
30 let footer = match htm.templates.footer with None -> "" | Some x -> replace x in
31 "<!DOCTYPE HTML><html><head><title>" ^ text_title ^ " • " ^ site_title ^ "</title>\n\
32 <link rel='stylesheet' href='main.css'>\
33 <link rel='alternate' href='feed.atom' type='application/atom+xml'>\
34 <meta charset='utf-8'/><meta name='viewport' content='width=device-width, initial-scale=1.0'>\
35 </head><body>\n" ^ header ^ body ^ footer ^ "</body></html>"
36
[2]37let topic_link root topic =
38 let replaced_space = String.map (function ' '->'+' | x->x) in
[3]39 "<a href='index." ^ replaced_space root ^ ".htm#" ^ replaced_space topic ^ "'>"
[2]40 ^ String.capitalize_ascii topic ^ "</a>"
41
[3]42module HtmlConverter = struct
43 include Converter.Html
44 let angled_uri u a = if String.sub u 0 10 <> "urn:txtid:" then
45 angled_uri u a else angled_uri (String.(sub u 10 (length u - 10)) ^ ext) a
46end
47
48let page htm conversion text =
[2]49 let open Logarion in
50 let open Text in
[3]51 let module T = Parsers.Plain_text.Make (HtmlConverter) in
[2]52 let sep_append ?(sep=", ") a x = match a,x with "",_ -> x | _, "" -> a | _ -> a ^ sep ^ x in
[3]53 let opt_kv key value = if String.length value > 0
54 then "<dt>" ^ key ^ "<dd>" ^ value else "" in
[2]55(* let author acc auth = sep_append acc Person.(auth.name ^ " ") in*)
56 let authors = (Person.Set.to_string text.authors ^ " ") in
57 let keywords = str_set "keywords" text in
58 let header =
59 let time x = {|<time datetime="|} ^ x ^ {|">|} ^ x ^ "</time>" in
60 let topic_links x =
61 let to_linked t a =
62 let ts = Topic_set.of_string t in
63 sep_append a (List.fold_left (fun a t -> sep_append ~sep:" > " a (topic_link (List.hd ts) t)) "" ts) in
64 String_set.fold to_linked x "" in
65 "<article><header><dl>"
66 ^ opt_kv "Title:" text.title
67 ^ opt_kv "Authors:" authors
68 ^ opt_kv "Date: " (time (Date.(pretty_date @@ listing text.date)))
69 ^ opt_kv "Series: " (str_set "series" text)
70 ^ opt_kv "Topics: " (topic_links (set "topics" text))
71 ^ opt_kv "Keywords: " keywords
[3]72 ^ opt_kv "Id: " text.id
[2]73 ^ {|</dl></header><pre style="white-space:pre-wrap">|} in
[3]74 wrap conversion htm text.title ((T.of_string text.body header) ^ "</pre></article>")
[2]75
76let to_dated_links ?(limit) meta_list =
77 let meta_list = match limit with
78 | None -> meta_list
79 | Some limit->
80 let rec reduced acc i = function
81 | [] -> acc
82 | h::t -> if i < limit then reduced (h::acc) (i+1) t else acc in
83 List.rev @@ reduced [] 0 meta_list
84 in
85 List.fold_left
[5]86 (fun a m -> Printf.sprintf "%s<li> %s <a href=\"%s.htm\">%s</a>" a
87 Logarion.(Date.(pretty_date (listing m.Text.date)))
88 (Logarion.Text.short_id m) m.Logarion.Text.title)
[2]89 "" meta_list
90
[3]91let date_index ?(limit) conv htm meta_list =
[2]92 match limit with
[3]93 | Some limit -> wrap conv htm "Index" (to_dated_links ~limit meta_list)
94 | None -> wrap conv htm "Index" (to_dated_links meta_list)
[2]95
96let fold_topic_roots topic_roots =
97 let list_item root t = "<li>" ^ topic_link root t in
98 "<nav><h2>Main topics</h2>"
99 ^ List.fold_left (fun a x -> a ^ list_item x x) "<ul>" (List.rev topic_roots)
100 ^ "</ul></nav>"
101
102let fold_topics topic_map topic_roots metas =
103 let open Logarion in
104 let rec unordered_list root topic =
105 List.fold_left (fun a x -> a ^ list_item root x) "<ul>" topic
106 ^ "</ul>"
107 and sub_items root topic = match Topic_set.Map.find_opt topic topic_map with
108 | None -> ""
109 | Some (_, subtopics) -> unordered_list root (String_set.elements subtopics)
110 and list_item root t =
111 let item =
112 if List.exists (fun x -> String_set.mem t (String_set.map Topic_set.topic (Text.set "topics" x))) metas
113 then topic_link root t else String.capitalize_ascii t
114 in
115 "<li>" ^ item ^ sub_items root t
116 in
117 "<nav><h2>Topics</h2>"
118 ^ List.fold_left (fun a x -> a ^ list_item x x) "<ul>" (List.rev topic_roots)
119 ^ "</ul></nav>"
120
121let text_item path meta =
122 let open Logarion in
123 "<time>" ^ Date.(pretty_date (listing meta.Text.date))
124 ^ {|</time> <a href="|} ^ path ^ Text.short_id meta ^ {|.htm">|} ^ meta.Text.title
125 ^ "</a><br>"
126
127let listing_index topic_map topic_roots path metas =
128 let rec item_group topics =
129 List.fold_left (fun acc topic -> acc ^ sub_groups topic ^ items topic) "" topics
130 and sub_groups topic = match Logarion.Topic_set.Map.find_opt topic topic_map with
131 | None -> ""
132 | Some (_, subtopics) -> item_group (Logarion.String_set.elements subtopics)
133 and items topic =
134 let items =
135 let open Logarion in
136 List.fold_left
137 (fun a e ->
138 if String_set.mem topic (String_set.map (Logarion.Topic_set.topic) (Text.set "Topics" e))
139 then text_item path e ^ a else a) "" metas in
140 match items with
141 | "" -> ""
142 | x -> {|<h2 id="|} ^ topic ^ {|">|} ^ String.capitalize_ascii topic ^ "</h2>" ^ x
143 in
144 "<nav><h1>Texts</h1>" ^ item_group topic_roots ^ "</nav>"
145
[3]146let topic_main_index conv htm topic_roots metas =
147 wrap conv htm "Topics"
[2]148 (fold_topic_roots topic_roots
[5]149 ^ "<nav><h1>Latest</h1><ul>" ^ to_dated_links ~limit:8 metas
150 ^ {|</ul><a href="index.date.htm">More by date</a></nav>|} )
[2]151
[3]152let topic_sub_index conv htm topic_map topic_root metas =
153 wrap conv htm topic_root
[2]154 (fold_topics topic_map [topic_root] metas
155(* ^ {|<a href=".atom" id="feed">|}^ String.capitalize_ascii topic_root ^{| feed </a>|}*)
156 ^ listing_index topic_map [topic_root] "" metas)
[3]157
158open Logarion
159let indices htm c =
160 let file name = Logarion.File_store.file (Filename.concat c.Conversion.dir name) in
161 let index_name = try Store.KV.find "HTM-index" c.Conversion.kv with Not_found -> "index.html" in
162 let title = try Store.KV.find "Title" c.Conversion.kv with Not_found -> "" in
163
164 if index_name <> "" then
165 file index_name (topic_main_index c htm c.topic_roots c.texts);
166
167 file "index.date.htm" (date_index c htm c.texts);
168
169 List.iter
170 (fun root -> file ("index." ^ root ^ ".htm") (topic_sub_index c htm c.topics root c.texts))
171 c.topic_roots;
172
173 let base_url = try
174 let _i = Str.(search_forward (regexp "https?://[^;]*") (Store.KV.find "Locations" c.kv) 0) in
175 Str.(matched_string (Store.KV.find "Locations" c.kv))
176 with Not_found -> prerr_endline "Missing location for HTTP(S)"; "" in
177 file "feed.atom" (Atom.feed title c.id base_url "text/html" c.texts)
Note: See TracBrowser for help on using the repository browser.