Support for NOODP flag, sitewide and per-page.
[cocanwiki.git] / scripts / lib / cocanwiki_diff.ml
1 (* COCANWIKI - a wiki written in Objective CAML.
2  * Written by Richard W.M. Jones <rich@merjis.com>.
3  * Copyright (C) 2004 Merjis Ltd.
4  * $Id: cocanwiki_diff.ml,v 1.8 2006/08/14 11:36:50 rich Exp $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; see the file COPYING.  If not, write to
18  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  *)
21
22 open Apache
23 open Registry
24 open Cgi
25 open Printf
26
27 open Cocanwiki_pages
28 open Cocanwiki_files
29 open Cocanwiki
30
31 (* Convenience code for generating diffs between versions.  See diff.ml
32  * and edit.ml which both use this code.
33  *)
34 let page_for_diff model css =
35   let title_or_url = match model.pt with Page t -> t | Title t -> t in
36   "Title or URL: " ^ title_or_url ^ "\n" ^
37   "Description: " ^ model.description ^ "\n\n" ^
38   (match model.keywords with
39    | None -> ""
40    | Some keywords -> "Keywords: " ^ keywords ^ "\n\n") ^
41   (match model.redirect with
42    | None -> ""
43    | Some redirect -> "Redirect: " ^ redirect ^ "\n\n") ^
44   "ROBOTS NOODP: " ^
45   (match model.noodp with
46    | None -> "inherit sitewide default"
47    | Some noodp -> string_of_bool noodp) ^ "\n\n" ^
48   (String.concat ""
49      (List.map (
50         fun (sectionname, divname, jsgo, content) ->
51           (match sectionname with
52            | None -> ""
53            | Some sectionname -> "HEADING: " ^ sectionname ^ "\n\n") ^
54           content ^ "\n\n" ^
55           (match divname with
56            | None -> ""
57            | Some divname -> "CSS Id: " ^ divname ^ "\n") ^
58           (match jsgo with
59            | None -> ""
60            | Some jsgo -> "Javascript Onclick: " ^ jsgo ^ "\n") ^
61           "\n"
62       ) model.contents_)
63   ) ^
64   (match css with
65    | None -> ""
66    | Some css -> "CSS:\n\n" ^ css ^ "\n")
67
68 let le_re = Pcre.regexp "\r?\n"
69 let le_subst = Pcre.subst "\n"
70
71 let diff_cmd old_page new_page =
72   (* Convert line-endings in the input files from \r\n to \n.  Diff
73    * can get confused by the \r characters, particularly in side-by-side
74    * mode when asked to expand tabs (-y -t).
75    *)
76   let f = Pcre.replace ~rex:le_re ~itempl:le_subst in
77   let new_page = f new_page in
78   let old_page = f old_page in
79
80   let new_filename = output_tempfile new_page in
81   let old_filename = output_tempfile old_page in
82
83   (* Side-by-side mode was good, but stupidly implemented.  It's
84    * disabled right now.
85    *)
86   let diff_sidebyside = false in
87
88   let options =
89     if not diff_sidebyside then
90       "-u"
91     else
92       "-y --left-column" in
93   let options = options ^ " -t -b -B" in
94
95   let cmd = sprintf "diff %s %s %s ||:" options old_filename new_filename in
96   let diff = pget cmd in
97
98   (* Remove the temporary files. *)
99   unlink new_filename; unlink old_filename;
100
101   let diff =
102     if not diff_sidebyside then
103       match diff with
104           _ :: _ :: diff -> diff
105         | diff -> diff
106     else diff in
107
108   String.concat "\n" diff
109
110 let get_version_for_diff dbh version =
111   if version = 0l then ""
112   else (
113     let title, description, keywords, noodp, redirect, css = List.hd (
114       PGSQL(dbh)
115         "select title, description, keywords, noodp, redirect, css from pages
116           where id = $version"
117     ) in
118
119     let contents_ = PGSQL(dbh)
120       "select sectionname, divname, jsgo, content
121          from contents where pageid = $version
122         order by ordering" in
123
124     let model = { id = version;
125                   pt = Title title;
126                   description = description;
127                   keywords = keywords;
128                   noodp = noodp;
129                   redirect = redirect;
130                   contents_ = contents_ } in
131
132     let page = page_for_diff model css in
133
134     page
135   )
136
137 let get_diff dbh hostid page ?old_version ~version ()=
138   let old_version =
139     match old_version with
140       | Some version -> version
141       | None ->
142           try
143             List.hd (
144               PGSQL(dbh)
145                 "select id from pages
146                   where hostid = $hostid
147                     and url_deleted = $page
148                     and id < $version
149                   order by 1 desc limit 1"
150             )
151           with
152             Not_found | ExtList.List.Empty_list | Failure "hd" -> 0l in
153
154   (* Get the two versions. *)
155   let new_page = get_version_for_diff dbh version in
156   let old_page = get_version_for_diff dbh old_version in
157
158   (* Compute the diff of the two versions. *)
159   let diff = diff_cmd old_page new_page in
160   diff, old_version