(*
* <COPYRIGHT>
* Copyright 2003 Gerd Stolpmann
*
* <GPL>
* This file is part of WTimer.
*
* WTimer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* WTimer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with WDialog; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* </>
*)
(* $Id: init.ml,v 1.5 2003/03/23 11:59:14 gerd Exp $
* ----------------------------------------------------------------------
*
*)
open Wd_dialog
open Wd_types
open Db_types.Types
open Db_types
open Db.Types
(**********************************************************************)
(* Definition of additional output encodings *)
(**********************************************************************)
(* These functions map strings to strings in order to beautify the
* displayed text. They can be called from UI language by the
* <ui:encode> element.
*)
(* (1) Mappings from dates to strings: Input is an ISO date in the
* format YYYY-MM-DD, e.g. 2002-08-31
*)
let enc_year date_str =
(* for <ui:encode enc="year">: outputs the year only *)
let date = Date.from_string date_str in
let year = (Date.access date).year in
string_of_int year
;;
let enc_mday date_str =
(* for <ui:encode enc="mday">: output is the day number *)
let date = Date.from_string date_str in
let mday = (Date.access date).mday in
string_of_int mday
;;
let enc_mday_en date_str =
(* for <ui:encode enc="mday-en">: output is the ordinal day number *)
let s = enc_mday date_str in
match s.[ String.length s - 1] with
'1' -> if s="11" then s ^ "th" else s ^ "st"
| '2' -> if s="12" then s ^ "th" else s ^ "nd"
| '3' -> if s="13" then s ^ "th" else s ^ "rd"
| _ -> s ^ "th"
;;
let tm_of_date date =
(* convert Db.Types.date to Unix.tm *)
let _, udate = Unix.mktime
{ Unix.tm_sec = 0;
Unix.tm_min = 0;
Unix.tm_hour = 0;
Unix.tm_mday = (Date.access date).mday;
Unix.tm_mon = (Date.access date).month - 1;
Unix.tm_year = (Date.access date).year - 1900;
Unix.tm_wday = 0;
Unix.tm_yday = 0;
Unix.tm_isdst = false;
} in
udate
;;
let enc_weekday_en date_str =
(* for <ui:encode enc="weekday-en">: output is the weekday name *)
let date = Date.from_string date_str in
let weekday = (tm_of_date date).Unix.tm_wday in
[| "Sunday";
"Monday";
"Tuesday";
"Wednesday";
"Thursday";
"Friday";
"Saturday";
|].(weekday);
;;
let enc_month date_str =
(* for <ui:encode enc="month-en">: output is the month number (1-12) *)
let date = Date.from_string date_str in
string_of_int ((Date.access date).month)
;;
let enc_month_en date_str =
(* for <ui:encode enc="month-en">: output is the month name *)
let date = Date.from_string date_str in
let month = (Date.access date).month - 1 in
[| "January"; "February"; "March"; "April"; "May"; "June";
"July"; "August"; "September"; "October"; "November"; "December"
|].(month);
;;
(* Now follows a more complicated function that performs some date
* computation. Input is the start date, usually the first day of a
* month, and an integer from 0 to 36, the position. Imagine you
* have a calendar sheet for a month, e.g.
*
* October 2002
* Su Mo Tu We Th Fr Sa
* 1 2 3 4 5
* 6 7 8 9 10 11 12
* 13 14 15 16 17 18 19
* 20 21 22 23 24 25 26
* 27 28 29 30 31
*
* The position counts the day positions of the sheet. The sunday in the
* first row is position 0, the monday in the sixth row is position 36.
* The result of the function is either "", meaning that the position
* is empty, or a number > 0 denoting the day to display.
*
* The input string contains the date and the position, separated by
* a single space. The output string is the decimal representation of
* the day, or "".
*)
let enc_cal_day date_pos_str =
try
let k = String.index date_pos_str ' ' in
let date = Date.from_string (String.sub date_pos_str 0 k) in
let pos = int_of_string
(String.sub
date_pos_str (k+1) (String.length date_pos_str - k - 1)) in
let weekday = (tm_of_date date).Unix.tm_wday in (* 0 to 6 *)
let days = Date.days_of_month date in
let first_pos = weekday in (* first filled position *)
let last_pos = first_pos + days - 1 in (* last filled position *)
if pos >= first_pos && pos <= last_pos then
string_of_int (pos - first_pos + 1)
else
""
with
Not_found -> "??"
;;
(**********************************************************************)
(* Database *)
(**********************************************************************)
(* [db] is an unbound database handle. Later it will be bound to a certain
* database, but the connection will remain closed until the first real
* db access happens, i.e. the connection is established lazily. The
* advantage of this approach that it is compatible with the JSERV
* mode where new processes are started, and it must be ensured that the
* database connection is opened for each process individually.
*)
let db = Db.Connection.create()
;;
let rollback() =
if Db.Connection.is_connected db then
Db.Connection.rollback db
;;
(**********************************************************************)
(* Fatal errors *)
(**********************************************************************)
(* Note: Don't forget to rollback the DB transaction !!! *)
let log_error_stderr error =
Printf.eprintf "[%s] [error] [wtimer application] %s\n"
(Netdate.format "%c" (Netdate.create ~zone:Netdate.localzone (Unix.time())))
error;
flush stderr
;;
let log_error_syslog error =
let _ =
Sys.command ("logger -p user.err -t wtimer " ^
Filename.quote ("[instance " ^ Const.wtimer_dir ^ "] ") ^
Filename.quote error) in
()
;;
let log_error =
log_error_syslog (* or log_error_stderr *)
;;
let simple_error_page (ch : Netchannels.out_obj_channel) error =
log_error (Printexc.to_string error);
let out = ch # output_string in
out "<html><body><h1>Internal Error</h1>\n";
out "The application wtimer cannot serve your request. The ";
out "exact reason can be found in the error log. Please inform ";
out "your administrator about this incidence.\n";
out "</body></html>\n";
rollback()
;;
let extended_error_page (universe : universe_type) ch error =
log_error (Printexc.to_string error);
(* Instantiate the template "fatal-error" of the startpage manually. *)
(* Step 1: Create our own faked cgi_activation object *)
let cgi_env = new Netcgi_env.custom_environment () in
let dummy = new Netchannels.output_null () in
cgi_env # set_output_ch dummy;
cgi_env # set_cgi ~gateway_interface:"FAKE CGI" ~request_method:"GET" ();
cgi_env # set_input_state `Received_header;
cgi_env # set_output_state `Start;
let cgi =
new Netcgi.std_activation ~env:(cgi_env :> Netcgi_env.cgi_environment) () in
(* Step 2: Get the startpage object *)
let environment = Wd_cycle.make_environment cgi in
let startpage = universe # create environment "startpage" in
(* Step 3: Initialize the startpage object *)
let errid = Definitions.errid_of_error error in
startpage # set_variable "runtime-error" (String_value errid);
let session = !Registry.new_session universe environment in
startpage # set_variable "session" (Dialog_value (Some session));
(* Step 4: Output the template "fatal-error" *)
let inst = Wd_template.apply_byname startpage "fatal-error" [] in
let s = Wd_template.to_string startpage inst in
ch # output_string s;
rollback()
;;
(* [print_error] is called to output fatal errors. It is initialized with
* a very simple definition which is replaced later by a more
* sophisticated one.
*)
let print_error = ref simple_error_page
;;
(**********************************************************************)
(* Register dialogs etc. *)
(**********************************************************************)
let reg config universe =
(* Get the parameters to connect to the database: *)
let db_name = Get_config.option config "database-name" in
let user_name = Get_config.option config "database-user" in
let passwd = Get_config.option config "database-passwd" in
(* Bind to the database: *)
Db.Connection.bind ~db_name ~user_name ~passwd db;
(* Register output encodings: *)
universe # application # add_output_encoding "weekday-en" enc_weekday_en;
universe # application # add_output_encoding "month-en" enc_month_en;
universe # application # add_output_encoding "month" enc_month;
universe # application # add_output_encoding "mday-en" enc_mday_en;
universe # application # add_output_encoding "mday" enc_mday;
universe # application # add_output_encoding "year" enc_year;
universe # application # add_output_encoding "cal-day" enc_cal_day;
(* Register dialogs: *)
universe # register "startpage" (new Startpage.startpage db);
universe # register "editor" (new Editor.editor db);
universe # register "timetravel" (new Timetravel.timetravel db);
universe # register "session" (new Session.session);
universe # register "selectuser" (new Selectuser.selectuser db);
universe # register "export" (new Export.export db);
universe # register "admin" (new Admin.admin db);
universe # register "admin-password" (new Admin_password.admin_password db);
universe # register "admin-accounts"
(new Admin_accounts.admin_accounts config db);
universe # register "admin-access" (new Admin_access.admin_access db);
print_error := (extended_error_page universe)
;;
(* ======================================================================
* History:
*
* $Log: init.ml,v $
* Revision 1.5 2003/03/23 11:59:14 gerd
* GPL
*
* Revision 1.4 2003/02/03 01:28:59 gerd
* Continued.
*
* Revision 1.3 2003/01/28 01:07:00 gerd
* Continued.
*
* Revision 1.2 2003/01/27 22:14:39 gerd
* Initial revision.
*
*
*)