Files
Geneweb/lib/wiki.ml
2024-03-05 22:01:20 +01:00

882 lines
31 KiB
OCaml

(* Copyright (c) 1998-2007 INRIA *)
open Def
open Config
open Util
(* TLSW: Text Language Stolen to Wikipedia
= title level 1 =
== title level 2 ==
...
====== title level 6 ======
* list ul/li item
* list ul/li item
** list ul/li item 2nd level
** list ul/li item 2nd level
...
# list ol/li item
: indentation list dl/dd item
; list dl dt item ; dd item
''italic''
'''bold'''
'''''bold+italic'''''
[[first_name/surname/oc/text]] link; 'text' displayed
[[first_name/surname/text]] link (oc = 0); 'text' displayed
[[first_name/surname]] link (oc = 0); 'first_name surname' displayed
[[[notes_subfile/text]]] link to a sub-file; 'text' displayed
[[[notes_subfile]]] link to a sub-file; 'notes_subfile' displayed
empty line : new paragraph
lines starting with space : displayed as they are (providing 1/ there
are at least two 2/ there is empty lines before and after the group
of lines).
__TOC__ : summary
__SHORT_TOC__ : short summary (unnumbered)
__NOTOC__ : no (automatic) numbered summary *)
module Buff2 = Buff.Make ()
module Buff = Buff.Make ()
let first_cnt = 1
let tab lev s = String.make (2 * lev) ' ' ^ s
let section_level s len =
let rec loop i j k =
if i > 5 then i
else if len > k && s.[i] = '=' && s.[j] = '=' then
loop (i + 1) (j - 1) (k + 2)
else i
in
loop 1 (len - 2) 4
let notes_aliases conf =
let fname =
match List.assoc_opt "notes_alias_file" conf.base_env with
| Some f -> Util.bpath f
| None -> Filename.concat (Util.bpath (conf.bname ^ ".gwb")) "notes.alias"
in
match try Some (Secure.open_in fname) with Sys_error _ -> None with
| Some ic ->
let rec loop list =
match try Some (input_line ic) with End_of_file -> None with
| Some s ->
let list =
(* S: is it replacable by `String.split_on_char ' '` s? *)
try
let i = String.index s ' ' in
( String.sub s 0 i,
String.sub s (i + 1) (String.length s - i - 1) )
:: list
with Not_found -> list
in
loop list
| None ->
close_in ic;
list
in
loop []
| None -> []
let map_notes aliases f = try List.assoc f aliases with Not_found -> f
let fname_of_path (dirs, file) = List.fold_right Filename.concat dirs file
let str_start_with str i x =
let rec loop i j =
if j = String.length x then true
else if i = String.length str then false
else if str.[i] = x.[j] then loop (i + 1) (j + 1)
else false
in
loop i 0
type wiki_info = {
wi_mode : string;
wi_file_path : string -> string;
wi_person_exists : string * string * int -> bool;
wi_always_show_link : bool;
}
let escape (s : string) = (Util.escape_html s : Adef.escaped_string :> string)
let encode (s : string) = (Mutil.encode s : Adef.encoded_string :> string)
let syntax_links conf wi s =
let slen = String.length s in
let rec loop quot_lev pos i len =
let len, quot_lev =
if i = slen || List.exists (str_start_with s i) [ "</li>"; "</p>" ] then
let len =
match quot_lev with
| 1 -> Buff.mstore len "</i>"
| 2 -> Buff.mstore len "</b>"
| 3 -> Buff.mstore len "</b></i>"
| _ -> len
in
(len, 0)
else (len, quot_lev)
in
if i = slen then Buff.get len
else if
s.[i] = '%'
&& i < slen - 1
&& List.mem s.[i + 1] [ '['; ']'; '{'; '}'; '\'' ]
then loop quot_lev pos (i + 2) (Buff.store len s.[i + 1])
else if s.[i] = '%' && i < slen - 1 && s.[i + 1] = '/' then
loop quot_lev pos (i + 2) (Buff.mstore len "")
else if s.[i] = '{' then
let b, j =
let rec loop len j =
if j = slen then ("", i + 1)
else if j < slen - 1 && s.[j] = '%' then
loop (Buff2.store len s.[j + 1]) (j + 2)
else if s.[j] = '}' then (Buff2.get len, j + 1)
else loop (Buff2.store len s.[j]) (j + 1)
in
loop 0 (i + 1)
in
let s =
if String.length b <> 0 then
Printf.sprintf "<span class=\"highlight\">%s</span>" (escape b)
else ""
in
loop quot_lev pos j (Buff.mstore len s)
else if
i <= slen - 5
&& s.[i] = '\''
&& s.[i + 1] = '\''
&& s.[i + 2] = '\''
&& s.[i + 3] = '\''
&& s.[i + 4] = '\''
&& (quot_lev = 0 || quot_lev = 3)
then
let s = if quot_lev = 0 then "<i><b>" else "</b></i>" in
loop (3 - quot_lev) pos (i + 5) (Buff.mstore len s)
else if
i <= slen - 3
&& s.[i] = '\''
&& s.[i + 1] = '\''
&& s.[i + 2] = '\''
&& (quot_lev = 0 || quot_lev = 2)
then
let s = if quot_lev = 0 then "<b>" else "</b>" in
loop (2 - quot_lev) pos (i + 3) (Buff.mstore len s)
else if
i <= slen - 2
&& s.[i] = '\''
&& s.[i + 1] = '\''
&& (quot_lev = 0 || quot_lev = 1)
then
let s = if quot_lev = 0 then "<i>" else "</i>" in
loop (1 - quot_lev) pos (i + 2) (Buff.mstore len s)
else
match NotesLinks.misc_notes_link s i with
| NotesLinks.WLpage (j, fpath1, fname1, anchor, text) ->
let fpath, fname =
let aliases = notes_aliases conf in
let fname = map_notes aliases fname1 in
match NotesLinks.check_file_name fname with
| Some fpath -> (fpath, fname)
| None -> (fpath1, fname1)
in
let c =
let f = wi.wi_file_path (fname_of_path fpath) in
if Sys.file_exists f then "" else " style=\"color:red\""
in
let anchor = if anchor = "" then "" else "#" ^ encode anchor in
let t =
Printf.sprintf {|<a href="%sm=%s&f=%s%s"%s>%s</a>|}
(commd conf : Adef.escaped_string :> string)
(encode wi.wi_mode) (encode fname) anchor c text
in
loop quot_lev pos j (Buff.mstore len t)
| NotesLinks.WLperson (j, (fn, sn, oc), name, _) ->
let t =
if wi.wi_person_exists (fn, sn, oc) then
Printf.sprintf "<a id=\"p_%d\" href=\"%sp=%s&n=%s%s\">%s</a>" pos
(commd conf :> string)
(encode fn) (encode sn)
(if oc = 0 then "" else "&oc=" ^ string_of_int oc)
name
else if wi.wi_always_show_link then
let s = " style=\"color:red\"" in
Printf.sprintf "<a id=\"p_%d\" href=\"%sp=%s&n=%s%s\"%s>%s</a>"
pos
(commd conf :> string)
(encode fn) (encode sn)
(if oc = 0 then "" else "&oc=" ^ string_of_int oc)
s name
else
Printf.sprintf "<a href=\"%s\" style=\"color:red\">%s</a>"
(commd conf :> string)
(if conf.hide_names then "x x" else escape name)
in
loop quot_lev (pos + 1) j (Buff.mstore len t)
| NotesLinks.WLwizard (j, wiz, name) ->
let t =
let s = if name <> "" then name else wiz in
Printf.sprintf "<a href=\"%sm=WIZNOTES&f=%s\">%s</a>"
(commd conf :> string)
(encode wiz) s
in
loop quot_lev (pos + 1) j (Buff.mstore len t)
| NotesLinks.WLnone -> loop quot_lev pos (i + 1) (Buff.store len s.[i])
in
loop 0 1 0 0
let toc_list = [ "__NOTOC__"; "__TOC__"; "__SHORT_TOC__" ]
let lines_list_of_string s =
let rec loop no_toc lines len i =
if i = String.length s then
(List.rev (if len = 0 then lines else Buff.get len :: lines), no_toc)
else if s.[i] = '\n' then
let line = Buff.get len in
let no_toc = List.mem line toc_list || no_toc in
loop no_toc (line :: lines) 0 (i + 1)
else loop no_toc lines (Buff.store len s.[i]) (i + 1)
in
loop false [] 0 0
let adjust_ul_level rev_lines old_lev new_lev =
if old_lev < new_lev then tab (old_lev + 1) "<ul>" :: rev_lines
else
let rev_lines = (List.hd rev_lines ^ "</li>") :: List.tl rev_lines in
let rec loop rev_lines lev =
if lev = new_lev then rev_lines
else loop (tab lev "</ul></li>" :: rev_lines) (lev - 1)
in
loop rev_lines old_lev
let message_txt conf i = transl_nth conf "visualize/show/hide/summary" i
let sections_nums_of_tlsw_lines lines =
let _, _, _, rev_sections_nums =
List.fold_left
(fun (prev_lev, indent_stack, cnt, sections_nums) s ->
let len = String.length s in
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
let slev = section_level s len in
let lev, stack =
let rec loop lev stack =
match stack with
| (prev_num, prev_slev) :: rest_stack ->
if slev < prev_slev then
match rest_stack with
| (_, prev_prev_slev) :: _ ->
if slev > prev_prev_slev then
let stack = (prev_num, slev) :: rest_stack in
loop lev stack
else loop (lev - 1) rest_stack
| [] ->
let stack = (prev_num + 1, slev) :: rest_stack in
(lev - 1, stack)
else if slev = prev_slev then
let stack = (prev_num + 1, slev) :: rest_stack in
(lev - 1, stack)
else
let stack = (1, slev) :: stack in
(lev, stack)
| [] ->
let stack = (1, slev) :: stack in
(lev, stack)
in
loop prev_lev indent_stack
in
let section_num =
let nums = List.map fst stack in
String.concat "." (List.rev_map string_of_int nums)
in
(lev + 1, stack, cnt + 1, (lev, section_num) :: sections_nums)
else (prev_lev, indent_stack, cnt, sections_nums))
(0, [], first_cnt, []) lines
in
List.rev rev_sections_nums
let remove_links s =
let rec loop len i =
if i = String.length s then Buff.get len
else
let len, i =
match NotesLinks.misc_notes_link s i with
| NotesLinks.WLpage (j, _, _, _, text) -> (Buff.mstore len text, j)
| NotesLinks.WLperson (j, _, name, text) ->
let text =
match text with
| Some text -> if text = "" then name else text
| None -> name
in
(Buff.mstore len text, j)
| NotesLinks.WLwizard (j, _, text) -> (Buff.mstore len text, j)
| NotesLinks.WLnone -> (Buff.store len s.[i], i + 1)
in
loop len i
in
loop 0 0
let summary_of_tlsw_lines conf short lines =
let sections_nums = sections_nums_of_tlsw_lines lines in
let rev_summary, lev, cnt, _ =
List.fold_left
(fun (summary, prev_lev, cnt, sections_nums) s ->
let s = remove_links s in
let len = String.length s in
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
let slev = section_level s len in
let lev, section_num, sections_nums =
match sections_nums with
| (lev, sn) :: sns -> (lev, sn, sns)
| [] -> (0, "fuck", [])
in
let summary =
let s =
Printf.sprintf "<a href=\"#a_%d\">%s%s</a>" cnt
(if short then "" else section_num ^ " - ")
(String.trim (String.sub s slev (len - (2 * slev))))
in
if short then if summary = [] then [ s ] else s :: "&" :: summary
else
let line = tab (lev + 1) "<li>" ^ s in
line :: adjust_ul_level summary (prev_lev - 1) lev
in
(summary, lev + 1, cnt + 1, sections_nums)
else (summary, prev_lev, cnt, sections_nums))
([], 0, first_cnt, sections_nums)
lines
in
if cnt <= first_cnt + 2 then ([], [])
else
let rev_summary =
if short then rev_summary
else "</ul>" :: adjust_ul_level rev_summary (lev - 1) 0
in
let lines =
({|<dl><dd><table id="summary" cellpadding="10"><tr><td align="|}
^ conf.left ^ {|"><div style="text-align:center" id="toctoggleanchor"><b>|}
^ Utf8.capitalize_fst (message_txt conf 3)
^ {|</b></div><div class="summary" id="tocinside">|})
:: List.rev_append rev_summary [ "</div></td></tr></table></dd></dl>" ]
in
(lines, sections_nums)
let string_of_modify_link conf cnt empty = function
| Some (can_edit, mode, sfn) when conf.wizard ->
(if empty then "<p>"
else {|<div class="small float-|} ^ conf.right ^ {|">|})
^ {|(<a href="|}
^ (commd conf :> string)
^ "m="
^ (if can_edit then "MOD" else "VIEW")
^ "_"
^ (Mutil.encode mode :> string)
^ "&v=" ^ string_of_int cnt
^ (if sfn = "" then "" else "&f=" ^ (Mutil.encode sfn :> string))
^ {|">|}
^ (if can_edit then transl_decline conf "modify" ""
else transl conf "view source")
^ "</a>)"
^ if empty then "</p>" else "</div>"
| _ -> ""
let rec tlsw_list tag1 tag2 lev list sl =
let btag2 = "<" ^ tag2 ^ ">" in
let etag2 = "</" ^ tag2 ^ ">" in
let list = tab lev ("<" ^ tag1 ^ ">") :: list in
let list =
let rec loop list = function
| s1 :: (s2 :: _ as sl) ->
if String.length s2 > 0 && List.mem s2.[0] [ '*'; '#'; ':'; ';' ] then
let list = (tab lev btag2 ^ s1) :: list in
let list, sl = do_sub_list s2.[0] lev list sl in
loop (tab lev etag2 :: list) sl
else
let s1, ss1 = sub_sub_list lev tag2 s1 in
loop ((tab lev btag2 ^ s1 ^ etag2 ^ ss1) :: list) sl
| [ s1 ] ->
let s1, ss1 = sub_sub_list lev tag2 s1 in
(tab lev btag2 ^ s1 ^ etag2 ^ ss1) :: list
| [] -> list
in
loop list sl
in
tab lev ("</" ^ tag1 ^ ">") :: list
and sub_sub_list lev tag2 s1 =
if tag2 = "dt" && String.contains s1 ':' then
let i = String.index s1 ':' in
let s = String.sub s1 0 i in
let ss =
"\n"
^ tab (lev + 1) "<dd>"
^ String.sub s1 (i + 1) (String.length s1 - i - 1)
^ "</dd>"
in
(s, ss)
else (s1, "")
and do_sub_list prompt lev list sl =
let tag1, tag2 =
match prompt with
| '*' -> ("ul", "li")
| '#' -> ("ol", "li")
| ':' -> ("dl", "dd")
| ';' -> ("dl", "dt")
| _ -> assert false
in
let list2, sl =
let rec loop list = function
| s :: sl ->
if String.length s > 0 && s.[0] = prompt then
let s = String.sub s 1 (String.length s - 1) in
loop (s :: list) sl
else (list, s :: sl)
| [] -> (list, [])
in
loop [] sl
in
let list = tlsw_list tag1 tag2 (lev + 1) list (List.rev list2) in
match sl with
| s :: _ ->
if String.length s > 0 && List.mem s.[0] [ '*'; '#'; ':'; ';' ] then
do_sub_list s.[0] lev list sl
else (list, sl)
| [] -> (list, sl)
let rec hotl conf wlo cnt edit_opt sections_nums list = function
| "__NOTOC__" :: sl -> hotl conf wlo cnt edit_opt sections_nums list sl
| "__TOC__" :: sl ->
let list =
match wlo with
| Some lines ->
let summary, _ = summary_of_tlsw_lines conf false lines in
List.rev_append summary list
| None -> list
in
hotl conf wlo cnt edit_opt sections_nums list sl
| "__SHORT_TOC__" :: sl ->
let list =
match wlo with
| Some lines ->
let summary, _ = summary_of_tlsw_lines conf true lines in
List.rev_append summary list
| None -> list
in
hotl conf wlo cnt edit_opt sections_nums list sl
| "" :: sl ->
let parag =
let rec loop1 parag = function
| "" :: sl -> Some (parag, sl, true)
| s :: sl ->
if
List.mem s.[0] [ '*'; '#'; ':'; ';'; '=' ]
|| List.mem s toc_list
then if parag = [] then None else Some (parag, s :: sl, true)
else if s.[0] = ' ' && parag = [] then loop2 [ s ] sl
else loop1 (s :: parag) sl
| [] -> Some (parag, [], true)
and loop2 parag = function
| "" :: sl -> Some (parag, sl, false)
| s :: sl ->
if s.[0] = ' ' then loop2 (s :: parag) sl
else loop1 parag (s :: sl)
| [] -> Some (parag, [], true)
in
loop1 [] sl
in
let list, sl =
match parag with
| Some ([], _, _) | None -> (list, sl)
| Some (parag, sl, false) when List.length parag >= 2 ->
("</pre>" :: (parag @ ("<pre>" :: list)), "" :: sl)
| Some (parag, sl, _) -> ("</p>" :: (parag @ ("<p>" :: list)), "" :: sl)
in
hotl conf wlo cnt edit_opt sections_nums list sl
| s :: sl -> (
let len = String.length s in
let tago =
if len > 0 then
match s.[0] with
| '*' -> Some ("ul", "li")
| '#' -> Some ("ol", "li")
| ':' -> Some ("dl", "dd")
| ';' -> Some ("dl", "dt")
| _ -> None
else None
in
match tago with
| Some (tag1, tag2) ->
let sl, rest = select_list_lines conf s.[0] [] (s :: sl) in
let list = tlsw_list tag1 tag2 0 list sl in
hotl conf wlo cnt edit_opt sections_nums list ("" :: rest)
| None ->
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
let slev = section_level s len in
let section_num, sections_nums =
match sections_nums with
| (_, a) :: l -> (a ^ " - ", l)
| [] -> ("", [])
in
let s =
let style = if slev <= 3 then " class=\"subtitle\"" else "" in
Printf.sprintf "<h%d%s>%s%s</h%d>" slev style section_num
(String.sub s slev (len - (2 * slev)))
slev
in
let list =
if wlo <> None then
let s = Printf.sprintf "<p><a id=\"a_%d\"></a></p>" cnt in
s :: list
else list
in
let list =
let s = string_of_modify_link conf cnt false edit_opt in
if s = "" then list else s :: list
in
hotl conf wlo (cnt + 1) edit_opt sections_nums list (s :: sl)
else hotl conf wlo cnt edit_opt sections_nums (s :: list) sl)
| [] -> List.rev list
and select_list_lines conf prompt list = function
| s :: sl ->
let len = String.length s in
if len > 0 && s.[0] = '=' then (List.rev list, s :: sl)
else if len > 0 && s.[0] = prompt then
let s = String.sub s 1 (len - 1) in
let s, sl =
let rec loop s1 = function
| "" :: s :: sl
when String.length s > 1 && s.[0] = prompt && s.[1] = prompt ->
let br = "<br" ^ ">" in
loop (s1 ^ br ^ br) (s :: sl)
| s :: sl ->
if String.length s > 0 && s.[0] = '=' then (s1, s :: sl)
else if String.length s > 0 && s.[0] <> prompt then
loop (s1 ^ "\n" ^ s) sl
else (s1, s :: sl)
| [] -> (s1, [])
in
loop s sl
in
select_list_lines conf prompt (s :: list) sl
else (List.rev list, s :: sl)
| [] -> (List.rev list, [])
let html_of_tlsw conf s =
let lines, _ = lines_list_of_string s in
let sections_nums =
match sections_nums_of_tlsw_lines lines with [ _ ] -> [] | l -> l
in
hotl conf (Some lines) first_cnt None sections_nums [] ("" :: lines)
let html_with_summary_of_tlsw conf wi edit_opt s =
let lines, no_toc = lines_list_of_string s in
let summary, sections_nums =
if no_toc then ([], []) else summary_of_tlsw_lines conf false lines
in
let rev_lines_before_summary, lines =
let rec loop lines_bef = function
| s :: sl ->
if String.length s > 1 && s.[0] = '=' then (lines_bef, s :: sl)
else loop (s :: lines_bef) sl
| [] -> (lines_bef, [])
in
loop [] lines
in
let lines_before_summary =
hotl conf (Some lines) first_cnt None [] []
(List.rev rev_lines_before_summary)
in
let lines_after_summary =
hotl conf (Some lines) first_cnt edit_opt sections_nums [] lines
in
let s =
syntax_links conf wi
(String.concat "\n"
(lines_before_summary @ summary @ lines_after_summary))
in
if lines_before_summary <> [] || lines = [] then
string_of_modify_link conf 0 (s = "") edit_opt ^ s
else s
(* v = 0 -> keeps the last lines until a title occurs, discards the rest *)
(* v = 1 -> *)
let rev_extract_sub_part (s : string) (v : int) : string list =
let lines, _ = lines_list_of_string s in
let rec loop (lines : string list) (* The accumulator of lines *) (lev : int)
(* The section level *) (cnt : int) : string list -> string list =
(* A counter of titles *) function
| s :: sl ->
let len = String.length s in
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
(* This line is a title *)
if v = first_cnt - 1 then lines
(* S: previous condition is a strange way to write `if v = 0` *)
else
let nlev = section_level s len in
if cnt = v (* *) then loop (s :: lines) nlev (cnt + 1) sl
else if cnt > v then
if nlev > lev then loop (s :: lines) lev (cnt + 1) sl else lines
else loop lines lev (cnt + 1) sl
else if (* This line is not a title *)
cnt <= v then
loop lines lev cnt sl (* Line is in an ignored section *)
else loop (s :: lines) lev cnt sl (* Keeping the line *)
| [] -> lines
in
loop [] 0 first_cnt lines
let extract_sub_part s v = List.rev (rev_extract_sub_part s v)
let print_sub_part_links conf edit_mode sfn cnt0 is_empty =
Output.print_sstring conf "<p>";
if cnt0 >= first_cnt then (
Output.print_sstring conf {|<a href="|};
Output.print_sstring conf (commd conf :> string);
Output.print_sstring conf {|m=|};
Output.print_string conf edit_mode;
Output.print_string conf sfn;
Output.print_sstring conf {|&v=|};
Output.print_sstring conf (string_of_int @@ (cnt0 - 1));
Output.print_sstring conf {|">|};
Output.print_sstring conf
{|<span class="fa fa-arrow-left fa-lg" title="<<"></span></a> |});
Output.print_sstring conf {|<a href="|};
Output.print_string conf (commd conf);
Output.print_sstring conf {|m=|};
Output.print_string conf edit_mode;
Output.print_string conf sfn;
Output.print_sstring conf
{|"><span class="fa fa-arrow-up fa-lg" title="^^"></span></a>|};
if not is_empty then (
Output.print_sstring conf {|<a href="|};
Output.print_string conf (commd conf);
Output.print_sstring conf "m=";
Output.print_string conf edit_mode;
Output.print_string conf sfn;
Output.print_sstring conf "&v=";
Output.print_sstring conf (string_of_int @@ (cnt0 + 1));
Output.print_sstring conf
{|"><span class="fa fa-arrow-right fa-lg" title=">>"></span></a>|});
Output.print_sstring conf "</p>"
let print_sub_part_text conf wi edit_opt cnt0 lines =
let lines =
List.map
(function
| "__TOC__" | "__SHORT_TOC__" ->
Printf.sprintf "<p>...%s...</p>" (message_txt conf 3)
| "__NOTOC__" -> ""
| s -> s)
lines
in
let lines = hotl conf None cnt0 edit_opt [] [] lines in
let s = String.concat "\n" lines in
let s = syntax_links conf wi s in
let s =
if cnt0 < first_cnt then string_of_modify_link conf 0 (s = "") edit_opt ^ s
else s
in
Output.print_string conf (Util.safe_html s)
let print_sub_part conf wi can_edit edit_mode sub_fname cnt0 lines =
let edit_opt = Some (can_edit, edit_mode, sub_fname) in
let sfn =
if sub_fname = "" then Adef.encoded "" else "&f=" ^<^ Mutil.encode sub_fname
in
print_sub_part_links conf (Mutil.encode edit_mode) sfn cnt0 (lines = []);
print_sub_part_text conf wi edit_opt cnt0 lines
let print_mod_view_page conf can_edit mode fname title env s =
let s = List.fold_left (fun s (k, v) -> s ^ k ^ "=" ^ v ^ "\n") "" env ^ s in
let mode_pref = Mutil.encode (if can_edit then "MOD_" else "VIEW_") in
let has_v, v =
match p_getint conf.env "v" with Some v -> (true, v) | None -> (false, 0)
in
let sub_part =
if not has_v then s else String.concat "\n" (extract_sub_part s v)
in
let is_empty = sub_part = "" in
let sfn =
if fname = "" then Adef.encoded "" else "&f=" ^<^ Mutil.encode fname
in
Hutil.header conf title;
if can_edit then (
Output.print_sstring conf {|<div style="font-size:80%;float:|};
Output.print_sstring conf conf.right;
Output.print_sstring conf {|;margin-|};
Output.print_sstring conf conf.left;
Output.print_sstring conf {|:3em">(<a href="|};
Output.print_string conf (commd conf);
Output.print_sstring conf {|m=|};
Output.print_string conf mode;
if has_v then (
Output.print_sstring conf "&v=";
Output.print_sstring conf (string_of_int v));
Output.print_string conf sfn;
Output.print_sstring conf {|">|};
Output.print_sstring conf (message_txt conf 0);
Output.print_sstring conf "</a>)</div>");
Hutil.print_link_to_welcome conf true;
if can_edit && has_v then
print_sub_part_links conf (mode_pref ^^^ mode) sfn v is_empty;
Output.print_sstring conf {|<form method="POST" action="|};
Output.print_sstring conf conf.command;
Output.print_sstring conf {|">|};
Util.hidden_env conf;
if can_edit then Util.hidden_input conf "m" ("MOD_" ^<^ mode ^>^ "_OK");
if has_v then Util.hidden_input conf "v" (Adef.encoded @@ string_of_int v);
if fname <> "" then Util.hidden_input conf "f" (Mutil.encode fname);
if can_edit then
Util.hidden_input conf "digest" (Mutil.digest s |> Mutil.encode);
Output.print_sstring conf
{|<div class="row ml-3"><div class="d-inline col-9 py-1">|};
Util.include_template conf [ ("name", Adef.encoded "notes") ] "toolbar" ignore;
Output.print_sstring conf
{|</div><textarea name="notes" id="notes_comments" class="col-9 form-control" rows="25" cols="110"|};
Output.print_sstring conf
(if can_edit then ">" else " readonly=\"readonly\">");
Output.print_string conf (Util.escape_html sub_part);
Output.print_sstring conf "</textarea>";
if can_edit then (
Output.print_sstring conf
{|<button type="submit" class="btn btn-outline-primary btn-lg col-4 py-3 mt-2 mb-3 mx-auto order-3">|};
Output.print_sstring conf
(Utf8.capitalize_fst (transl_nth conf "validate/delete" 0));
Output.print_sstring conf "</button>");
Output.print_sstring conf {|<div class="d-inline col-9 py-1">|};
Util.include_template conf [ ("name", Adef.encoded "notes") ] "accent" ignore;
Output.print_sstring conf "</div></div></form>";
Hutil.trailer conf
let insert_sub_part s v sub_part =
let lines, _ = lines_list_of_string s in
let lines, sl =
let rec loop sub_part_added lines lev cnt = function
| s :: sl ->
let len = String.length s in
if len > 2 && s.[0] = '=' && s.[len - 1] = '=' then
if v = first_cnt - 1 then
((if sub_part = "" then [] else [ ""; sub_part ]), s :: sl)
else
let nlev = section_level s len in
if cnt = v then
let lines =
if sub_part = "" then lines else "" :: sub_part :: lines
in
loop true lines nlev (cnt + 1) sl
else if cnt > v then
if nlev > lev then loop sub_part_added lines lev (cnt + 1) sl
else (lines, s :: sl)
else loop sub_part_added (s :: lines) lev (cnt + 1) sl
else if cnt <= v then loop sub_part_added (s :: lines) lev cnt sl
else loop sub_part_added lines lev cnt sl
| [] ->
let lines =
if sub_part_added then lines
else if sub_part = "" then lines
else "" :: sub_part :: lines
in
(lines, [])
in
loop false [] 0 first_cnt lines
in
String.concat "\n" (List.rev_append lines sl)
(* TODO: simplify with Str *)
let rec find_env s i =
match
try Some (String.index_from s i '=', String.index_from s i '\n')
with Not_found -> None
with
| Some (j, k) ->
if j > i && j < k then
let is_key =
let rec loop i =
if i = j then true
else match s.[i] with 'A' .. 'Z' -> loop (i + 1) | _ -> false
in
loop i
in
if is_key then
let key = String.sub s i (j - i) in
let v = String.sub s (j + 1) (k - j - 1) in
let env, i = find_env s (k + 1) in
((key, v) :: env, i)
else ([], i)
else ([], i)
| None -> ([], i)
let split_title_and_text s =
let env, i = find_env s 0 in
let s = if i = 0 then s else String.sub s i (String.length s - i) in
if (try List.assoc "TITLE" env with Not_found -> "") = "" then
let tit, txt =
try
let i = String.index s '\n' in
let tit = String.sub s 0 i in
let txt = String.sub s (i + 1) (String.length s - i - 1) in
(tit, txt)
with Not_found -> (s, "")
in
let tit, txt =
if
(String.length tit > 0 && tit.[0] = '=')
|| String.contains tit '<' || String.contains tit '['
then ("", s)
else (tit, txt)
in
let env = if tit <> "" then ("TITLE", tit) :: env else env in
(env, txt)
else (env, s)
let print_ok conf wi edit_mode fname title_is_1st s =
let title _ =
Output.print_sstring conf
(Utf8.capitalize_fst (Util.transl conf "notes modified"))
in
Hutil.header_no_page_title conf title;
Output.print_sstring conf {|<div style="text-align:center"> --- |};
title ();
Output.print_sstring conf {| --- </div>|};
Hutil.print_link_to_welcome conf true;
let get_v = Util.p_getint conf.env "v" in
let v = match get_v with Some v -> v | None -> 0 in
let title, s =
if v = 0 && title_is_1st then
let env, s = split_title_and_text s in
((try List.assoc "TITLE" env with Not_found -> ""), s)
else ("", s)
in
let lines, _ = lines_list_of_string s in
let lines =
if v = 0 && title <> "" then ("<h1>" ^ title ^ "</h1>") :: lines else lines
in
print_sub_part conf wi conf.wizard edit_mode fname v lines;
Hutil.trailer conf
let print_mod_ok conf wi edit_mode fname read_string commit string_filter
title_is_1st =
let fname = fname (Util.p_getenv conf.env "f") in
match edit_mode fname with
| Some edit_mode ->
let old_string =
let e, s = read_string fname in
List.fold_left (fun s (k, v) -> s ^ k ^ "=" ^ v ^ "\n") "" e ^ s
in
let sub_part =
match Util.p_getenv conf.env "notes" with
| Some v -> Mutil.strip_all_trailing_spaces v
| None -> failwith "notes unbound"
in
let digest =
match Util.p_getenv conf.env "digest" with Some s -> s | None -> ""
in
if digest <> Mutil.digest old_string then Update.error_digest conf
else
let s =
match Util.p_getint conf.env "v" with
| Some v -> insert_sub_part old_string v sub_part
| None -> sub_part
in
if s <> old_string then commit fname s;
let sub_part = string_filter sub_part in
print_ok conf wi edit_mode fname title_is_1st sub_part
| None -> Hutil.incorrect_request conf