Show all changed fields in the diff report.
[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.6 2006/08/04 12:20:07 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.redirect with
39    | None -> ""
40    | Some redirect -> "Redirect: " ^ redirect ^ "\n\n") ^
41   (String.concat ""
42      (List.map (
43         fun (sectionname, divname, jsgo, content) ->
44           (match sectionname with
45            | None -> ""
46            | Some sectionname -> "HEADING: " ^ sectionname ^ "\n\n") ^
47           content ^ "\n\n" ^
48           (match divname with
49            | None -> ""
50            | Some divname -> "CSS Id: " ^ divname ^ "\n") ^
51           (match jsgo with
52            | None -> ""
53            | Some jsgo -> "Javascript Onclick: " ^ jsgo ^ "\n") ^
54           "\n"
55       ) model.contents_)
56   ) ^
57   (match css with
58    | None -> ""
59    | Some css -> "CSS:\n\n" ^ css ^ "\n")
60
61 let le_re = Pcre.regexp "\r?\n"
62 let le_subst = Pcre.subst "\n"
63
64 let diff_cmd old_page new_page =
65   (* Convert line-endings in the input files from \r\n to \n.  Diff
66    * can get confused by the \r characters, particularly in side-by-side
67    * mode when asked to expand tabs (-y -t).
68    *)
69   let f = Pcre.replace ~rex:le_re ~itempl:le_subst in
70   let new_page = f new_page in
71   let old_page = f old_page in
72
73   let new_filename = output_tempfile new_page in
74   let old_filename = output_tempfile old_page in
75
76   (* Side-by-side mode was good, but stupidly implemented.  It's
77    * disabled right now.
78    *)
79   let diff_sidebyside = false in
80
81   let options =
82     if not diff_sidebyside then
83       "-u"
84     else
85       "-y --left-column" in
86   let options = options ^ " -t -b -B" in
87
88   let cmd = sprintf "diff %s %s %s ||:" options old_filename new_filename in
89   let diff = pget cmd in
90
91   (* Remove the temporary files. *)
92   unlink new_filename; unlink old_filename;
93
94   let diff =
95     if not diff_sidebyside then
96       match diff with
97           _ :: _ :: diff -> diff
98         | diff -> diff
99     else diff in
100
101   String.concat "\n" diff
102
103 let get_version_for_diff dbh version =
104   if version = 0l then ""
105   else (
106     let title, description, redirect, css = List.hd (
107       PGSQL(dbh)
108         "select title, description, redirect, css from pages
109           where id = $version"
110     ) in
111
112     let contents_ = PGSQL(dbh)
113       "select sectionname, divname, jsgo, content
114          from contents where pageid = $version
115         order by ordering" in
116
117     let model = { id = version;
118                   pt = Title title;
119                   description = description;
120                   redirect = redirect;
121                   contents_ = contents_ } in
122
123     let page = page_for_diff model css in
124
125     page
126   )
127
128 let get_diff dbh hostid page ?old_version ~version ()=
129   let old_version =
130     match old_version with
131       | Some version -> version
132       | None ->
133           try
134             List.hd (
135               PGSQL(dbh)
136                 "select id from pages
137                   where hostid = $hostid
138                     and url_deleted = $page
139                     and id < $version
140                   order by 1 desc limit 1"
141             )
142           with
143             Not_found | ExtList.List.Empty_list | Failure "hd" -> 0l in
144
145   (* Get the two versions. *)
146   let new_page = get_version_for_diff dbh version in
147   let old_page = get_version_for_diff dbh old_version in
148
149   (* Compute the diff of the two versions. *)
150   let diff = diff_cmd old_page new_page in
151   diff, old_version