2022-03-31 00:01:52 +02:00
|
|
|
open Syntax
|
|
|
|
|
open Babillard
|
2022-01-29 09:34:57 +01:00
|
|
|
open Db
|
|
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let pp_post fmt t =
|
|
|
|
|
let thread_data_opt, post =
|
|
|
|
|
match t with
|
|
|
|
|
| Op (data, post) -> (Some data, post)
|
|
|
|
|
| Post post -> (None, post)
|
|
|
|
|
in
|
2022-02-21 00:51:44 +01:00
|
|
|
let { id
|
|
|
|
|
; parent_id = _parent_id
|
|
|
|
|
; date
|
2022-03-08 21:20:01 +01:00
|
|
|
; user_id
|
2022-02-21 00:51:44 +01:00
|
|
|
; nick
|
|
|
|
|
; comment
|
|
|
|
|
; image_info
|
|
|
|
|
; tags
|
|
|
|
|
; replies
|
|
|
|
|
; citations = _citations
|
|
|
|
|
} =
|
|
|
|
|
post
|
2022-02-18 19:40:19 +01:00
|
|
|
in
|
2022-01-29 09:34:57 +01:00
|
|
|
|
2022-02-18 01:37:25 +01:00
|
|
|
let image_view fmt () =
|
2022-01-29 09:34:57 +01:00
|
|
|
match image_info with
|
|
|
|
|
| Some (_image_name, image_alt) ->
|
2022-02-18 01:37:25 +01:00
|
|
|
Format.fprintf fmt
|
2022-01-29 09:34:57 +01:00
|
|
|
{|
|
2022-02-18 20:17:24 +01:00
|
|
|
<div class="post-image-container">
|
2022-02-18 19:40:19 +01:00
|
|
|
<a href="/img/%s">
|
2022-03-15 00:12:36 +01:00
|
|
|
<img class="post-image" src="/img/s/%s" alt="%s" title="%s" data-id="%s" loading="lazy">
|
2022-01-29 09:34:57 +01:00
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
|}
|
2022-03-15 00:12:36 +01:00
|
|
|
id id image_alt image_alt id
|
2022-02-18 01:37:25 +01:00
|
|
|
| None -> Format.fprintf fmt ""
|
2022-01-29 09:34:57 +01:00
|
|
|
in
|
|
|
|
|
|
|
|
|
|
let pp_print_reply fmt reply =
|
2022-02-18 20:17:24 +01:00
|
|
|
Format.fprintf fmt {|<a class="reply-link" href="#%s">>>%s</a>|} reply
|
2022-01-29 09:34:57 +01:00
|
|
|
reply
|
|
|
|
|
in
|
2022-02-18 01:37:25 +01:00
|
|
|
let pp_print_replies fmt replies =
|
|
|
|
|
Format.fprintf fmt {|<div class="replies">%a</div>|}
|
2022-01-29 09:34:57 +01:00
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_print_reply)
|
2022-02-02 13:47:04 +01:00
|
|
|
replies
|
2022-01-29 09:34:57 +01:00
|
|
|
in
|
|
|
|
|
|
2022-02-18 01:37:25 +01:00
|
|
|
let replies_view fmt () =
|
2022-02-21 02:24:17 +01:00
|
|
|
if Option.is_some thread_data_opt then
|
2022-02-21 00:51:44 +01:00
|
|
|
(* TODO put thread_posts count in thread_info ? *)
|
2022-02-18 19:40:19 +01:00
|
|
|
let res_nb = Db.find Q.count_thread_posts id in
|
2022-01-29 09:34:57 +01:00
|
|
|
match res_nb with
|
2022-02-18 01:37:25 +01:00
|
|
|
| Error _ -> Format.fprintf fmt ""
|
2022-01-29 09:34:57 +01:00
|
|
|
| Ok ((1 | 2) as nb) ->
|
2022-02-18 01:37:25 +01:00
|
|
|
Format.fprintf fmt {|<div class="replies">%d reply</div>|} (nb - 1)
|
2022-01-29 09:34:57 +01:00
|
|
|
| Ok nb ->
|
2022-02-21 00:51:44 +01:00
|
|
|
Format.fprintf fmt {|<div class="replies">%d replies</div>|} (nb - 1)
|
|
|
|
|
else pp_print_replies fmt replies
|
2022-01-29 09:34:57 +01:00
|
|
|
in
|
|
|
|
|
|
2022-02-18 01:37:25 +01:00
|
|
|
let post_links_view fmt () =
|
2022-02-21 02:24:17 +01:00
|
|
|
if Option.is_some thread_data_opt then
|
2022-02-21 00:51:44 +01:00
|
|
|
Format.fprintf fmt {|
|
|
|
|
|
%a
|
|
|
|
|
|} replies_view ()
|
|
|
|
|
else
|
2022-02-18 01:37:25 +01:00
|
|
|
Format.fprintf fmt
|
2022-01-29 09:34:57 +01:00
|
|
|
{|
|
|
|
|
|
<span class=postNo>
|
2022-02-18 20:17:24 +01:00
|
|
|
<button data-id="%s" class="quote-link" title="Reply to this post">%s</button>
|
2022-01-29 09:34:57 +01:00
|
|
|
</span>
|
2022-02-18 01:37:25 +01:00
|
|
|
%a
|
2022-01-29 09:34:57 +01:00
|
|
|
|}
|
2022-02-22 08:48:08 +01:00
|
|
|
id id replies_view ()
|
2022-01-29 09:34:57 +01:00
|
|
|
in
|
|
|
|
|
|
2022-02-18 01:37:25 +01:00
|
|
|
let post_info_view fmt () =
|
|
|
|
|
Format.fprintf fmt
|
2022-01-29 09:34:57 +01:00
|
|
|
{|
|
2022-02-18 20:17:24 +01:00
|
|
|
<div class="post-info">
|
2022-03-08 21:20:01 +01:00
|
|
|
<span class="nick" data-user-id="%s">%s</span>
|
2022-02-23 14:30:06 +01:00
|
|
|
<span class="date" data-time="%f"></span>
|
2022-02-22 08:48:08 +01:00
|
|
|
<div class="dropend post-menu-div">
|
|
|
|
|
<a class="dropdown-toggle post-menu-link" href="#" role="button" id="dropdownMenuLink" data-bs-toggle="dropdown" aria-expanded="false">
|
|
|
|
|
</a>
|
|
|
|
|
<ul class="dropdown-menu post-menu-content" aria-labelledby="dropdownMenuLink">
|
|
|
|
|
<li><a class="dropdown-item" href="#%s">Link to this post</a></li>
|
|
|
|
|
<li><a class="dropdown-item" href="/delete/%s">Delete</a></li>
|
|
|
|
|
<li><a class="dropdown-item" href="/report/%s">Report</a></li>
|
|
|
|
|
</ul>
|
|
|
|
|
</div>
|
2022-02-18 01:37:25 +01:00
|
|
|
%a
|
2022-01-29 09:34:57 +01:00
|
|
|
</div>|}
|
2022-03-08 21:20:01 +01:00
|
|
|
user_id nick date id id id post_links_view ()
|
2022-01-29 09:34:57 +01:00
|
|
|
in
|
2022-02-02 13:47:04 +01:00
|
|
|
|
2022-03-09 14:36:19 +01:00
|
|
|
let pp_print_category fmt category =
|
|
|
|
|
Format.fprintf fmt {|<span class="category tag">%s</span>|} category
|
|
|
|
|
in
|
2022-02-02 13:47:04 +01:00
|
|
|
let pp_print_tag fmt tag =
|
|
|
|
|
Format.fprintf fmt {|<span class="tag">%s</span>|} tag
|
|
|
|
|
in
|
2022-02-18 01:37:25 +01:00
|
|
|
let pp_print_tags fmt tags =
|
2022-03-09 14:36:19 +01:00
|
|
|
let categories, tags =
|
|
|
|
|
List.partition (fun tag -> List.mem tag App.categories) tags
|
|
|
|
|
in
|
|
|
|
|
let categories = List.sort String.compare categories in
|
|
|
|
|
let tags = List.sort String.compare tags in
|
|
|
|
|
Format.fprintf fmt {|<div class="tags">%a%a</div>|}
|
|
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_print_category)
|
|
|
|
|
categories
|
2022-02-02 13:47:04 +01:00
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_print_tag)
|
|
|
|
|
tags
|
|
|
|
|
in
|
|
|
|
|
let tags = List.sort String.compare tags in
|
2022-02-18 01:37:25 +01:00
|
|
|
let tags_view fmt () = pp_print_tags fmt tags in
|
2022-02-02 13:47:04 +01:00
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let subject =
|
|
|
|
|
Option.fold ~none:""
|
|
|
|
|
~some:(fun thread_data -> thread_data.subject)
|
|
|
|
|
thread_data_opt
|
|
|
|
|
in
|
2022-02-22 10:23:34 +01:00
|
|
|
(* put a link in if its a preview *)
|
|
|
|
|
let link fmt () =
|
|
|
|
|
if Option.is_some thread_data_opt then
|
|
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|<a class="stretched-link preview-link" href="/thread/%s"></a>|} id
|
|
|
|
|
in
|
2022-02-23 14:30:06 +01:00
|
|
|
|
2022-02-22 10:23:34 +01:00
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|
|
|
|
|
|
<div class="position-relative post" id="%s">
|
2022-02-21 02:24:17 +01:00
|
|
|
<div class="thread-subject">
|
|
|
|
|
%s
|
|
|
|
|
</div>
|
|
|
|
|
%a
|
|
|
|
|
%a
|
|
|
|
|
%a
|
2022-02-22 10:23:34 +01:00
|
|
|
<blockquote class="post-comment">%s</blockquote>
|
2022-02-22 01:48:37 +01:00
|
|
|
%a
|
|
|
|
|
</div>
|
2022-02-22 10:23:34 +01:00
|
|
|
|}
|
|
|
|
|
id subject link () post_info_view () image_view () comment tags_view ()
|
2022-01-29 09:34:57 +01:00
|
|
|
|
2022-02-21 09:46:27 +01:00
|
|
|
let view_post id =
|
|
|
|
|
let* post = get_post id in
|
|
|
|
|
Ok (Format.asprintf "%a" pp_post (Post post))
|
|
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let pp_thread_preview fmt op =
|
2022-02-21 01:59:36 +01:00
|
|
|
let thread_data, post = op in
|
2022-02-02 11:58:18 +01:00
|
|
|
let thread_preview =
|
2022-02-21 02:24:17 +01:00
|
|
|
Format.fprintf fmt
|
2022-02-02 11:58:18 +01:00
|
|
|
{|
|
2022-02-22 01:48:37 +01:00
|
|
|
<div class="thread-preview">
|
|
|
|
|
%a
|
|
|
|
|
</div>
|
2022-02-02 11:58:18 +01:00
|
|
|
|}
|
2022-02-22 01:48:37 +01:00
|
|
|
pp_post
|
2022-02-21 02:24:17 +01:00
|
|
|
(Op (thread_data, post))
|
2022-02-02 11:58:18 +01:00
|
|
|
in
|
2022-02-21 01:59:36 +01:00
|
|
|
thread_preview
|
2022-01-29 09:34:57 +01:00
|
|
|
|
2022-02-21 00:19:35 +01:00
|
|
|
let catalog_content () =
|
2022-02-21 02:24:17 +01:00
|
|
|
let^ ids = Db.collect_list Q.get_threads () in
|
|
|
|
|
let* ops = get_ops ids in
|
|
|
|
|
Ok
|
|
|
|
|
(Format.asprintf "%a"
|
2022-02-22 00:01:35 +01:00
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_thread_preview)
|
|
|
|
|
ops )
|
2022-02-21 02:24:17 +01:00
|
|
|
|
2022-02-23 22:39:48 +01:00
|
|
|
let pp_report fmt post report request =
|
|
|
|
|
let url = "/admin" in
|
2022-03-08 21:20:01 +01:00
|
|
|
let _reporter_id, reporter_nick, reason, _date, id = report in
|
2022-02-23 22:39:48 +01:00
|
|
|
let input_post_id fmt id =
|
|
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|<input value="%s" name="post_id" type="hidden"></input>|} id
|
|
|
|
|
in
|
2022-03-17 01:05:25 +01:00
|
|
|
let button fmt action =
|
|
|
|
|
let s = moderation_action_to_string action in
|
2022-02-23 22:39:48 +01:00
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|<button value="%s" name="action" type="submit" class="btn btn-primary">%s</button>|}
|
2022-03-17 01:05:25 +01:00
|
|
|
s (String.uppercase_ascii s)
|
2022-02-23 22:39:48 +01:00
|
|
|
in
|
2022-03-17 01:05:25 +01:00
|
|
|
let form fmt action =
|
2022-02-23 22:39:48 +01:00
|
|
|
Format.fprintf fmt {|%s %a %a </form>|}
|
|
|
|
|
(Dream.form_tag ~action:url request)
|
2022-03-17 01:05:25 +01:00
|
|
|
input_post_id id button action
|
2022-02-23 22:39:48 +01:00
|
|
|
in
|
|
|
|
|
|
|
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|
|
|
|
|
|
<div class="report">
|
|
|
|
|
<div class="row mb-3">
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
%a
|
|
|
|
|
</div>
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
<span> From: %s Reason: %s</span>
|
|
|
|
|
<div>
|
|
|
|
|
%a
|
|
|
|
|
</form><br>
|
|
|
|
|
%a
|
|
|
|
|
</form><br>
|
|
|
|
|
%a
|
|
|
|
|
</form><br>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div><br>
|
|
|
|
|
|}
|
2022-03-17 01:05:25 +01:00
|
|
|
pp_post (Post post) reporter_nick reason form Ignore form Delete form Banish
|
2022-02-23 22:39:48 +01:00
|
|
|
|
|
|
|
|
let admin_page_content posts reports request =
|
|
|
|
|
let posts_reports = List.combine posts reports in
|
|
|
|
|
Format.asprintf "%a"
|
|
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space
|
|
|
|
|
(fun fmt (post, report) -> pp_report fmt post report request) )
|
|
|
|
|
posts_reports
|
|
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let pp_thread fmt op posts =
|
|
|
|
|
let thread_data, _post = op in
|
2022-02-18 01:37:25 +01:00
|
|
|
(*order by date *)
|
2022-02-21 02:24:17 +01:00
|
|
|
let posts = List.sort (fun a b -> compare a.date b.date) posts in
|
2022-02-22 00:29:31 +01:00
|
|
|
let posts_view fmt () =
|
2022-02-23 14:30:06 +01:00
|
|
|
Format.pp_print_list ~pp_sep:Format.pp_print_space
|
|
|
|
|
(fun fmt post -> pp_post fmt (Post post))
|
|
|
|
|
fmt posts
|
2022-02-21 02:24:17 +01:00
|
|
|
in
|
|
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|
|
2022-02-02 11:58:18 +01:00
|
|
|
<div class="thread">
|
2022-02-18 20:17:24 +01:00
|
|
|
<div class="thread-subject">
|
2022-02-26 01:36:40 +01:00
|
|
|
<h1>%s</h1>
|
2022-02-02 11:58:18 +01:00
|
|
|
</div>
|
2022-02-18 20:17:24 +01:00
|
|
|
<div class="thread-posts">
|
2022-02-22 00:29:31 +01:00
|
|
|
%a
|
2022-02-02 11:58:18 +01:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|}
|
2022-02-22 00:29:31 +01:00
|
|
|
thread_data.subject posts_view ()
|
2022-02-21 00:19:35 +01:00
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let view_thread thread_id =
|
|
|
|
|
let* op = get_op thread_id in
|
|
|
|
|
let^ ids = Db.collect_list Q.get_thread_posts thread_id in
|
|
|
|
|
let* posts = get_posts ids in
|
|
|
|
|
let s =
|
|
|
|
|
(Format.asprintf "%a" (fun fmt (op, posts) -> pp_thread fmt op posts))
|
|
|
|
|
(op, posts)
|
|
|
|
|
in
|
|
|
|
|
Ok s
|
2022-02-21 00:19:35 +01:00
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let pp_marker fmt op =
|
|
|
|
|
let thread_data, post = op in
|
|
|
|
|
let content = Format.asprintf "%a" pp_thread_preview op in
|
|
|
|
|
(* geojson use lng lat, and not lat lng*)
|
|
|
|
|
let json =
|
|
|
|
|
`Assoc
|
|
|
|
|
[ ("type", `String "Feature")
|
|
|
|
|
; ( "geometry"
|
|
|
|
|
, `Assoc
|
|
|
|
|
[ ("type", `String "Point")
|
|
|
|
|
; ( "coordinates"
|
|
|
|
|
, `List [ `Float thread_data.lng; `Float thread_data.lat ] )
|
|
|
|
|
] )
|
|
|
|
|
; ( "properties"
|
|
|
|
|
, `Assoc
|
|
|
|
|
[ ("content", `String content); ("thread_id", `String post.id) ] )
|
|
|
|
|
]
|
|
|
|
|
in
|
|
|
|
|
Yojson.pretty_print fmt json
|
2022-02-21 00:19:35 +01:00
|
|
|
|
2022-02-21 02:24:17 +01:00
|
|
|
let get_markers () =
|
|
|
|
|
let^ ids = Db.collect_list Q.get_threads () in
|
|
|
|
|
let* ops = get_ops ids in
|
|
|
|
|
let markers =
|
|
|
|
|
Format.asprintf "[%a]"
|
|
|
|
|
(Format.pp_print_list
|
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt ",")
|
2022-02-22 00:01:35 +01:00
|
|
|
pp_marker )
|
2022-02-21 02:24:17 +01:00
|
|
|
ops
|
|
|
|
|
in
|
|
|
|
|
Ok markers
|
2022-02-27 13:45:43 +01:00
|
|
|
|
2022-03-14 17:20:02 +01:00
|
|
|
let pp_checkboxes fmt () =
|
2022-03-09 14:36:19 +01:00
|
|
|
let pp_checkbox fmt category =
|
|
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|
|
|
|
|
|
<div class="form-check col">
|
|
|
|
|
<input name="category" id="category-%s" type="checkbox" class"form-check-input" value="%s">
|
|
|
|
|
<label class="form-check-label" for="category-%s">%s</label>
|
|
|
|
|
</div>
|
|
|
|
|
|}
|
|
|
|
|
category category category category
|
|
|
|
|
in
|
2022-03-14 17:20:02 +01:00
|
|
|
Format.fprintf fmt
|
2022-03-09 14:36:19 +01:00
|
|
|
{|
|
|
|
|
|
<div class="row">
|
|
|
|
|
%a
|
|
|
|
|
</div>|}
|
|
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_checkbox)
|
|
|
|
|
App.categories
|
|
|
|
|
|
2022-02-27 13:45:43 +01:00
|
|
|
(* RFC-3339 date-time *)
|
|
|
|
|
let pp_date fmt date =
|
|
|
|
|
let date = Unix.gmtime date in
|
|
|
|
|
Format.fprintf fmt "%04d-%02d-%02dT%02d:%02d:%02dZ" (1900 + date.tm_year)
|
|
|
|
|
(1 + date.tm_mon) date.tm_mday date.tm_hour date.tm_min date.tm_sec
|
|
|
|
|
|
|
|
|
|
let pp_feed_entry fmt post =
|
|
|
|
|
Format.fprintf fmt
|
|
|
|
|
{|
|
|
|
|
|
<entry>
|
2022-02-27 19:44:19 +01:00
|
|
|
<title></title>
|
2022-02-27 13:45:43 +01:00
|
|
|
<id>urn:uuid:%s</id>
|
|
|
|
|
<updated>%a</updated>
|
|
|
|
|
<author>
|
|
|
|
|
<name>%s</name>
|
|
|
|
|
</author>
|
|
|
|
|
<content type="html">%s</content>
|
|
|
|
|
<link rel="alternate" href="%s/thread/%s#%s"/>
|
|
|
|
|
</entry>
|
|
|
|
|
|}
|
|
|
|
|
post.id pp_date post.date post.nick
|
|
|
|
|
(Dream.html_escape post.comment)
|
2022-02-27 19:44:19 +01:00
|
|
|
App.hostname post.parent_id post.id
|
2022-02-27 13:45:43 +01:00
|
|
|
|
|
|
|
|
let feed thread_id =
|
|
|
|
|
let* thread_data, op_post = get_op thread_id in
|
|
|
|
|
let^ ids = Db.collect_list Q.get_thread_posts thread_id in
|
|
|
|
|
let* posts = get_posts ids in
|
|
|
|
|
let posts = List.sort (fun a b -> compare b.date a.date) posts in
|
2022-02-27 19:58:32 +01:00
|
|
|
let* last_update =
|
2022-02-28 18:35:16 +01:00
|
|
|
match posts with [] -> Error "empty thread" | op :: _l -> Ok op.date
|
2022-02-27 19:58:32 +01:00
|
|
|
in
|
|
|
|
|
|
2022-02-27 13:45:43 +01:00
|
|
|
let entries fmt () =
|
|
|
|
|
(Format.pp_print_list ~pp_sep:Format.pp_print_space pp_feed_entry) fmt posts
|
|
|
|
|
in
|
|
|
|
|
let feed =
|
|
|
|
|
Format.asprintf
|
|
|
|
|
{|<?xml version="1.0" encoding="utf-8"?>
|
|
|
|
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
2022-02-27 19:44:19 +01:00
|
|
|
<title>%s</title>
|
2022-02-27 13:45:43 +01:00
|
|
|
<link rel="self" href="%s/thread/%s"/>
|
|
|
|
|
<updated>%a</updated>
|
|
|
|
|
<author>
|
|
|
|
|
<name>%s</name>
|
|
|
|
|
</author>
|
|
|
|
|
<id>urn:uuid:%s</id>
|
|
|
|
|
%a
|
2022-02-27 19:44:19 +01:00
|
|
|
</feed>|}
|
|
|
|
|
thread_data.subject App.hostname thread_id pp_date last_update
|
|
|
|
|
op_post.nick op_post.id entries ()
|
2022-02-27 13:45:43 +01:00
|
|
|
in
|
|
|
|
|
Ok feed
|