About half way through switching cocanwiki to using the new PG interface.
[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.4 2006/03/27 16:43:44 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_files
28 open Cocanwiki
29
30 (* Convenience code for generating diffs between versions.  See diff.ml
31  * and edit.ml which both use this code.
32  *)
33 let page_for_diff css sections =
34   (String.concat ""
35      (List.map (fun (sectionname, content) ->
36                   "HEADING: " ^ sectionname ^ "\n\n" ^
37                   content ^ "\n\n") sections)) ^
38   "CSS:\n\n" ^ css
39
40 let le_re = Pcre.regexp "\r?\n"
41 let le_subst = Pcre.subst "\n"
42
43 let diff_cmd old_page new_page =
44   (* Convert line-endings in the input files from \r\n to \n.  Diff
45    * can get confused by the \r characters, particularly in side-by-side
46    * mode when asked to expand tabs (-y -t).
47    *)
48   let f = Pcre.replace ~rex:le_re ~itempl:le_subst in
49   let new_page = f new_page in
50   let old_page = f old_page in
51
52   let new_filename = output_tempfile new_page in
53   let old_filename = output_tempfile old_page in
54
55   (* Side-by-side mode was good, but stupidly implemented.  It's
56    * disabled right now.
57    *)
58   let diff_sidebyside = false in
59
60   let options =
61     if not diff_sidebyside then
62       "-u"
63     else
64       "-y --left-column" in
65   let options = options ^ " -t -b -B" in
66
67   let cmd = sprintf "diff %s %s %s ||:" options old_filename new_filename in
68   let diff = pget cmd in
69
70   (* Remove the temporary files. *)
71   unlink new_filename; unlink old_filename;
72
73   let diff =
74     if not diff_sidebyside then
75       match diff with
76           _ :: _ :: diff -> diff
77         | diff -> diff
78     else diff in
79
80   String.concat "\n" diff
81
82 let get_version_for_diff dbh version =
83   if version = 0l then ""
84   else (
85     let css = List.hd (
86       PGSQL(dbh)
87         "select css from pages where id = $version"
88     ) in
89     let css = match css with None -> "" | Some css -> css in
90
91     let rows = PGSQL(dbh)
92       "select sectionname, content
93          from contents where pageid = $version
94         order by ordering" in
95
96     let sections =
97       List.map (
98         function
99         | (Some sectionname, content) ->
100             sectionname, content
101         | (None, content) ->
102             "", content
103       ) rows in
104     let page = page_for_diff css sections in
105
106     page
107   )
108
109 let get_diff dbh hostid page ?old_version ~version ()=
110   let old_version =
111     match old_version with
112       | Some version -> version
113       | None ->
114           try
115             List.hd (
116               PGSQL(dbh)
117                 "select id from pages
118                   where hostid = $hostid
119                     and url_deleted = $page
120                     and id < $version
121                   order by 1 desc limit 1"
122             )
123           with
124             Not_found | ExtList.List.Empty_list -> 0l in
125
126   (* Get the two versions. *)
127   let new_page = get_version_for_diff dbh version in
128   let old_page = get_version_for_diff dbh old_version in
129
130   (* Compute the diff of the two versions. *)
131   let diff = diff_cmd old_page new_page in
132   diff, old_version