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

Last change on this file since 68 was 62, checked in by Izuru Yakumo, 13 months ago

Remove the old UUID code, update the mailing list address, and use 'favicon.ico'

Signed-off-by: Izuru Yakumo <yakumo.izuru@…>

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