My aggregated monorepo of OCaml code, automaintained
at main 214 lines 6.9 kB view raw
1(*--------------------------------------------------------------------------- 2 Copyright (c) 2025 Anil Madhavapeddy <anil@recoil.org>. All rights reserved. 3 SPDX-License-Identifier: ISC 4 ---------------------------------------------------------------------------*) 5 6(** Paper rendering for Arod webserver *) 7 8open Htmlit 9open Printf 10 11module MP = Arod_model.Paper 12module MC = Arod_model.Contact 13 14(** Author name with text-wrap:nowrap *) 15let author_name name = 16 El.span ~at:[At.style "text-wrap:nowrap"] [El.txt name] 17 18(** Render one author - link to their best URL if available *) 19let one_author author_name_str = 20 match Arod_model.lookup_by_name author_name_str with 21 | None -> 22 El.span ~at:[At.class' "author"] [author_name author_name_str] 23 | Some contact -> 24 let name = MC.name contact in 25 match MC.best_url contact with 26 | None -> 27 El.span ~at:[At.class' "author"] [author_name name] 28 | Some url -> 29 El.a ~at:[At.href url] [author_name name] 30 31(** Render all authors with proper comma and "and" formatting *) 32let authors p = 33 let author_names = MP.authors p in 34 let author_els = List.map one_author author_names in 35 match author_els with 36 | [] -> El.splice [] 37 | [a] -> a 38 | els -> 39 let rec make_list = function 40 | [] -> [] 41 | [x] -> [El.txt " and "; x] 42 | x :: xs -> x :: El.txt ", " :: make_list xs 43 in 44 El.splice (make_list els) 45 46(** Generate publication info based on bibtype *) 47let paper_publisher p = 48 let bibty = MP.bibtype p in 49 let ourl l = function 50 | None -> l 51 | Some u -> sprintf {|<a href="%s">%s</a>|} u l 52 in 53 let string_of_vol_issue p = 54 match (MP.volume p), (MP.number p) with 55 | Some v, Some n -> sprintf " (vol %s issue %s)" v n 56 | Some v, None -> sprintf " (vol %s)" v 57 | None, Some n -> sprintf " (issue %s)" n 58 | _ -> "" 59 in 60 let result = match String.lowercase_ascii bibty with 61 | "misc" -> 62 sprintf {|Working paper at %s|} (ourl (MP.publisher p) (MP.url p)) 63 | "inproceedings" -> 64 sprintf {|Paper in the %s|} (ourl (MP.booktitle p) (MP.url p)) 65 | "proceedings" -> 66 sprintf {|%s|} (ourl (MP.title p) (MP.url p)) 67 | "abstract" -> 68 sprintf {|Abstract in the %s|} (ourl (MP.booktitle p) (MP.url p)) 69 | "article" | "journal" -> 70 sprintf {|Journal paper in %s%s|} (ourl (MP.journal p) (MP.url p)) (string_of_vol_issue p) 71 | "book" -> 72 sprintf {|Book published by %s|} (ourl (MP.publisher p) (MP.url p)) 73 | "techreport" -> 74 sprintf {|Technical report%s at %s|} 75 (match MP.number p with None -> "" | Some n -> " (" ^ n ^ ")") 76 (ourl (MP.institution p) (MP.url p)) 77 | _ -> sprintf {|Publication in %s|} (ourl (MP.publisher p) (MP.url p)) 78 in 79 El.unsafe_raw result 80 81(** Extract host without www prefix *) 82let host_without_www u = 83 match Uri.host (Uri.of_string u) with 84 | None -> "" 85 | Some h -> 86 if String.starts_with ~prefix:"www." h then 87 String.sub h 4 (String.length h - 4) 88 else h 89 90(** Render the links bar (URL, DOI, BIB, PDF) *) 91let paper_bar_for_feed ?(nopdf=false) p = 92 let cfg = Arod_model.get_config () in 93 let pdf = 94 let pdf_path = Filename.concat cfg.paths.static_dir (sprintf "papers/%s.pdf" (MP.slug p)) in 95 if Sys.file_exists pdf_path && not nopdf then 96 Some (El.a ~at:[At.href (sprintf "/papers/%s.pdf" (MP.slug p))] [ 97 El.span ~at:[At.class' "nobreak"] [ 98 El.txt "PDF"; 99 El.img ~at:[At.class' "inline-icon"; At.alt "pdf"; At.src "/assets/pdf.svg"] () 100 ] 101 ]) 102 else None 103 in 104 let bib = 105 if nopdf then None 106 else Some (El.a ~at:[At.href (sprintf "/papers/%s.bib" (MP.slug p))] [El.txt "BIB"]) 107 in 108 let url = 109 match MP.url p with 110 | None -> None 111 | Some u -> 112 Some (El.splice [ 113 El.a ~at:[At.href u] [El.txt "URL"]; 114 El.txt " "; 115 El.unsafe_raw (sprintf {|<i style="color: #666666">(%s)</i>|} (host_without_www u)) 116 ]) 117 in 118 let doi = 119 match MP.doi p with 120 | None -> None 121 | Some d -> 122 Some (El.a ~at:[At.href ("https://doi.org/" ^ d)] [El.txt "DOI"]) 123 in 124 let bits = [url; doi; bib; pdf] |> List.filter_map Fun.id in 125 El.splice ~sep:(El.unsafe_raw " &nbsp; ") bits 126 127(** Render paper for feed/listing (blockquote style) *) 128let paper_for_feed p = 129 let title_el = El.p ~at:[At.class' "paper-title"] [ 130 El.a ~at:[At.href (Arod_model.Entry.site_url (`Paper p))] [El.txt (MP.title p)] 131 ] in 132 (El.blockquote ~at:[At.class' "paper noquote"] [ 133 El.div ~at:[At.class' "paper-info"] [ 134 title_el; 135 El.p [authors p; El.txt "."]; 136 El.p [paper_publisher p; El.txt "."]; 137 El.p [paper_bar_for_feed p] 138 ] 139 ], None) 140 141(** Render paper for entry listing *) 142let paper_for_entry ?nopdf p = 143 (El.div ~at:[At.class' "paper"] [ 144 El.div ~at:[At.class' "paper-info"] [ 145 El.p ~at:[At.class' "paper-title"] [ 146 El.a ~at:[At.href (Arod_model.Entry.site_url (`Paper p))] [El.txt (MP.title p)] 147 ]; 148 El.p [authors p; El.txt "."]; 149 El.p [paper_publisher p; El.txt "."]; 150 El.p [paper_bar_for_feed ?nopdf p] 151 ] 152 ], None) 153 154(** Render older versions section for a paper *) 155let one_paper_extra p = 156 let entries = Arod_model.get_entries () in 157 let all = Arod_model.Entry.old_papers entries 158 |> List.filter (fun op -> MP.slug op = MP.slug p) 159 in 160 match all with 161 | [] -> El.splice [] 162 | all -> 163 let older_versions = List.map (fun op -> 164 let (paper_html, _) = paper_for_entry ~nopdf:true op in 165 El.splice [ 166 El.hr (); 167 El.p [ 168 El.txt ("This is " ^ op.Arod_model.Paper.ver ^ " of the publication from " ^ 169 Arod_view.ptime_date ~with_d:false (MP.date op) ^ ".") 170 ]; 171 El.blockquote ~at:[At.class' "noquote"] [ 172 paper_html 173 ]; 174 Arod_view.tags_meta (`Paper op) 175 ] 176 ) all in 177 El.splice [ 178 El.h1 [El.txt "Older versions"]; 179 El.p [ 180 El.txt "There are earlier revisions of this paper available below for historical reasons. "; 181 El.txt "Please cite the latest version of the paper above instead of these." 182 ]; 183 El.splice older_versions 184 ] 185 186(** Render full paper page *) 187let one_paper_full p = 188 let img_el = 189 match Arod_model.lookup_image (MP.slug p) with 190 | Some img -> 191 El.p [ 192 El.a ~at:[At.href (Option.value ~default:"#" (MP.best_url p))] [ 193 Arod_view.img ~cl:"image-center" img 194 ] 195 ] 196 | None -> El.splice [] 197 in 198 let abstract_html = 199 let abstract = MP.abstract p in 200 if abstract <> "" then 201 El.p [El.unsafe_raw (Arod_view.md_to_html abstract)] 202 else 203 El.splice [] 204 in 205 El.div ~at:[At.class' "paper"] [ 206 El.div ~at:[At.class' "paper-info"] [ 207 El.h2 [El.txt (MP.title p)]; 208 El.p [authors p; El.txt "."]; 209 El.p [paper_publisher p; El.txt "."]; 210 El.p [paper_bar_for_feed p] 211 ]; 212 img_el; 213 abstract_html 214 ]