COCANWIKI is now ~95% UTF-8 safe.
[cocanwiki.git] / scripts / lib / cocanwiki_template.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_template.ml,v 1.11 2006/08/16 15:27:02 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  * This module wraps around the Template library.  It provides caching
22  * of templates and fills in standard fields on a host-specific basis.
23  *
24  *)
25
26 open Unix
27
28 open Cocanwiki_files
29 open Cocanwiki_strings
30
31 (* This is used to generate the id fields from URLs on the site menu. *)
32 let id_of_url str =
33   let buf = UTF8.Buf.create (String.length str) in
34   UTF8.iter (
35     fun c ->
36       if iswebsafe c then UTF8.Buf.add_char buf c
37       else UTF8.Buf.add_char buf (UChar.of_char '_')
38   ) str;
39   UTF8.Buf.contents buf
40
41 let base =
42   let base =
43     try Sys.getenv "COCANWIKI_TEMPLATES"
44     with Not_found -> "/usr/share/cocanwiki/templates" in
45   let is_dir path =
46     try (Unix.stat path).Unix.st_kind = Unix.S_DIR
47     with Unix.Unix_error _ -> false in
48   if not (is_dir base) then
49     failwith ("environment variable $COCANWIKI_TEMPLATES " ^
50               "must be set to point to my 'templates' directory " ^
51               "(see README file for more details)");
52   base
53
54 (* The webserver gets restarted regularly enough that this is reasonable. *)
55 let { tm_year = year } = gmtime (time ())
56 let year = year + 1900
57
58 (* Cache of precompiled templates, arranged by full path. *)
59 let cache = Hashtbl.create 32
60
61 let _get_template filename =
62   let path = base // filename in
63   let stat = Unix.stat path in
64   let mtime = stat.st_mtime in
65
66   try
67     let template, old_mtime = Hashtbl.find cache path in
68     if old_mtime < mtime then (
69       (* The template has changed on disk since it was compiled.  Reload. *)
70       let template = Template.template path in
71       Hashtbl.replace cache path (template, mtime);
72       template
73     ) else
74       template
75   with
76       Not_found ->
77         (* Template not seen before, so load it. *)
78         let template = Template.template path in
79         Hashtbl.replace cache path (template, mtime);
80         template
81
82 let get_template ?page dbh hostid filename =
83   let template = _get_template filename in
84
85   if hostid > 0l then (
86     (* Get standard fields concerning this host from the database. *)
87     let rows =
88       PGSQL(dbh) "nullable-results"
89         "select h.theme_css, h.css is not null,
90                 p.name, p.url, h.search_box,
91                 h.brand, h.brand_tagline, h.brand_description,
92                 h.pagebug, h.ie_imagetoolbar_no, h.global_noodp
93            from hosts h left outer join powered_by p on h.powered_by = p.id
94           where h.id = $hostid" in
95
96     let theme_css, has_host_css, powered_by_name, powered_by_url, search_box,
97       brand, brand_tagline, brand_description, pagebug, ie_imagetoolbar_no,
98       global_noodp =
99       match rows with
100       | [ row ] -> row
101       | _ -> assert false in
102
103     let theme_css =
104       match theme_css with
105       | None -> "/_css/standard.css"
106       | Some file -> file in
107
108     let has_host_css =
109       match has_host_css with
110       | Some true -> true
111       | _ -> false in
112
113     let powered_by_name, powered_by_url =
114       match powered_by_name, powered_by_url with
115       | None, None ->
116           let url = "http://sandbox.merjis.com/" in
117           let name = Cocanwiki_version.package ^ " " ^
118                      Cocanwiki_version.version in
119           name, url
120       | Some name, Some url -> name, url
121       | _ -> assert false in
122
123     let branding, brand,
124       has_brand_tagline, brand_tagline,
125       has_brand_description, brand_description =
126       match brand with
127       | None -> false, "", false, "", false, ""
128       | Some brand ->
129           let has_brand_tagline, brand_tagline =
130             match brand_tagline with
131             | None -> false, ""
132             | Some s -> true, s in
133           let has_brand_description, brand_description =
134             match brand_description with
135             | None -> false, ""
136             | Some s -> true, s in
137           true, brand,
138           has_brand_tagline, brand_tagline,
139           has_brand_description, brand_description in
140
141     let has_pagebug, pagebug =
142       match pagebug with
143       | None -> false, ""
144       | Some pagebug -> true, pagebug in
145
146     let search_box = match search_box with Some b -> b | _ -> assert false in
147
148     let ie_imagetoolbar_no =
149       match ie_imagetoolbar_no with Some b -> b | _ -> assert false in
150
151     let global_noodp =
152       match global_noodp with Some b -> b | _ -> assert false in
153
154     template#set "theme_css" theme_css;
155     template#conditional "has_host_css" has_host_css;
156     template#set "powered_by_name" powered_by_name;
157     template#set "powered_by_url" powered_by_url;
158     template#conditional "search_box" search_box;
159     template#conditional "branding" branding;
160     template#set "brand" brand;
161     template#conditional "has_brand_tagline" has_brand_tagline;
162     template#set "brand_tagline" brand_tagline;
163     template#conditional "has_brand_description" has_brand_description;
164     template#set "brand_description" brand_description;
165     template#conditional "has_pagebug" has_pagebug;
166     template#set "pagebug" pagebug;
167     template#conditional "ie_imagetoolbar_no" ie_imagetoolbar_no;
168     template#conditional "noodp" global_noodp;
169
170     (* Site menu. *)
171     let rows = PGSQL(dbh)
172       "select url, label, ordering from sitemenu
173         where hostid = $hostid order by ordering" in
174
175     let is_homepage =
176       match page with
177       | None -> false
178       | Some "index" -> true
179       | _ -> false in
180     template#conditional "is_homepage" is_homepage;
181
182     let table = List.map
183       (fun (url, label, _) ->
184          let is_linked =
185            match page with
186            | None -> true
187            | Some page when page = url -> false
188            | _ -> true in
189          let id = id_of_url url in
190          [ "url", Template.VarString url;
191            "label", Template.VarString label;
192            "is_linked", Template.VarConditional is_linked;
193            "id", Template.VarString id ]
194       ) rows in
195
196     template#table "sitemenu" table;
197   )
198   else (* if we have no hostid *) (
199     template#set "theme_css" "/_css/standard.css";
200     template#conditional "has_host_css" false;
201     template#set "powered_by_name" (Cocanwiki_version.package ^ " " ^
202                                     Cocanwiki_version.version);
203     template#set "powered_by_url" "http://sandbox.merjis.com/";
204     template#conditional "search_box" false;
205     template#conditional "branding" false;
206     template#set "brand" "";
207     template#conditional "has_brand_tagline" false;
208     template#set "brand_tagline" "";
209     template#conditional "has_brand_description" false;
210     template#set "brand_description" "";
211     template#conditional "has_pagebug" false;
212     template#set "pagebug" "";
213     template#conditional "ie_imagetoolbar_no" false;
214     template#conditional "noodp" false;
215     template#conditional "is_homepage" false;
216     template#table "sitemenu" [];
217   );
218   (* Copyright year. *)
219   template#set "year" (string_of_int year);
220
221   template