generate an atom feed
This commit is contained in:
parent
72c07b6b55
commit
e71a904120
7
Makefile
7
Makefile
|
@ -3,19 +3,21 @@ cache := .cache
|
||||||
www_root := _site
|
www_root := _site
|
||||||
www := $(www_root)/posts
|
www := $(www_root)/posts
|
||||||
post_index := $(www_root)/posts.html
|
post_index := $(www_root)/posts.html
|
||||||
|
feed := $(www_root)/feed.xml
|
||||||
|
|
||||||
post_compiler := src/webcc.ml
|
post_compiler := src/webcc.ml
|
||||||
builddir := _build/default
|
builddir := _build/default
|
||||||
|
|
||||||
POSTCC := $(builddir)/$(patsubst %.ml,%.exe,$(post_compiler))
|
POSTCC := $(builddir)/$(patsubst %.ml,%.exe,$(post_compiler))
|
||||||
INDEXCC := $(POSTCC) -i
|
INDEXCC := $(POSTCC) -i
|
||||||
|
FEEDCC := $(POSTCC) -a
|
||||||
|
|
||||||
post-sources := $(shell find $(src)/ -type f)
|
post-sources := $(shell find $(src)/ -type f)
|
||||||
post-htmls := $(patsubst $(src)/%.md,$(www)/%.html,$(post-sources))
|
post-htmls := $(patsubst $(src)/%.md,$(www)/%.html,$(post-sources))
|
||||||
|
|
||||||
.PHONY: all serve clean $(POSTCC)
|
.PHONY: all serve clean $(POSTCC)
|
||||||
|
|
||||||
all: $(POSTCC) $(post-htmls) $(post_index) $(www_root)/static
|
all: $(POSTCC) $(post-htmls) $(post_index) $(feed) $(www_root)/static
|
||||||
cp -R ~/www/* $(www_root)
|
cp -R ~/www/* $(www_root)
|
||||||
|
|
||||||
$(www):
|
$(www):
|
||||||
|
@ -27,6 +29,9 @@ $(www)/%.html: $(src)/%.md $(www)
|
||||||
$(post_index): $(post-sources)
|
$(post_index): $(post-sources)
|
||||||
$(INDEXCC) $^ -o $@
|
$(INDEXCC) $^ -o $@
|
||||||
|
|
||||||
|
$(feed): $(post-sources)
|
||||||
|
$(FEEDCC) $^ -o $@
|
||||||
|
|
||||||
$(www_root)/static: static
|
$(www_root)/static: static
|
||||||
cp -R static $(www_root)
|
cp -R static $(www_root)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
open Post
|
||||||
|
|
||||||
|
let format_entry ~title ~date ~url ~contents ~author =
|
||||||
|
String.concat ""
|
||||||
|
[
|
||||||
|
{|<entry>
|
||||||
|
<title>|}; title; {|</title>
|
||||||
|
<id>|};
|
||||||
|
url;
|
||||||
|
{|</id>
|
||||||
|
<author>
|
||||||
|
<name>|}; author; {|</name>
|
||||||
|
</author>
|
||||||
|
<updated>|}; ISO8601.Permissive.string_of_datetime date;{|</updated>
|
||||||
|
<link href="|}; url ;{|" rel="alternate" />
|
||||||
|
<content type="xhtml"> <div xmlns="http://www.w3.org/1999/xhtml">|};
|
||||||
|
contents;
|
||||||
|
{| </div></content>
|
||||||
|
</entry>
|
||||||
|
|}]
|
||||||
|
|
||||||
|
let format_feed ~title ~subtitle ~author ~feed_url ~site_url ~last_updated ~(entries: Post.t list) =
|
||||||
|
let opener =
|
||||||
|
String.concat "" [
|
||||||
|
{|<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<title>|}; title; {|</title>
|
||||||
|
<id>|}; feed_url; {|</id>
|
||||||
|
<subtitle>|}; subtitle; {|</subtitle>
|
||||||
|
<updated>|}; ISO8601.Permissive.string_of_datetime last_updated; {|</updated>
|
||||||
|
<link href="|}; feed_url; {|" rel="self"/>
|
||||||
|
<link href="|}; site_url; {|"/>
|
||||||
|
|}] in
|
||||||
|
opener ^
|
||||||
|
(String.concat "\n" @@ List.map
|
||||||
|
(fun (md,contents) ->
|
||||||
|
format_entry
|
||||||
|
~title:md.title
|
||||||
|
~date:md.date
|
||||||
|
~url:(site_url ^ md.filename)
|
||||||
|
~author
|
||||||
|
~contents)
|
||||||
|
entries) ^
|
||||||
|
"</feed>"
|
3
src/dune
3
src/dune
|
@ -1,3 +1,4 @@
|
||||||
(executable
|
(executable
|
||||||
(name webcc)
|
(name webcc)
|
||||||
(libraries omd ISO8601 str))
|
(libraries omd ISO8601 str)
|
||||||
|
(modules_without_implementation post))
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
type date = float (* repr for the ISO8601 library *)
|
||||||
|
type metadata = { title : string ; date : date ; tags : string list ;
|
||||||
|
filename : string ;
|
||||||
|
other : (string * string) list }
|
||||||
|
type t = metadata * string
|
76
src/webcc.ml
76
src/webcc.ml
|
@ -1,21 +1,27 @@
|
||||||
|
open Post
|
||||||
|
|
||||||
|
let author = "Daniel"
|
||||||
|
let site_url = "https://groupoid.moe"
|
||||||
|
let feed_url = "https://groupoid.moe/feed.xml"
|
||||||
|
let site_title = "groupoid.moe"
|
||||||
|
let feed_title = "Recent posts"
|
||||||
let input = ref []
|
let input = ref []
|
||||||
let output = ref ""
|
let output = ref ""
|
||||||
let gen_index = ref false
|
let gen_index = ref false
|
||||||
|
let gen_atom = ref false
|
||||||
|
|
||||||
let spec =
|
let spec =
|
||||||
[
|
[
|
||||||
("-o", Arg.Set_string output,
|
("-o", Arg.Set_string output,
|
||||||
" file.html Specify the output file (default is stdout).");
|
" file.html Specify the output file (default is stdout).");
|
||||||
("-i", Arg.Set gen_index,
|
("-i", Arg.Set gen_index,
|
||||||
" generate a post index instead of compiling individual posts.");
|
" generate the post index instead of compiling individual posts.");
|
||||||
|
("-a", Arg.Set gen_atom,
|
||||||
|
" generate the atom feed.");
|
||||||
("--", Rest(fun s -> input := s :: !input),
|
("--", Rest(fun s -> input := s :: !input),
|
||||||
" Consider all remaining arguments as input file names.")
|
" Consider all remaining arguments as input file names.")
|
||||||
]
|
]
|
||||||
|
|
||||||
type post_metadata = { title : string ; date : float ; tags : string list ;
|
|
||||||
filename : string ;
|
|
||||||
other : (string * string) list }
|
|
||||||
|
|
||||||
(** Utitilies *)
|
(** Utitilies *)
|
||||||
let with_open_in fn f =
|
let with_open_in fn f =
|
||||||
let ic = open_in fn in
|
let ic = open_in fn in
|
||||||
|
@ -29,7 +35,7 @@ let with_open_out fn f =
|
||||||
| r -> close_out oc; r
|
| r -> close_out oc; r
|
||||||
| exception e -> close_out_noerr oc; raise e
|
| exception e -> close_out_noerr oc; raise e
|
||||||
|
|
||||||
let read_metadata filename ic : post_metadata =
|
let read_metadata filename ic : Post.metadata =
|
||||||
let rec go lst =
|
let rec go lst =
|
||||||
let line =
|
let line =
|
||||||
try input_line ic
|
try input_line ic
|
||||||
|
@ -46,12 +52,23 @@ let read_metadata filename ic : post_metadata =
|
||||||
| None -> "-"
|
| None -> "-"
|
||||||
in
|
in
|
||||||
let date = ISO8601.Permissive.date (lookup "date") in
|
let date = ISO8601.Permissive.date (lookup "date") in
|
||||||
|
(* TODO! Fix the paths here!!!!!! *)
|
||||||
|
let target_fname =
|
||||||
|
let open Str in
|
||||||
|
if string_match (regexp {|content/\(.*\)\.md|}) filename 0
|
||||||
|
then "/posts/" ^ matched_group 1 filename ^ ".html"
|
||||||
|
else filename
|
||||||
|
in
|
||||||
{ title = lookup "title";
|
{ title = lookup "title";
|
||||||
date = date;
|
date = date;
|
||||||
tags = String.split_on_char ',' (lookup "tags");
|
tags = String.split_on_char ',' (lookup "tags");
|
||||||
filename = filename;
|
filename = target_fname;
|
||||||
other = md }
|
other = md }
|
||||||
|
|
||||||
|
let read_post filename ic : Post.t =
|
||||||
|
let metadata = read_metadata filename ic in
|
||||||
|
let md = Omd.of_channel ic in
|
||||||
|
(metadata, Omd.to_html md)
|
||||||
|
|
||||||
(** Functions for processing an individual post *)
|
(** Functions for processing an individual post *)
|
||||||
|
|
||||||
|
@ -97,16 +114,16 @@ let format_post ~title ~tags ~date ~contents =
|
||||||
|
|
||||||
(* convert contents from [ic] to a post webpage, output to [oc] *)
|
(* convert contents from [ic] to a post webpage, output to [oc] *)
|
||||||
let process filename ic oc =
|
let process filename ic oc =
|
||||||
let metadata = read_metadata filename ic in
|
let (metadata, contents) = read_post filename ic in
|
||||||
let md = Omd.of_channel ic in
|
|
||||||
let html = format_post
|
let html = format_post
|
||||||
~title:(metadata.title)
|
~title:(metadata.title)
|
||||||
~tags:(String.concat ", " metadata.tags)
|
~tags:(String.concat ", " metadata.tags)
|
||||||
~date:(ISO8601.Permissive.string_of_date metadata.date)
|
~date:(ISO8601.Permissive.string_of_date metadata.date)
|
||||||
~contents:(Omd.to_html md)
|
~contents
|
||||||
in
|
in
|
||||||
output_string oc html
|
output_string oc html
|
||||||
|
|
||||||
|
(** Compile the posts to HTML *)
|
||||||
let compile_posts oc =
|
let compile_posts oc =
|
||||||
if !input = [] then process "<stdin>" stdin oc
|
if !input = [] then process "<stdin>" stdin oc
|
||||||
else begin
|
else begin
|
||||||
|
@ -115,23 +132,14 @@ let compile_posts oc =
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
(** Generating index of posts *)
|
(** Generate the index of posts *)
|
||||||
let generate_index oc =
|
let generate_index oc =
|
||||||
let f filename = with_open_in filename (read_metadata filename) in
|
let f filename = with_open_in filename (read_metadata filename) in
|
||||||
let mds = List.map f !input in
|
let mds = List.map f !input in
|
||||||
let mds = List.sort (fun md1 md2 -> compare md2.date md1.date) mds in
|
let mds = List.sort (fun md1 md2 -> compare md2.date md1.date) mds in
|
||||||
(* TODO! Fix the paths here!!!!!! *)
|
|
||||||
let format_item md =
|
let format_item md =
|
||||||
(* path to the html file *)
|
|
||||||
let fname = md.filename in
|
|
||||||
let target_fname =
|
|
||||||
let open Str in
|
|
||||||
if string_match (regexp {|content/\(.*\)\.md|}) fname 0
|
|
||||||
then matched_group 1 fname ^ ".html"
|
|
||||||
else fname
|
|
||||||
in
|
|
||||||
String.concat "" [
|
String.concat "" [
|
||||||
{|<li><a href="/posts/|}; target_fname; {|">|};
|
{|<li><a href="|}; md.filename; {|">|};
|
||||||
md.title;
|
md.title;
|
||||||
{|</a> - |}; ISO8601.Permissive.string_of_date md.date ; {|</li>|}]
|
{|</a> - |}; ISO8601.Permissive.string_of_date md.date ; {|</li>|}]
|
||||||
in
|
in
|
||||||
|
@ -139,6 +147,28 @@ let generate_index oc =
|
||||||
let html = format_page ~title:"Recent posts" ~contents in
|
let html = format_page ~title:"Recent posts" ~contents in
|
||||||
output_string oc html
|
output_string oc html
|
||||||
|
|
||||||
|
(** Generate the atom feed *)
|
||||||
|
let generate_atom oc =
|
||||||
|
let f filename = with_open_in filename (read_post filename) in
|
||||||
|
let posts = List.map f !input in
|
||||||
|
let posts = List.sort (fun (md1,_) (md2,_) -> compare md2.date md1.date) posts in
|
||||||
|
let last_updated =
|
||||||
|
match posts with
|
||||||
|
| [] -> 0.
|
||||||
|
| (md,_)::_ -> md.date
|
||||||
|
in
|
||||||
|
let xml = Atom.format_feed
|
||||||
|
~title:site_title
|
||||||
|
~subtitle:feed_title
|
||||||
|
~author
|
||||||
|
~feed_url
|
||||||
|
~site_url
|
||||||
|
~last_updated
|
||||||
|
~entries:posts
|
||||||
|
in
|
||||||
|
output_string oc xml
|
||||||
|
|
||||||
|
|
||||||
(** main *)
|
(** main *)
|
||||||
let main () =
|
let main () =
|
||||||
Arg.parse (Arg.align spec)
|
Arg.parse (Arg.align spec)
|
||||||
|
@ -150,10 +180,14 @@ let main () =
|
||||||
else
|
else
|
||||||
with_open_out !output f
|
with_open_out !output f
|
||||||
in
|
in
|
||||||
|
begin
|
||||||
with_output @@
|
with_output @@
|
||||||
if !gen_index
|
if !gen_index
|
||||||
then generate_index
|
then generate_index
|
||||||
else compile_posts
|
else compile_posts
|
||||||
|
end;
|
||||||
|
if !gen_atom then with_output generate_atom
|
||||||
|
|
||||||
|
|
||||||
let () =
|
let () =
|
||||||
try
|
try
|
||||||
|
|
Loading…
Reference in New Issue