(* 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.28 2006/03/27 18:09:46 rich Exp $
+ * $Id: edit.ml,v 1.38 2006/12/06 09:46:57 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
open Cocanwiki_pages
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
+ let template = get_template r dbh hostid "edit.html" in
+ let template_conflict = get_template r dbh hostid "edit_conflict.html" in
+ let template_email = get_template r dbh hostid "edit_page_email.txt" in
(* Workaround bugs in IE, specifically lack of support for <button>
* elements.
let build_internal_model () =
let id = Int32.of_string (q#param "id") in
let description = q#param "description" in
+ let keywords = q#param "keywords" in
+ let keywords =
+ if string_is_whitespace keywords then None else Some keywords in
+ let noodp = match q#param "noodp" with
+ | "" -> None
+ | "t" -> Some true
+ | "f" -> Some false
+ | _ -> failwith "unknown value for noodp parameter" in
let redirect = q#param "redirect" in
let redirect =
- if string_is_whitespace redirect then
- None else Some redirect in
+ 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")
| "title" -> Title (q#param "pt_value")
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;
+ let divclass = q#param ("divclass_" ^ string_of_int !i) in
+ let divclass =
+ if string_is_whitespace divclass then None else Some divclass in
+ let jsgo = q#param ("jsgo_" ^ string_of_int !i) in
+ let jsgo = if string_is_whitespace jsgo then None else Some jsgo in
+ contents := (sectionname, divname, divclass, jsgo, content) :: !contents;
incr i
done;
let contents = List.rev !contents in
{ id = id;
pt = pt;
description = description;
+ keywords = keywords;
+ noodp = noodp;
redirect = redirect;
contents_ = contents; }
in
* section ONLY is allowed to have an empty title.
*)
if model.contents_ <> [] then
- List.iter (function (None, _, _) ->
- add_error
- ("Every section except the first must have a title.");
+ List.iter (function
+ | (None, _, _, _, _) ->
+ add_error
+ "Every section except the first must have a title.";
| _ -> ())
(List.tl model.contents_);
+ (* There are two constraints on any non-null jsgo's:
+ * (1) Must only be present if divname or divclass is non-null.
+ * (2) Must point to a valid URL on the current host.
+ *)
+ List.iter (
+ function
+ | (_, None, None, Some _, _) ->
+ add_error
+ "Javascript onclick can only be used with a CSS id/class."
+ | (_, _, _, Some jsgo, _) ->
+ let rows =
+ PGSQL(dbh) "select 1 from pages
+ where hostid = $hostid
+ and url is not null
+ and url = $jsgo
+ and redirect is null" in
+ let ok = rows = [Some 1l] in
+ if not ok then
+ add_error ("Javascript onclick must point to an ordinary page " ^
+ "on the current site (ie. not to a redirect). " ^
+ "Do not put '/' at the beginning of the URL.")
+ | _ -> ()
+ ) model.contents_;
+
get_errors ()
in
let model_to_template model template =
template#set "id" (Int32.to_string model.id);
template#set "description" model.description;
+ template#set "keywords"
+ (match model.keywords with None -> "" | Some keywords -> keywords);
+
+ template#conditional "noodp_null" false;
+ template#conditional "noodp_true" false;
+ template#conditional "noodp_false" false;
+ (match model.noodp with
+ | None -> template#conditional "noodp_null" true
+ | Some true -> template#conditional "noodp_true" true
+ | Some false -> template#conditional "noodp_false" true);
(match model.pt with
Page page ->
let table = List.map (
fun (url, title) ->
let url = Option.get url in
+ let is_index = url = "index" in
let selected = model.redirect = Some url in
[ "url", Template.VarString url;
"title", Template.VarString title;
- "selected", Template.VarConditional selected ]
+ "selected", Template.VarConditional selected;
+ "is_index", Template.VarConditional is_index ]
) rows in
template#table "redirects" table;
let ordering = ref 0 in
let table =
List.map
- (fun (sectionname, divname, content) ->
+ (fun (sectionname, divname, divclass, jsgo, content) ->
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
+ let divclass = match divclass with None -> "" | Some s -> s in
+ let jsgo = match jsgo with None -> "" | Some s -> s in
[ "ordering", Template.VarString (Int32.to_string ordering);
"sectionname", Template.VarString sectionname;
"divname", Template.VarString divname;
+ "divclass", Template.VarString divclass;
+ "jsgo", Template.VarString jsgo;
"content", Template.VarString content ]) model.contents_ in
template#table "contents" table;
(* 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"
- dbh hostid 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 r 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"
+ r 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
if is_action "insert" then (
let posn = get_action "insert" in
let item =
- Some "The title of this section", None, "Write something here." in
+ Some "The title of this section",
+ None, None, None,
+ "Write something here." in
model := action_insert !model posn item
) else if is_action "moveup" then (
let posn = get_action "moveup" in
model_to_template !model template
in
- (* Try to save the page. Returns a boolean indicating if the
- * page was saved successfully.
+ (* Try to save the page. Only returns if there were errors in
+ * the model.
*)
let try_save () =
let model = build_internal_model () in
let no_errors = [] = check_for_errors model in
if no_errors then (
(* No errors, so we can save the page ... *)
-
let url, pageid =
try
- save_page dbh hostid ~user ~r model
+ save_page r dbh hostid ~user model
with
SaveURLError ->
error ~back_button:true ~title:"Page exists"
- dbh hostid q ("While you were editing that page, it looks " ^
- "like another user created the same page.");
+ r 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) ->
(* 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) ->
- let sectionname = match sectionname with
- | None -> ""
- | Some s -> s in
- sectionname, content
- ) model.contents_) in
+ page_for_diff model css 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" (Int32.to_string old_version);
- template_conflict#set "new_version" (Int32.to_string 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;
let subject =
"Site notice: " ^ hostname ^ "/" ^ url ^ " has been updated" in
+ let content_type =
+ "text/plain", ["charset", Mimestring.mk_param "UTF-8"] in
+
(* Send each email individually (they all have different opt out
* links).
*)
List.iter (fun (to_addr, opt_out) ->
template_email#set "opt_out" opt_out;
let body = template_email#to_string in
- Sendmail.send_mail ~subject
- ~to_addr:[to_addr] body)
+
+ let msg = Netsendmail.compose ~to_addrs:["", to_addr]
+ ~subject ~content_type body in
+ Netsendmail.sendmail msg)
addrs
);
email_notify ~body ~subject ~user dbh hostid;
(* Redirect back to the URL. *)
- q#redirect ("http://" ^ hostname ^ "/" ^ url);
- return ()
+ q#redirect ("http://" ^ hostname ^ "/" ^ url)
);
in
*)
"" in
- q#redirect ("http://" ^ hostname ^ "/" ^ url);
- return ()
+ q#redirect ("http://" ^ hostname ^ "/" ^ url)
in
(* This code decides where we are in the current editing cycle.