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: search.ml,v 1.7 2004/11/02 19:21:17 rich Exp $
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.
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.
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.
29 open Cocanwiki_template
30 open Cocanwiki_strings
33 let split_words = Pcre.regexp "\\W+"
35 let run r (q : cgi) (dbh : Dbi.connection) hostid host user =
36 let template = get_template dbh hostid "search.html" in
38 template#set "canonical_hostname" host.canonical_hostname;
40 (* Get the query, if it exists. *)
41 let query = try q#param "q" with Not_found -> "" in
42 let have_query = not (string_is_whitespace query) in
43 template#set "query" query;
44 template#conditional "have_query" have_query;
47 let can_edit = can_edit host user in
48 template#conditional "can_edit" can_edit;
50 (* Search old versions? Only permit this if can_edit is true. *)
53 try q#param_true "old_versions"
54 with Not_found -> false
58 (* If we have a query, make some results. *)
61 (* Get the keywords from the query string. *)
62 let keywords = Pcre.split ~rex:split_words query in
63 let keywords = List.map String.lowercase keywords in
65 (* Turn the keywords into a tsearch2 ts_query string. *)
66 let tsquery = String.concat "&" keywords in
68 (* Search the titles first. *)
71 ("select id, url, url_deleted, title, last_modified_date,
72 (lower (title) = lower (?)) as exact
75 (if not old_versions then "and url is not null " else "") ^ "
77 and title_description_fti @@ to_tsquery (?, ?)
78 order by exact desc, last_modified_date desc, title") in
79 sth#execute [`String query;
80 `Int hostid; `String "default"; `String tsquery];
84 | [_; `String url; `Null; `String title;
85 `Timestamp last_modified; _] ->
86 url, title, None, last_modified
87 | [`Int version; `Null; `String url; `String title;
88 `Timestamp last_modified; _] ->
89 url, title, Some version, last_modified
90 | _ -> assert false) in
92 let have_titles = titles <> [] in
93 template#conditional "have_titles" have_titles;
95 (* Search the contents. *)
98 ("select c.id, p.id, p.url, p.url_deleted, p.title,
100 from contents c, pages p
101 where c.pageid = p.id
103 (if not old_versions then "and url is not null " else "") ^ "
104 and p.redirect is null
105 and c.content_fti @@ to_tsquery (?, ?)
106 order by p.last_modified_date desc, p.title
108 sth#execute [`Int hostid; `String "default"; `String tsquery];
112 | [`Int contentid; _; `String url; `Null;
113 `String title; `Timestamp last_modified] ->
114 contentid, url, title, None, last_modified
115 | [`Int contentid; `Int version; `Null; `String url;
116 `String title; `Timestamp last_modified] ->
117 contentid, url, title, Some version, last_modified
118 | _ -> assert false) in
120 let have_contents = contents <> [] in
121 template#conditional "have_contents" have_contents;
123 (* Pull out the actual text which matched so we can generate a summary.*)
125 if contents = [] then []
127 let qs = Dbi.placeholders (List.length contents) in
130 ("select id, sectionname, content from contents
131 where id in " ^ qs) in
133 (List.map (fun (contentid, _,_,_,_) -> `Int contentid) contents);
135 | [ `Int id; `Null; `String content ] ->
137 | [ `Int id; `String sectionname; `String content ] ->
138 id, (Some sectionname, content)
142 (* Generate the final tables. *)
144 List.map (fun (url, title, version, last_modified) ->
145 let have_version, version =
148 | Some version -> true, version in
149 let last_modified = printable_date last_modified in
150 [ "url", Template.VarString url;
151 "title", Template.VarString title;
152 "have_version", Template.VarConditional have_version;
153 "version", Template.VarString (string_of_int version);
154 "last_modified", Template.VarString last_modified ]
156 template#table "titles" table;
160 (fun (contentid, url, title, version, last_modified) ->
161 let have_version, version =
164 | Some version -> true, version in
165 let sectionname, content = List.assoc contentid content_map in
166 let have_sectionname, sectionname =
167 match sectionname with
169 | Some sectionname -> true, sectionname in
172 (Wikilib.text_of_xhtml
173 (Wikilib.xhtml_of_content dbh hostid content)) in
174 let linkname = linkname_of_sectionname sectionname in
175 let last_modified = printable_date last_modified in
176 [ "url", Template.VarString url;
177 "title", Template.VarString title;
178 "have_version", Template.VarConditional have_version;
179 "version", Template.VarString (string_of_int version);
180 "have_sectionname", Template.VarConditional have_sectionname;
181 "sectionname", Template.VarString sectionname;
182 "linkname", Template.VarString linkname;
183 "content", Template.VarString content;
184 "last_modified", Template.VarString last_modified ]
186 template#table "contents" table;
188 (* Do we have any results? *)
189 let have_results = have_titles || have_contents in
193 template#conditional "have_results" have_results;
198 register_script ~restrict:[CanView] run