+csv dep for PG'OCaml.
[cocanwiki.git] / scripts / lib / cocanwiki_pages.ml
index 948f8a5..baba26e 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: cocanwiki_pages.ml,v 1.5 2006/03/27 16:43:44 rich Exp $
+ * $Id: cocanwiki_pages.ml,v 1.12 2006/12/07 15:46:54 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
@@ -26,41 +26,49 @@ open Cocanwiki_strings
 
 type pt = Page of string | Title of string
 
+type section =
+    string option * string option * string option * string option * string
+    (* (sectionname, divname, divclass, jsgo, content) *)
+
 type model = {
   id : int32;                          (* Original page ID (0 = none). *)
   pt : pt;                             (* Page of title (only used if id=0) *)
   description : string;                        (* Description. *)
+  keywords : string option;            (* Keywords. *)
+  noodp : bool option;                 (* NOODP per-page override. *)
   redirect : string option;            (* Redirect to. *)
   (* NB. Don't call this 'contents' because that clashes with the
    * Pervasives.contents fields of the ref type.
    *)
-  contents_ : (string option * string option * string) list;
-                                       (* (sectionname, divname, content)
-                                        * for each section. *)
+  contents_ : section list;
 }
 
 exception SaveURLError
-exception SaveConflict of int32 * int32 * string * string
+exception SaveConflict of int32 * int32 * string * string option
 
 let new_page pt =
   let description =
     match pt with
-       Page page -> page
-      | Title title -> title in
+    | Page page -> page
+    | Title title -> title in
 
   let model = { id = 0l;
                pt = pt;
                description = description;
+               keywords = None;
+               noodp = None;
                redirect = None;
                contents_ = [] } in
   model
 
 let new_page_with_title title =
   (* Initial page contents. *)
-  let contents = [ None, None, "<b>" ^ title ^ "</b> is " ] in
+  let contents = [ None, None, None, None, "<b>" ^ title ^ "</b> is " ] in
   let model = { id = 0l;
                pt = Title title;
                description = title;
+               keywords = None;
+               noodp = None;
                redirect = None;
                contents_ = contents } in
   model
@@ -70,23 +78,23 @@ let load_page dbh hostid ~url ?version () =
   let rows =
     match version with
     | None ->
-       PGSQL(dbh) "select id, title, description, redirect
+       PGSQL(dbh) "select id, title, description, keywords, noodp, redirect
                       from pages
                      where hostid = $hostid and url = $url"
     | Some version ->
-       PGSQL(dbh) "select id, title, description, redirect
+       PGSQL(dbh) "select id, title, description, keywords, noodp, redirect
                       from pages
                      where hostid = $hostid and id = $version and
                            (url = $url or url_deleted = $url)" in
 
-  let pageid, title, description, redirect =
+  let pageid, title, description, keywords, noodp, redirect =
     match rows with
     | [row] -> row
     | _ -> raise Not_found in
 
   (* Get the sections. *)
   let contents = PGSQL(dbh)
-    "select sectionname, divname, content
+    "select sectionname, divname, divclass, jsgo, content
        from contents
       where pageid = $pageid
       order by ordering" in
@@ -94,11 +102,13 @@ let load_page dbh hostid ~url ?version () =
   let model = { id = pageid;
                pt = Page url;
                description = description;
+               keywords = keywords;
+               noodp = noodp;
                redirect = redirect;
                contents_ = contents } in
   model
 
-let save_page dbh hostid ?user ?r model =
+let save_page r dbh hostid ?user model =
   (* Logging information, if available. *)
   let logged_user =
     match user with
@@ -109,11 +119,8 @@ let save_page dbh hostid ?user ?r model =
            | _ -> None in
 
   let logged_ip =
-    match r with
-       None -> None
-      | Some r ->
-         try Some (Connection.remote_ip (Request.connection r))
-         with Not_found -> None in
+    try Some (Connection.remote_ip (Request.connection r))
+    with Not_found -> None in
 
   let url, pageid =
     (* Creating a new page (id = 0)?  If so, we're just going to insert
@@ -125,17 +132,21 @@ let save_page dbh hostid ?user ?r model =
        match model.pt with
            Page url -> url, url
          | Title title ->
-             match Wikilib.generate_url_of_title dbh hostid title with
+             match Wikilib.generate_url_of_title dbh hostid title with
                  Wikilib.GenURL_OK url -> url, title
                | _ ->
                    raise SaveURLError in
 
       let description = model.description in
+      let keywords = model.keywords in
+      let noodp = model.noodp in
       let redirect = model.redirect in
       PGSQL(dbh) "insert into pages (hostid, url, title,
-                                     description, logged_ip, logged_user,
+                                     description, keywords, noodp,
+                                     logged_ip, logged_user,
                                      redirect)
-                  values ($hostid, $url, $title, $description,
+                  values ($hostid, $url, $title, $description, $?keywords,
+                          $?noodp,
                           $?logged_ip, $?logged_user, $?redirect)";
 
       let pageid = PGOCaml.serial4 dbh "pages_id_seq" in
@@ -143,13 +154,13 @@ let save_page dbh hostid ?user ?r model =
       (* Create the page contents. *)
       let ordering = ref 0 in  (* Creating new ordering. *)
       List.iter (
-       fun (sectionname, divname, content) ->
+       fun (sectionname, divname, divclass, jsgo, content) ->
          incr ordering; let ordering = Int32.of_int !ordering in
          PGSQL(dbh)
            "insert into contents (pageid, ordering, sectionname, divname,
-                                   content)
+                                   divclass, jsgo, content)
              values ($pageid, $ordering,
-                     $?sectionname, $?divname, $content)"
+                     $?sectionname, $?divname, $?divclass, $?jsgo, $content)"
       ) model.contents_;
 
       url, pageid
@@ -187,10 +198,8 @@ let save_page dbh hostid ?user ?r model =
 
       let edited = max_id <> model_id in
 
-      if edited then (
-       let css = match css with None -> "" | Some css -> css in
-       raise (SaveConflict (max_id, model_id, url, css))
-      );
+      if edited then
+       raise (SaveConflict (max_id, model_id, url, css));
 
       (* Defer the pages_redirect_cn constraint because that would
        * temporarily fail on the next UPDATE.
@@ -201,22 +210,26 @@ let save_page dbh hostid ?user ?r model =
                page_emails_url_cn, links_from_cn, recently_visited_url_cn
              deferred";
 
-      (* Mark the old page as deleted.  NB. There is a small race
-       * condition here because PostgreSQL doesn't do isolation
-       * properly.  If a user tries to visit this page between the
-       * delete and the creation of the new page, then they'll get
-       * a page not found error. (XXX)
+      (* Lock the pages table to avoid bogus 404 errors.  The
+       * lock is released at the end of the current transaction.
        *)
+      PGSQL(dbh) "lock table pages";
+
+      (* Mark the old page as deleted. *)
       PGSQL(dbh) "update pages set url_deleted = url, url = null
                    where hostid = $hostid and id = $model_id";
 
       let description = model.description in
+      let keywords = model.keywords in
+      let noodp = model.noodp in
       let redirect = model.redirect in
       PGSQL(dbh)
        "insert into pages (hostid, url, title,
-                            description, creation_date, logged_ip,
+                            description, keywords, noodp,
+                            creation_date, logged_ip,
                             logged_user, redirect, css)
-         values ($hostid, $url, $title, $description, $creation_date,
+         values ($hostid, $url, $title, $description, $?keywords, $?noodp,
+                 $creation_date,
                  $?logged_ip, $?logged_user, $?redirect, $?css)";
 
       (* New page ID <> old page ID model.id. *)
@@ -225,18 +238,19 @@ let save_page dbh hostid ?user ?r model =
       (* Create the page contents. *)
       let ordering = ref 0 in  (* Creating new ordering. *)
       List.iter (
-       fun (sectionname, divname, content) ->
+       fun (sectionname, divname, divclass, jsgo, content) ->
          incr ordering; let ordering = Int32.of_int !ordering in
          PGSQL(dbh) "insert into contents (pageid,
-                         ordering, sectionname, divname, content)
+                         ordering, sectionname, divname, divclass,
+                         jsgo, content)
                       values ($pageid, $ordering, $?sectionname,
-                              $?divname, $content)"
+                              $?divname, $?divclass, $?jsgo, $content)"
       ) model.contents_;
 
       url, pageid
     ) in
 
   (* Keep the links table in synch. *)
-  Cocanwiki_links.update_links_for_page dbh hostid url;
+  Cocanwiki_links.update_links_for_page dbh hostid url;
 
   url, pageid