Fixed some bitrot and some unused variables.
[cocanwiki.git] / scripts / edit.ml
index 2e9ee50..c9aa1df 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: edit.ml,v 1.24 2004/11/01 12:57:53 rich Exp $
+ * $Id: edit.ml,v 1.29 2006/07/26 13:12:10 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
@@ -34,7 +34,7 @@ open Cocanwiki_diff
 open Cocanwiki_strings
 open Cocanwiki_pages
 
-let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
+let run r (q : cgi) dbh hostid {hostname = hostname} user =
   let template = get_template dbh hostid "edit.html" in
   let template_conflict = get_template dbh hostid "edit_conflict.html" in
   let template_email = get_template dbh hostid "edit_page_email.txt" in
@@ -48,16 +48,19 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
       ignore (String.find ua "MSIE"); (* Throws Invalid_string if not found. *)
       true
     with
-       Not_found | String.Invalid_string -> false in
+       Not_found | Invalid_string -> false in
   template#conditional "msie" msie;
 
   (* Build the internal model from the parameters passed to the script. *)
   let build_internal_model () =
-    let id = int_of_string (q#param "id") in
+    let id = Int32.of_string (q#param "id") in
     let description = q#param "description" in
     let redirect = q#param "redirect" in
+    let redirect =
+      if string_is_whitespace redirect then
+       None else Some redirect in
     let pt = match q#param "pt_type" with
-       "page" -> Page (q#param "pt_value")
+      | "page" -> Page (q#param "pt_value")
       | "title" -> Title (q#param "pt_value")
       | _ -> failwith "unknown value for pt_type parameter" in
 
@@ -65,8 +68,12 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
     let i = ref 1 in
     while q#param_exists ("content_" ^ string_of_int !i) do
       let sectionname = q#param ("sectionname_" ^ string_of_int !i) in
+      let sectionname =
+       if string_is_whitespace sectionname then None else Some sectionname in
       let content = q#param ("content_" ^ string_of_int !i) in
       let divname = q#param ("divname_" ^ string_of_int !i) in
+      let divname =
+       if string_is_whitespace divname then None else Some divname in
       contents := (sectionname, divname, content) :: !contents;
       incr i
     done;
@@ -76,7 +83,7 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
       pt = pt;
       description = description;
       redirect = redirect;
-      contents = contents; }
+      contents_ = contents; }
   in
 
   (* Check for errors in the model. *)
@@ -85,44 +92,45 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
     let add_error msg = errors := msg :: !errors in
     let get_errors () = List.rev !errors in
 
-    if model.redirect = "" then (
-      (* Empty page? *)
-      if model.contents = [] then
-       add_error ("This page is empty.  Use 'Insert new section here' " ^
-                  "to write something!");
-
-      (* Description field? *)
-      if model.description = "" then
-       add_error ("The description field is very important!  This field is " ^
-                   "used by search engines and directories to describe " ^
-                   "what's on this page.");
-    )
-    else (* it's a redirect *) (
-      (* Redirect points to a real page? *)
-      let sth = dbh#prepare_cached "select 1 from pages
-                                     where hostid = ?
-                                       and url is not null
-                                       and url = ?
-                                       and id <> ?
-                                       and redirect is null" in
-      sth#execute [`Int hostid; `String model.redirect; `Int model.id];
-
-      let ok = try sth#fetch1 () = [`Int 1] with Not_found -> false in
-      if not ok then
-       add_error ("Redirect must point to an ordinary page " ^
-                  "(ie. not to a page which is itself a redirect).")
+    (match model.redirect with
+     | None ->
+        (* Empty page? *)
+        if model.contents_ = [] then
+          add_error ("This page is empty.  Use 'Insert new section here' " ^
+                       "to write something!");
+
+        (* Description field? *)
+        if model.description = "" then
+          add_error ("The description field is very important!  " ^
+                       "This field is " ^
+                       "used by search engines and directories to describe " ^
+                       "what's on this page.");
+
+     | Some redirect ->
+        (* Redirect points to a real page? *)
+        let rows =
+          let model_id = model.id in
+          PGSQL(dbh)
+          "select 1 from pages
+             where hostid = $hostid and url is not null
+               and url = $redirect and id <> $model_id
+               and redirect is null" in
+
+        let ok = rows = [Some 1l] in
+        if not ok then
+          add_error ("Redirect must point to an ordinary page " ^
+                       "(ie. not to a page which is itself a redirect).")
     );
 
     (* All sections after the first one have sectionnames?  The first
      * section ONLY is allowed to have an empty title.
      *)
-    if model.contents <> [] then
-      List.iter (function (sectionnames, _, _)
-                    when string_is_whitespace sectionnames ->
-                      add_error
-                      ("Every section except the first must have a title.");
-                  | _ -> ())
-       (List.tl model.contents);
+    if model.contents_ <> [] then
+      List.iter (function (None, _, _) ->
+                  add_error
+                    ("Every section except the first must have a title.");
+                | _ -> ())
+       (List.tl model.contents_);
 
     get_errors ()
   in
@@ -136,8 +144,8 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
         | _, [] -> [ item ]
         | n, x :: xs -> x :: (loop (n-1, xs))
     in
-    let contents = loop (posn, model.contents) in
-    { model with contents = contents }
+    let contents = loop (posn, model.contents_) in
+    { model with contents_ = contents }
   in
   let action_moveup model posn =
     (* posn = 1 means move up the first element, ie. do nothing
@@ -152,8 +160,8 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
         | 2, x :: y :: xs -> y :: x :: xs
         | n, x :: xs -> x :: (loop (n-1, xs))
     in
-    let contents = loop (posn, model.contents) in
-    { model with contents = contents }
+    let contents = loop (posn, model.contents_) in
+    { model with contents_ = contents }
   in
   let action_movedn model posn =
     (* posn = 1 means move down the first element to the second position
@@ -166,8 +174,8 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
         | 1, x :: y :: xs -> y :: x :: xs
         | n, x :: xs -> x :: (loop (n-1, xs))
     in
-    let contents = loop (posn, model.contents) in
-    { model with contents = contents }
+    let contents = loop (posn, model.contents_) in
+    { model with contents_ = contents }
   in
   let action_delete model posn =
     (* posn = 1 means delete the first element *)
@@ -178,13 +186,13 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
         | 1, x :: xs -> xs
         | n, x :: xs -> x :: (loop (n-1, xs))
     in
-    let contents = loop (posn, model.contents) in
-    { model with contents = contents }
+    let contents = loop (posn, model.contents_) in
+    { model with contents_ = contents }
   in
 
   (* Convert model to template. *)
   let model_to_template model template =
-    template#set "id" (string_of_int model.id);
+    template#set "id" (Int32.to_string model.id);
     template#set "description" model.description;
 
     (match model.pt with
@@ -196,42 +204,50 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
           template#set "pt_value" title);
 
     (* Redirects table. *)
-    let sth = dbh#prepare_cached "select url, title from pages
-                                   where url is not null
-                                     and redirect is null
-                                     and hostid = ? and id <> ?
-                                   order by 2" in
-    sth#execute [`Int hostid; `Int model.id];
-    let table = sth#map (function [`String url; `String title] ->
-                          let selected = model.redirect = url in
-                          [ "url", Template.VarString url;
-                            "title", Template.VarString title;
-                            "selected", Template.VarConditional selected ]
-                          | _ -> assert false) in
+    let rows =
+      let model_id = model.id in
+      PGSQL(dbh)
+      "select url, title from pages
+        where url is not null
+          and redirect is null
+          and hostid = $hostid and id <> $model_id
+        order by 2" in
+    let table = List.map (
+      fun (url, title) ->
+       let url = Option.get url in
+       let selected = model.redirect = Some url in
+       [ "url", Template.VarString url;
+         "title", Template.VarString title;
+         "selected", Template.VarConditional selected ]
+    ) rows in
     template#table "redirects" table;
 
-    if model.id <> 0 then (
+    if model.id <> 0l then (
       (* Need to go to the database to get the title of the page ... *)
-      let sth = dbh#prepare_cached "select title from pages
-                                     where hostid = ? and id = ?" in
-      sth#execute [`Int hostid; `Int model.id];
-      let title = sth#fetch1string () in
+      let rows =
+       let model_id = model.id in
+       PGSQL(dbh)
+         "select title from pages
+            where hostid = $hostid and id = $model_id" in
+      let title = List.hd rows in
       template#set "title" title;
     ) else (
       match model.pt with
-         Page page -> template#set "title" page
-       | Title title -> template#set "title" title
+      | Page page -> template#set "title" page
+      | Title title -> template#set "title" title
     );
 
     let ordering = ref 0 in
     let table =
       List.map
        (fun (sectionname, divname, content) ->
-          incr ordering; let ordering = !ordering in
-          [ "ordering", Template.VarString (string_of_int ordering);
+          incr ordering; let ordering = Int32.of_int !ordering in
+          let sectionname = match sectionname with None -> "" | Some s -> s in
+          let divname = match divname with None -> "" | Some s -> s in
+          [ "ordering", Template.VarString (Int32.to_string ordering);
             "sectionname", Template.VarString sectionname;
             "divname", Template.VarString divname;
-            "content", Template.VarString content ]) model.contents in
+            "content", Template.VarString content ]) model.contents_ in
     template#table "contents" table;
 
     (* Check for errors and put those into the template. *)
@@ -244,11 +260,9 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
 
   (* Check if a URL exists in the database. *)
   let page_exists page =
-    let sth =
-      dbh#prepare_cached "select 1 from pages where hostid = ? and url = ?" in
-    sth#execute [`Int hostid; `String page];
-
-    try sth#fetch1int () = 1 with Not_found -> false
+    let rows = PGSQL(dbh)
+      "select 1 from pages where hostid = $hostid and url = $page" in
+    rows = [ Some 1l ]
   in
 
   (* Begin editing a page, pulling the page out of the database and building
@@ -261,22 +275,23 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
 
   (* Begin editing with a blank page, typically a template. *)
   let begin_editing_new pt =
-    let url, title =
-      match pt with
-         Page url -> url, url
-       | Title title ->
-           match Wikilib.generate_url_of_title dbh hostid title with
-               Wikilib.GenURL_OK url -> url, title
-             | Wikilib.GenURL_Duplicate url ->
-                 q#redirect ("http://" ^ hostname ^ "/" ^ url);
-                 return ()
-             | Wikilib.GenURL_TooShort | Wikilib.GenURL_BadURL ->
-                 error ~back_button:true ~title:"Bad page name"
-                   q "The page name supplied is too short or invalid.";
-                 return () in
+    (* Just check the title. *)
+    (match pt with
+     | Page url -> ()
+     | Title title ->
+        match Wikilib.generate_url_of_title dbh hostid title with
+        | Wikilib.GenURL_OK url -> ()
+        | Wikilib.GenURL_Duplicate url ->
+            q#redirect ("http://" ^ hostname ^ "/" ^ url)
+        | Wikilib.GenURL_TooShort | Wikilib.GenURL_BadURL ->
+            error ~back_button:true ~title:"Bad page name"
+              dbh hostid q
+              "The page name supplied is too short or invalid.";
+            return ()
+    );
 
     let model = match pt with
-       Page url -> new_page pt
+      | Page url -> new_page pt
       | Title title -> new_page_with_title title in
 
     model_to_template model template
@@ -321,7 +336,8 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
 
     if is_action "insert" then (
       let posn = get_action "insert" in
-      let item = "New section - change this", "", "Write some content here." in
+      let item =
+       Some "The title of this section", None, "Write something here." in
       model := action_insert !model posn item
     ) else if is_action "moveup" then (
       let posn = get_action "moveup" in
@@ -352,8 +368,8 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
        with
            SaveURLError ->
              error ~back_button:true ~title:"Page exists"
-             q ("While you were editing that page, it looks " ^
-                "like another user created the same page.");
+               dbh hostid q ("While you were editing that page, it looks " ^
+                             "like another user created the same page.");
              return ()
 
          | SaveConflict (new_version, old_version, url, css) ->
@@ -364,15 +380,20 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
              (* Synthesize our own changes. *)
              let old_page = get_version_for_diff dbh old_version in
              let new_page =
-               page_for_diff css (List.map (fun (sectionname, _, content) ->
-                               sectionname, content) model.contents) in
+               page_for_diff css (List.map (
+                                    fun (sectionname, _, content) ->
+                                      let sectionname = match sectionname with
+                                        | None -> ""
+                                        | Some s -> s in
+                                      sectionname, content
+                                  ) model.contents_) in
              let our_diff = diff_cmd old_page new_page in
 
              (* Fill out the conflict template. *)
              template_conflict#set "other_diff" other_diff;
              template_conflict#set "our_diff" our_diff;
-             template_conflict#set "old_version" (string_of_int old_version);
-             template_conflict#set "new_version" (string_of_int new_version);
+             template_conflict#set "old_version" (Int32.to_string old_version);
+             template_conflict#set "new_version" (Int32.to_string new_version);
              template_conflict#set "url" url;
 
              q#template template_conflict;
@@ -382,15 +403,15 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
        * anyone in the page_emails table who has a confirmed address
        * and who hasn't received an email already today.
        *)
-      let sth = dbh#prepare_cached "select email, opt_out from page_emails
-                                     where hostid = ? and url = ?
-                                       and pending is null
-                                       and last_sent < current_date" in
-      sth#execute [`Int hostid; `String url];
-
-      let addrs = sth#map (function [`String email; `String opt_out] ->
-                            email, opt_out
-                            | _ -> assert false) in
+      let rows = PGSQL(dbh)
+       "select email, opt_out from page_emails
+          where hostid = $hostid and url = $url
+            and pending is null
+            and last_sent < current_date" in
+      let addrs = List.map (
+       fun (email, opt_out) ->
+         email, opt_out
+      ) rows in
 
       if addrs <> [] then (
        (* Construct the email. *)
@@ -407,19 +428,19 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
                     template_email#set "opt_out" opt_out;
                     let body = template_email#to_string in
                     Sendmail.send_mail ~subject
-                      ~to_addr:[to_addr] ~body ())
+                      ~to_addr:[to_addr] body)
          addrs
       );
 
       (* Update the database to record when these emails were sent. *)
-      let sth = dbh#prepare_cached "update page_emails
-                                       set last_sent = current_date
-                                     where hostid = ? and url = ?
-                                       and pending is null" in
-      sth#execute [`Int hostid; `String url];
+      PGSQL(dbh)
+       "update page_emails
+            set last_sent = current_date
+          where hostid = $hostid and url = $url
+            and pending is null";
 
       (* Commit changes to the database. *)
-      dbh#commit ();
+      PGOCaml.commit dbh;
 
       (* Email notification, if anyone is listed for this host. *)
       let subject = "Page " ^ url ^ " has been edited" in
@@ -433,19 +454,18 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
       email_notify ~body ~subject ~user dbh hostid;
 
       (* Redirect back to the URL. *)
-      q#redirect ("http://" ^ hostname ^ "/" ^ url);
-      return ()
+      q#redirect ("http://" ^ hostname ^ "/" ^ url)
     );
   in
 
   let cancel id =
     let url =
-      if id <> 0 then (
-       let sth = dbh#prepare_cached "select coalesce (url, url_deleted)
-                                        from pages
-                                       where hostid = ? and id = ?" in
-       sth#execute [`Int hostid; `Int id];
-       sth#fetch1string ()
+      if id <> 0l then (
+       let rows = PGSQL(dbh)
+         "select coalesce (url, url_deleted)
+             from pages
+            where hostid = $hostid and id = $id" in
+       Option.get (List.hd rows)
       ) else if q#param "pt_type" = "page" then
        q#param "pt_value"
       else
@@ -455,11 +475,10 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
         *)
        "" in
 
-    q#redirect ("http://" ^ hostname ^ "/" ^ url);
-    return ()
+    q#redirect ("http://" ^ hostname ^ "/" ^ url)
   in
 
-  (* This codes decides where we are in the current editing cycle.
+  (* This code decides where we are in the current editing cycle.
    *
    * Inputs:
    *   id - if set, then we are in the midst of editing a page.
@@ -471,7 +490,7 @@ let run r (q : cgi) (dbh : Dbi.connection) hostid {hostname = hostname} user =
    *   title - page doesn't yet exist; create it.
    *)
   let id =
-    try Some (int_of_string (q#param "id")) with Not_found -> None in
+    try Some (Int32.of_string (q#param "id")) with Not_found -> None in
   (match id with
      | None ->                         (* Begin editing the page. *)
         if q#param_exists "page" then (