Add support for using {{macros}} (no support for editing yet).
[cocanwiki.git] / scripts / lib / wikilib.ml
index 557d7a9..a4a8e77 100644 (file)
@@ -1,7 +1,7 @@
 (* COCANWIKI - a wiki written in Objective CAML.
  * Written by Richard W.M. Jones <rich@merjis.com>.
  * Copyright (C) 2004 Merjis Ltd.
- * $Id: wikilib.ml,v 1.5 2006/03/27 16:43:44 rich Exp $
+ * $Id: wikilib.ml,v 1.6 2006/07/26 14:59:04 rich Exp $
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -348,10 +348,10 @@ let _markup_paragraph dbh hostid text =
               (* close tags ignored *)
               escape_html first :: "&lt;/" :: escape_html elem :: "&gt;" ::
                 loop (rest, [])
-          | FoundOpen (first, elem, rest) when elem = "nowiki" ->
+          | FoundOpen (first, "nowiki", rest) ->
               (* handle <nowiki> specially ... *)
-              escape_html first :: loop (rest, elem :: [])
-          | FoundOpen (first, elem, rest) when elem = "br" ->
+              escape_html first :: loop (rest, "nowiki" :: [])
+          | FoundOpen (first, "br", rest) ->
               (* handle <br> specially ... *)
               escape_html first :: "<br/>" :: loop (rest, [])
           | FoundOpen (first, elem, rest) ->
@@ -374,10 +374,10 @@ let _markup_paragraph dbh hostid text =
           | FoundClose (first, elem, rest, elem_rest) ->
               (* non-matching close tag *)
               escape_html first :: "</" :: x :: ">" :: loop (elem_rest, xs)
-          | FoundOpen (first, elem, rest) when elem = "nowiki" ->
+          | FoundOpen (first, "nowiki", rest) ->
               (* handle <nowiki> specially ... *)
-              escape_html first :: loop (rest, elem :: stack)
-          | FoundOpen (first, elem, rest) when elem = "br" ->
+              escape_html first :: loop (rest, "nowiki" :: stack)
+          | FoundOpen (first, "br", rest) ->
               (* handle <br> specially ... *)
               escape_html first :: "<br/>" :: loop (rest, stack)
           | FoundOpen (first, elem, rest) ->
@@ -640,11 +640,53 @@ let numbered_re = Pcre.regexp "^(\\#)\\s+(.*)"
 let preformatted_re = Pcre.regexp "^ (.*)"
 let html_open_re = Pcre.regexp "^<html>\\s*$"
 let html_close_re = Pcre.regexp "^</html>\\s*$"
+let macro_re = Pcre.regexp "^{{(\\w+)}}$"
 
 let xhtml_of_content dbh hostid text =
   (* Split the text into lines. *)
   let lines = Pcre.split ~rex:split_lines_re text in
 
+  (* Do macro expansion before anything else, because macros could
+   * contain <html> sections, etc.
+   *)
+  let is_macro line =
+    try
+      let subs = Pcre.exec ~rex:macro_re line in
+      let name = Pcre.get_substring subs 1 in
+      let rows = PGSQL(dbh) "select 1 from macros
+                              where hostid = $hostid and name = $name" in
+      (match rows with
+       | [] -> false (* Not an actual macro name from the database. *)
+       | [_] -> true (* Is an actual macro name. *)
+       | _ -> assert false (* Uniqueness should stop this from happening. *)
+      )
+    with
+      Not_found -> false
+  in
+  let expand_macro line =
+    try
+      let subs = Pcre.exec ~rex:macro_re line in
+      let name = Pcre.get_substring subs 1 in
+      let content =
+       List.hd (
+         PGSQL(dbh) "select content from macros
+                       where hostid = $hostid and name = $name"
+       ) in
+      (* Split the content into lines of text. *)
+      let lines = Pcre.split ~rex:split_lines_re content in
+      lines
+    with
+      (Not_found | Failure "hd" | ExtList.List.Empty_list) as exn ->
+       failwith ("Wikilib: expand_macro: you should never see this: " ^
+                   Printexc.to_string exn)
+  in
+  let rec loop = function
+    | [] -> []
+    | line :: xs when is_macro line -> expand_macro line @ loop xs
+    | x :: xs -> x :: loop xs
+  in
+  let lines = loop lines in
+
   (* HTML blocks span multiple lines, so isolate these out first. *)
   let rec loop = function
     | [] -> []
@@ -670,36 +712,37 @@ let xhtml_of_content dbh hostid text =
 
   (* Iterate over the lines to isolate headers and paragraphs. *)
   let lines =
-    List.map
-      (function
-        | STpLine line ->
-            if Pcre.pmatch ~rex:preformatted_re line then (
-              let subs = Pcre.exec ~rex:preformatted_re line in
-              let line = Pcre.get_substring subs 1 in
-              STPreformatted [line]
-            )
-             else if Pcre.pmatch ~rex:blank_re line then
-               STBlank
-             else if Pcre.pmatch ~rex:heading_re line then (
-               let subs = Pcre.exec ~rex:heading_re line in
-               let count = String.length (Pcre.get_substring subs 1) + 2 in
-               let line = Pcre.get_substring subs 2 in
-               STHeading (count, line)
-             )
-             else if Pcre.pmatch ~rex:unnumbered_re line then (
-               let subs = Pcre.exec ~rex:unnumbered_re line in
-               let line = Pcre.get_substring subs 2 in
-               STUnnumbered [line]
-             )
-             else if Pcre.pmatch ~rex:numbered_re line then (
-               let subs = Pcre.exec ~rex:numbered_re line in
-               let line = Pcre.get_substring subs 2 in
-               STNumbered [line]
-             ) else
-               STParagraph line
-        | STpHTML html ->
-            STHTML html
-      ) lines in
+    List.map (
+      function
+      | STpLine line ->
+         if Pcre.pmatch ~rex:preformatted_re line then (
+           let subs = Pcre.exec ~rex:preformatted_re line in
+           let line = Pcre.get_substring subs 1 in
+           STPreformatted [line]
+         )
+          else if Pcre.pmatch ~rex:blank_re line then
+            STBlank
+          else if Pcre.pmatch ~rex:heading_re line then (
+            let subs = Pcre.exec ~rex:heading_re line in
+            let count = String.length (Pcre.get_substring subs 1) + 2 in
+            let line = Pcre.get_substring subs 2 in
+            STHeading (count, line)
+          )
+          else if Pcre.pmatch ~rex:unnumbered_re line then (
+            let subs = Pcre.exec ~rex:unnumbered_re line in
+            let line = Pcre.get_substring subs 2 in
+            STUnnumbered [line]
+          )
+          else if Pcre.pmatch ~rex:numbered_re line then (
+            let subs = Pcre.exec ~rex:numbered_re line in
+            let line = Pcre.get_substring subs 2 in
+            STNumbered [line]
+         )
+         else
+            STParagraph line
+      | STpHTML html ->
+         STHTML html
+    ) lines in
 
   (* Aggregate paragraphs and lists. *)
   let rec loop = function