# HG changeset patch
# User address@hidden
# Date 1159908156 -7200
# Node ID a66569eaf6b1cb505fde6a058e55dce1185cd14a
# Parent eb62ed1a54f11cc48bc934e023d3be3b9a8e62bb
Rewrite of wdialog based web-client.
Highlights:
* register through webinterface (without verification)
* browse / edit / vote all on the same page
* some dirty hacks and assumptions since this was done mostly as a demonstration and review of the demexp architecture.
diff -r eb62ed1a54f1 -r a66569eaf6b1 config/Makefile.inc
--- a/config/Makefile.inc Sun Oct 01 17:31:20 2006 +0200
+++ b/config/Makefile.inc Tue Oct 03 22:42:36 2006 +0200
@@ -218,19 +218,11 @@ WEB_SRC:=lib/misc.ml.nw \
lib/time.ml.nw \
lib/cache.ml.nw \
web/serverConnection.ml.nw \
- web/registry.ml.nw \
- web/session.ui.nw \
- web/session.ml.nw \
+ web/variables.ui.nw \
+ web/templates.ui.nw \
+ web/pages.ml.nw \
web/login.ui.nw \
- web/login.ml.nw \
web/browse.ui.nw \
- web/browse.ml.nw \
- web/addResponse.ui.nw \
- web/addResponse.ml.nw \
- web/addQuestion.ui.nw \
- web/addQuestion.ml.nw \
- web/voteWeb.ui.nw \
- web/voteWeb.ml.nw \
web/demexpweb.ui.nw \
web/demexpweb.ml.nw
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/browse.ui.nw
--- a/web/browse.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ b/web/browse.ui.nw Tue Oct 03 22:42:36 2006 +0200
@@ -7,145 +7,217 @@
@
-Four variables are making the browse dialog state (two are comming from
-the [[session]] dialog variable):
-\begin{itemize}
-\item [[tags]]: the set of tags displayed in the tag selector;
-\item [[session.selected-tag]]: the tag chosen by the user in the tag
- selector;
-\item [[questions]]: the set of question selected by the chosen tags in
- the tag selector;
-\item [[session.selected-question]]: the question chosen by the user in
- the question selector.
-\end{itemize}
-
-Moreover, variable [[new-response]] is used to store a new response
-added by a user and variable [[responses]] is used to display the set of
-possible responses to a question.
-
<>=
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Invalid format
+
+
+
+
+ Invalid format
+
+
+
+
+
+
+
+
+
+ Response ${int}: |
+
+
+
+ |
+
+
+ With this link: |
+
+
+
+ |
+
+
+
+
+
+
+
+ $ext (rem)
+
+
+
+
+
+ Tags: (add new)
+
+ |
+
+ Questions:
+ (add new)
+
+
+ |
+
+
+
+
+ Add a new response:
+
+ With this link:
+
+
+
+
+
+
+
+
+
+ Your vote(s):
+
+ |
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+ Browse position base
+ Navigation dans la base des positions
+
+
+
+ Logged in as $[session.login]
+
+
+
+
+
+ Navigation area
+
+
+ (hide)
+
+
+
+ (show)
+
+
+
+
+
+
+
+ Name of new tag:
+
+
+
+
+
-
-
-
-
-
-
+
+
+ Title:
+ (#)
+
+ (direktlänk)
+
+ Limit date:
+
+
+
+ Tags: |
+
+
+ -
+ -
+ -
+
+ (manage tags)
+ |
+
+
+
+
+
+ Applied tags: |
+ |
+
+
+ Available tags: |
+
+
+
+
+ |
+
+
+
+ Responses: (add new)
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+ |
+
+
+ Winning response(s):
+
+ Number of votes:
+
+
+
+
+
+
+
+
New question
+ Your question:
+
+
+
+
+
+
+
+
+
+
+
+
+
@
-The browse dialog is divided into four areas:
-\begin{itemize}
-\item the error area, displaying error messages if necessary;
-\item the tag selection area, where the user can select a tag;
-\item the question selection area, where the list of questions
- corresponding to the selected tag is displayed. The user can chose one
- question from this list;
-\item the question details area, where information about the selected
- questions are displayed.
-\end{itemize}
-
-The two latter areas are displayed only if the user has used the
-previous one (cf. use of [[ui:ifvar]] construct).
-
-<>=
-
-
-
-
- Browse position base
-
-
-
-
-
-
- Navigation dans la base des positions
-
-
- Browse position base
-
-
-
-
-
-
-
-
- Logged as
-
-
-
-
-
-
-
-
-
-
-
-
- Tags:
-
-
-
-
-
-
-
- Questions:
-
-
-
-
-
-
-
-
- Title:
- (#)
-
- Limit date:
- Tags:
- Responses:
-
- Winning response(s):
-
- Number of votes:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/demexpweb.ml.nw
--- a/web/demexpweb.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ b/web/demexpweb.ml.nw Tue Oct 03 22:42:36 2006 +0200
@@ -5,19 +5,60 @@ handler.
<>=
open Wd_dialog
+open Wd_types
open Wd_run_cgi
+open Pages
+
+let main universe name env = object (self : dialog)
+ inherit dialog universe name env
+
+ initializer Pages.Var.dlg := Some self
+
+ method prepare_page () =
+ (*self#set_variable "debug" (String_value self#session#session_id);*)
+ (match self#dialog_variable "session" with
+ | None ->
+ let init dlg_name =
+ let dlg = universe#create env dlg_name in
+ self#set_variable dlg_name (Dialog_value (Some dlg))
+ in init "session";
+ init "tag";
+ init "question";
+ init "vote";
+ init "bool"
+ | Some _ -> ()
+ );
+ let lang = env.cgi#argument_value ~default:"en" "lang" in
+ self#set_variable "lang" (String_value lang);
+
+ let q_id = env.cgi#argument_value "question_id" in
+
+ let cur_page = try
+ let _ = int_of_string q_id in
+ self#set_variable "session.selected-question" (String_value q_id);
+ self#set_variable "bool.nav" (String_value "0");
+ "browse"
+ with Failure "int_of_string" -> self#string_variable "cur-page" in
+ match cur_page with
+ | "login" -> Login.prepare self
+ | "browse" -> Browse.prepare self
+ | "register" -> Register.prepare self
+ | _ -> Login.prepare self
+
+
+ method handle () =
+ match self#string_variable "cur-page" with
+ | "login" -> Login.handle self
+ | "browse" -> Browse.handle self
+ | "register" -> Register.handle self
+ | _ -> ()
+
+end
let _ =
run
~charset:`Enc_utf8
- ~reg:(fun universe ->
- universe#register "session" (new Session.session);
- universe#register "browse" (new Browse.browse);
- universe#register "vote" (new VoteWeb.vote);
- universe#register "add-response" (new AddResponse.add_response);
- universe#register "add-question" (new AddQuestion.add_question);
- universe#register "login" (new Login.login)
- )
+ ~reg:(fun universe -> universe#register "main" main)
~uifile:"demexpweb.ui"
()
@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/demexpweb.ui.nw
--- a/web/demexpweb.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ b/web/demexpweb.ui.nw Tue Oct 03 22:42:36 2006 +0200
@@ -13,25 +13,51 @@ This file includes all the other dialogs
+
+
-
-
-
+
+0">
+1">
]>
-
+
+
- &include_session;
+ &include_variables;
+ &include_templates;
&include_login;
&include_browse;
- &include_add_response;
- &include_add_question;
- &include_vote;
+
+
+
+ $body
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/login.ui.nw
--- a/web/login.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ b/web/login.ui.nw Tue Oct 03 22:42:36 2006 +0200
@@ -9,61 +9,54 @@ This dialog handles user login et settin
@
-The dialog display an error message in case the variable
-[[error-message]] is not empty through a call to [[display-error]]
-template.
+<>=
+
+
+
+ Please enter your desired login and password:
+
+ Login:
+ Password:
+ Repeat password:
+
+
+
+
+ Account creation was successfull.
+ You can now go back to the login page to login.
+
+
+
+
-\nextchunklabel{code:login.ui:login}
-<>=
-
+
+
+
+ Veuillez entrer votre identifiant et mot de passe :
+ Please enter your login and password:
+
+
+ Identifiant :
+ Login:
+
+ Mot de passe :
+ Password:
+
+
+
+
+
+
+
+
+
-
-
-
+
+ fr
+ en
+
+
+
-
-
-
-
- demexp login
-
-
-
- demexp login
-
-
-
-
-
- Veuillez entrer votre identifiant et mot de passe :
-
- Identifiant :
- Mot de passe :
-
-
-
-
-
-
- Please enter your login and password:
-
- Login:
- Password:
-
-
-
-
-
-
- fr
- en
-
-
-
-
-
-
-
-
@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/serverConnection.ml.nw
--- a/web/serverConnection.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ b/web/serverConnection.ml.nw Tue Oct 03 22:42:36 2006 +0200
@@ -10,10 +10,15 @@ open Messages_clnt
open Messages_clnt
@
-Right now, the server to connect to is statically defined.
+Right now, the server to connect to is read from the file "demexp_server".
<>=
-let web_default_server = Config.default_server_name
+let web_default_server =
+ if Sys.file_exists "demexp_server" then (
+ let ch = open_in "demexp_server" in
+ input_line ch
+ ) else Config.default_server_name
+
let web_default_port = Config.default_server_port
@
@@ -75,3 +80,96 @@ let cache_filename =
"/tmp/demexp-web-cache-" ^ web_default_server ^ ":"
^ (string_of_int web_default_port)
@
+
+Bla bla, bla?
+
+<>=
+open Messages_aux
+open Messages_clnt
+
+type server =
+ < question_info : int -> int -> question_t array
+ >
+
+let chk_rc rc errfunc =
+ if rc <> Messages_aux.rt_ok then (
+ let msg = Misc.string_of_return_code rc in
+ errfunc msg
+ )
+
+let server_of_ccc client cookie cache errfunc =
+ let chk rc = chk_rc rc errfunc in
+ object
+ (* Is this safe? *) (* well... it is ugly *)
+ method client = client method cookie = cookie method cache = cache
+
+ method question_info id quantity : question_t array =
+ let r = Cache.question_info cache client (cookie, id, quantity) in
+ chk r.question_info_rc;
+ r.question_info
+ method get_question_tags id : int array =
+ let r = Demexp.V1.get_question_tags client (cookie, id) in
+ r
+ method tag_info id quantity : info_on_tag_t array =
+ let r = Demexp.V1.tag_info client (cookie, id, quantity) in
+ chk r.tag_info_rc;
+ r.tag_info
+ method get_vote id login : int array =
+ let r = Demexp.V1.get_vote client (cookie, id, login) in
+ chk r.get_vote_rc;
+ r.get_vote
+ method vote q_id ans_ids =
+ let r = Demexp.V1.vote client (cookie, q_id, ans_ids) in
+ chk r;
+ Cache.invalidate cache (Cache.Question q_id)
+ method tag_question q_id tag_id =
+ let r = Demexp.V1.tag_question client (cookie, q_id, tag_id) in
+ chk r
+ method untag_question q_id tag_id =
+ let r = Demexp.V1.untag_question client (cookie, q_id, tag_id) in
+ chk r
+ method create_tag label : int =
+ let r = Demexp.V1.create_tag client (cookie, label) in
+ chk r.create_tag_rc;
+ r.create_tag_id
+ method add_response q_id desc link =
+ let r = Demexp.V1.add_response client (cookie, q_id, desc, link) in
+ chk r;
+ Cache.invalidate cache (Cache.Question q_id)
+ method new_question question : int =
+ let r = Demexp.V1.new_question client (cookie, question) in
+ chk r.question_id_return_code;
+ r.question_id_id
+ method set_question_status q_id status =
+ let status = Rtypes.int4_of_int status in
+ let r = Demexp.V1.set_question_status client (cookie, q_id, status) in
+ chk r
+ method max_question_id =
+ let r = Demexp.V1.max_question_id client cookie in
+ chk r.max_question_id_rc;
+ r.max_question_id
+ method add_participant login pass groups =
+ let r = Demexp.V1.add_participant client (cookie, login, pass, groups) in
+ chk r.add_participant_rc;
+ r
+ end
+
+exception Server_error of string
+
+let default_error_func err = raise (Server_error err)
+
+let do_in_server
+ ?(url=web_default_server) ?(port=web_default_port) ?(on_error=default_error_func)
+ login pass f =
+ let client, cookie = login_on_server url port login pass in
+ let cache = Cache.create cache_filename client cookie in
+ try
+ let x = f (server_of_ccc client cookie cache on_error) in
+ Cache.save cache;
+ close_connection_to_server client cookie;
+ x
+ with e ->
+ Cache.save cache;
+ close_connection_to_server client cookie;
+ raise e
+
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/pages.ml.nw
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/pages.ml.nw Tue Oct 03 22:42:36 2006 +0200
@@ -0,0 +1,500 @@
+<>=
+open Wd_dialog
+open Wd_types
+open Messages_aux
+open Messages_clnt
+
+exception Display_error = Misc.Display_error
+
+let ppe msg f x = (* prepend error *)
+ try f x with Display_error err -> raise (Display_error (msg^err))
+
+let raise_if b exc = if b then raise exc
+
+module Var = struct
+ let dlg = ref (None : dialog option)
+ let d () = match !dlg with Some d -> d | None -> assert false
+ type 'a var = { get : unit -> 'a ; set : 'a -> unit }
+ let objify var = object method get = var.get () method set = var.set end
+ let bug n f x = try f x with e -> prerr_endline ("Hint: "^n); raise e
+ let mkvar f1 f2 name = objify
+ { get = (fun () -> bug name (f1 (d())) name);
+ set = fun x -> bug name ((d())#set_variable name) (f2 x) }
+ let string_var = mkvar (fun d -> d#string_variable)
+ (fun x -> String_value x)
+ let bool_var = mkvar (fun d n -> int_of_string (d#string_variable n) <> 0)
+ (function true -> String_value "1" | false -> String_value "0")
+ let int_var = mkvar (fun d n -> int_of_string (d#string_variable n))
+ (fun x -> String_value (string_of_int x))
+ let dyn_enum_var = mkvar (fun d -> d#dyn_enum_variable)
+ (fun x -> Dyn_enum_value x)
+ let assert_string = function String_value x -> x | _ -> assert false
+ let string_alist_var =
+ mkvar (fun d n -> List.map (fun (a,b) -> a, assert_string b) (d#alist_variable n))
+ (fun x -> Alist_value (List.map (fun (a,b) -> a, String_value b) x))
+ (* variables: *)
+ let login = string_var "session.login"
+ let password = string_var "session.password"
+ let password_confirm = string_var "session.password-confirm"
+ let cur_page = string_var "cur-page"
+ let error_message = string_var "error-message"
+ module Bool = struct
+ let nav = bool_var "bool.nav"
+ let new_tag = bool_var "bool.new-tag"
+ let new_ans = bool_var "bool.new-ans"
+ let new_question = bool_var "bool.new-question"
+ let tag_question = bool_var "bool.tag-question"
+ let reg_success = bool_var "bool.reg-success"
+ end
+ module T = struct (* tag *)
+ let list = dyn_enum_var "tag.list"
+ let selected = string_var "tag.selected"
+ let previous = string_var "tag.previous"
+ let for_addition = string_var "tag.new"
+ end
+ module Q = struct (* question *)
+ let list = dyn_enum_var "question.list"
+ let selected = string_var "question.selected"
+ let previous = string_var "question.previous"
+ let title = string_var "question.title"
+ let responses = dyn_enum_var "question.responses"
+ let limit_date = string_var "question.limit-date"
+ let tags = dyn_enum_var "question.tags"
+ let winning_responses = string_var "question.winning-responses"
+ let number_of_votes = int_var "question.number-of-votes"
+ end
+ module V = struct (* vote *)
+ let list = dyn_enum_var "vote.list"
+ let selected = string_var "vote.selected"
+ end
+ module Add = struct (* values for addition *)
+ let tag = T.for_addition
+ let q_desc = string_var "question.new"
+ let ans_desc = string_var "question.new-ans"
+ let ans_url = string_var "question.new-ans-url"
+ let ans_desc_list = string_alist_var "question.new-ans-list"
+ let ans_url_list = string_alist_var "question.new-ans-url-list"
+ end
+ module Err = struct (* error variables *)
+ let general = error_message
+ let q_desc = string_var "question.error-new"
+ let ans_desc_list = string_alist_var "question.new-ans-error"
+ let ans_url_list = string_alist_var "question.new-ans-url-error"
+ end
+end
+
+let do_in_server ?(login=Var.login#get) ?(pass=Var.password#get) f =
+ let on_error x = raise (Display_error x) in
+ ServerConnection.do_in_server ~on_error login pass f
+
+let error x = Var.error_message#set x
+
+type page = Login | Register | Browse
+
+let change_page pagename =
+ Var.Err.general#set "";
+ (function
+ | Login -> Var.cur_page#set "login"
+ | Register -> Var.cur_page#set "register"
+ | Browse -> Var.cur_page#set "browse") pagename
+
+@
+
+
+Helper function [[fill_tag_selector]] get tags from the server and put
+them into the tag selector. It returns a hash table [[tags]] mapping tag
+id to label.
+
+\todo{The [[List.sort]] should handle UTF-8 encoded strings with
+ a correct locale.}
+
+<>=
+let fill_tag_list dlg srv =
+ let tags = Hashtbl.create 3 in
+ Cache.update_tags_hash tags srv#client srv#cookie srv#cache;
+ let aggregate_non_question_tags id label tag_list =
+ if not (Norm.is_question_specific_tag label) then
+ (id, label) :: tag_list
+ else tag_list in
+ let tags_dyn_val =
+ let raw_tags = Hashtbl.fold aggregate_non_question_tags tags [] in
+ let sorted = List.sort (fun (_, l1) (_, l2) -> compare l1 l2) raw_tags in
+ List.map (fun (id, label) ->
+ (string_of_int id, Printf.sprintf "%s" label)) sorted in
+ Var.T.list#set tags_dyn_val
+@
+
+
+<>=
+
+let fill_question_list dlg srv =
+ let max_question_id =
+ ppe "Unable to get max_question_id :" (fun () -> srv#max_question_id) () in
+ let questions = Hashtbl.create 3 in
+ Cache.update_questions_hash
+ questions max_question_id srv#client srv#cookie srv#cache;
+ try
+ let chosen_tag = int_of_string (Var.T.selected#get) in
+ let select_question id (desc, tags) selection =
+ if List.exists (fun tag_id -> tag_id = chosen_tag) tags then
+ (string_of_int id, desc) :: selection
+ else
+ selection in
+ Var.Q.list#set (Hashtbl.fold select_question questions [])
+ with Failure "int_of_string" -> ()
+
+let fill_question_details q_id dlg srv =
+ let q_info =
+ let x = ppe "Unable to load question information: " srv#question_info q_id 1 in
+ assert (Array.length x = 1); x.(0)
+ in
+ try
+ Var.Q.title#set q_info.q_desc;
+ (* limit date *)
+ Var.Q.limit_date#set
+ (match q_info.q_info_limit_date with
+ | x when x = Int64.zero -> "no limit date"
+ | x -> (* transform limit date in local time *)
+ let offset = Int64.to_float x in
+ Time.time_as_localtime_iso_string offset);
+ (* tags *)
+ let tag_set = srv#get_question_tags q_id in
+ let tag_pair id =
+ let arr = srv#tag_info id 1 in
+ (string_of_int id, arr.(0).a_tag_label)
+ in
+ let tag_pairs = List.map tag_pair (Array.to_list tag_set) in
+ let f (_, s) = String.length s < 9 || String.sub s 0 9 <> "question " in
+ Var.Q.tags#set (List.filter f tag_pairs);
+ (* responses *)
+ let make_url link = "" ^ link ^ "" in
+ let string_of_response i r =
+ let link =
+ if r.r_info_link <> "" then ("[" ^ (make_url r.r_info_link) ^ "]")
+ else "" in
+ (string_of_int i,
+ Printf.sprintf "%d. %s %s\n" i r.r_info_desc link) in
+ (*let descr_of_response i r =
+ (string_of_int i, r.r_info_desc) in*)
+ let str_responses =
+ Array.to_list (Array.mapi string_of_response
+ q_info.q_info_responses) in
+ Var.Q.responses#set str_responses;
+ Var.Q.number_of_votes#set q_info.q_info_num_votes;
+ (* winning response(s) *)
+ let response_desc r_id =
+ let desc = q_info.q_info_responses.(r_id).r_info_desc in
+ Printf.sprintf "%d. %s" r_id desc in
+ let str =
+ Array.fold_left
+ (fun str r_id -> str ^ (response_desc r_id) ^ " ") ""
+ q_info.q_info_elected_responses in
+ Var.Q.winning_responses#set str;
+ (* get own vote *)
+ let vote = ppe "Cannot get own vote: " (srv#get_vote q_id) Var.login#get in
+ let vote = Array.to_list vote in
+ let responses =
+ Array.to_list (Array.mapi (fun i r -> (i, r.r_info_desc))
+ q_info.q_info_responses) in
+ let votes_with_desc,_ = Misc.split_responses vote responses in
+ let string_id (id, desc) = (string_of_int id, desc) in
+ dlg#set_variable "vote.selected" (String_value "");
+ dlg#set_variable "vote.list"
+ (Dyn_enum_value (List.map string_id votes_with_desc))
+ with Display_error str -> error str
+
+let fill_question_area dlg srv =
+ fill_question_list dlg srv;
+
+ let q_id = Var.Q.selected#get in
+ let last_q_id = Var.Q.previous#get in
+ if q_id <> last_q_id then (
+ Var.Q.previous#set q_id;
+ Var.Bool.new_ans#set false;
+ Var.Bool.tag_question#set false;
+ if q_id <> "" then (Var.Bool.new_question#set false);
+ try
+ let q_id = int_of_string Var.Q.selected#get in
+ fill_question_details q_id dlg srv
+ with Failure("int_of_string") -> ()
+ )
+@
+
+
+<>=
+let submit_vote dlg =
+ let votes = dlg#dyn_enum_variable "vote.list" in
+ let vote_ids = List.map (fun (id,_) -> int_of_string id) votes in
+ try do_in_server (fun srv ->
+ let q_id = int_of_string Var.Q.selected#get in
+ ppe "Vote failed: " (srv#vote q_id) (Array.of_list vote_ids);
+ fill_question_details q_id dlg srv)
+ with Display_error msg -> error msg
+
+let submit_tags dlg =
+ let tags = List.map (fun (x,_) -> int_of_string x) Var.Q.tags#get in
+ let q_id = int_of_string Var.Q.selected#get in
+ try do_in_server (fun srv ->
+ let tag id = ppe "Cannot tag question: " (srv#tag_question q_id) id in
+ let untag id = ppe "Cannot untag question: " (srv#untag_question q_id) id in
+ let old_tags = srv#get_question_tags q_id in
+ Array.iter untag old_tags;
+ List.iter tag tags)
+ with Display_error msg -> error msg
+
+
+let prepare_question_addition dlg =
+ let change_assoc key v al =
+ let rec f = function
+ | [] -> assert false
+ | (k,x)::tl -> if k = key then (k,v) :: tl else (k,x) :: f tl
+ in f al
+ in
+ let get var n =
+ match List.assoc (string_of_int n) (dlg#alist_variable var) with
+ | String_value s -> s
+ | _ -> assert false
+ in
+ let set var n v =
+ let al = dlg#alist_variable var in
+ let nl = change_assoc (string_of_int n) (String_value v) al in
+ dlg#set_variable var (Alist_value nl) in
+ let desc = "question.new-ans-list" in
+ let url = "question.new-ans-url-list" in
+ let desc_error = "question.new-ans-error" in
+ let url_error = "question.new-ans-url-error" in
+ let ids = List.map (fun (i,_) -> int_of_string i) (dlg#alist_variable desc) in
+ let f id =
+ let a = Norm.normalize_response (get desc id) in
+ let u = Norm.normalize_link (get url id) in
+ set desc_error id ""; set url_error id ""; error "";
+ let check f v errvar =
+ try f v with Norm.Invalid_format ->
+ set errvar id "E";
+ error "Invalid response or link format"
+ in
+ if a <> "" then check (fun x -> Norm.check_response x) a desc_error;
+ check Norm.check_link u url_error;
+ (a, u)
+ in
+ let question = Norm.normalize_question (dlg#string_variable "question.new") in
+ (try Norm.check_question question with Norm.Invalid_format ->
+ dlg#set_variable "question.error-new" (String_value "E");
+ error "Invalid question format");
+ (question, List.map f ids)
+
+@
+
+<>=
+module Login = struct
+ let prepare dlg =
+ change_page Login
+
+ let handle dlg =
+ match dlg#event with
+
+ | Button "login" ->
+ (try
+ do_in_server ignore;
+ change_page Browse
+ with
+ | ServerConnection.Protocol_warning (_, _, _) ->
+ (* todo: we don't display warning message. We should show it *)
+ change_page Browse
+ | ServerConnection.Login_error (msg, _, _) -> error msg;
+ | other_exception ->
+ let msg =
+ Printf.sprintf "Unknown error message (%s). Please report it to address@hidden"
+ (Printexc.to_string other_exception) in
+ error msg
+ )
+
+ | Button "new_user" ->
+ Var.login#set "";
+ Var.password#set "";
+ Var.password_confirm#set "";
+ Var.Bool.reg_success#set false;
+ change_page Register
+
+ | _ -> ()
+end
+
+
+module Register = struct
+ let prepare dlg = ()
+
+ let handle dlg =
+ match dlg#event with
+
+ | Button "register" ->
+ error "";
+ (try do_in_server ~login:"root" ~pass:"demexp" (fun srv ->
+ raise_if (Var.password#get <> Var.password_confirm#get)
+ (Display_error "The passwords are not identical.");
+ srv#add_participant Var.login#get Var.password#get [|"classifier"|];
+ Var.Bool.reg_success#set true)
+ with Display_error msg -> error msg)
+
+
+ | Button "back" ->
+ dlg#unset_variable "session.login";
+ dlg#unset_variable "session.password";
+ change_page Login
+
+ | _ -> ()
+end
+
+
+module Browse = struct
+ let prepare dlg =
+ do_in_server (fun srv ->
+ fill_tag_list dlg srv;
+ fill_question_area dlg srv)
+
+ let handle dlg =
+ match dlg#event with
+
+ | Button("new_question") ->
+ Var.Add.q_desc#set "";
+ Var.Err.q_desc#set "";
+ Var.Q.selected#set ""; (* ugly *)
+
+ let x = ["1",""; "2",""; "3",""] in
+ Var.Add.ans_desc_list#set x;
+ Var.Add.ans_url_list#set x;
+ Var.Err.ans_desc_list#set x;
+ Var.Err.ans_url_list#set x;
+
+ Var.Bool.new_question#set true
+
+ | Button("add_response") ->
+ Var.Add.ans_desc#set "";
+ Var.Add.ans_url#set "";
+ Var.Bool.new_ans#set true
+
+ | Button("new_tag") ->
+ Var.T.for_addition#set "";
+ Var.Bool.new_tag#set true
+
+ | Button("submit_tag") ->
+ (try do_in_server (fun srv -> srv#create_tag Var.T.for_addition#get);
+ Var.Bool.new_tag#set false
+ with Display_error msg -> error msg)
+
+ | Button("cancel_new_tag") -> Var.Bool.new_tag#set false
+
+ | Button("logout") -> change_page Login
+
+ | Button "move_up" ->
+ let id = Var.V.selected#get in
+ let rec f = function
+ | [] -> assert false
+ | v::[] -> [v]
+ | v::(i,desc)::tl when i = id -> (i,desc)::v::tl
+ | v::tl -> v :: f tl
+ in Var.V.list#set (f Var.V.list#get);
+ submit_vote dlg
+
+ | Button "move_down" ->
+ let id = Var.V.selected#get in
+ let rec f = function
+ | [] -> assert false
+ | v::[] -> [v]
+ | (i,desc)::v::tl when i = id -> v::(i,desc)::tl
+ | v::tl -> v :: f tl
+ in Var.V.list#set (f Var.V.list#get);
+ submit_vote dlg
+
+ | Button "remove" ->
+ let id = Var.V.selected#get in
+ Var.V.list#set (List.filter (fun (x,_) -> x<>id) Var.V.list#get);
+ Var.V.selected#set "";
+ submit_vote dlg
+
+ | Button "ans_submit" ->
+ let desc = Var.Add.ans_desc#get in
+ let link = Var.Add.ans_url#get in
+ let q_id = int_of_string Var.Q.selected#get in
+ (try
+ do_in_server (fun srv -> srv#add_response q_id desc link);
+ Var.Bool.new_ans#set false;
+ Var.Add.ans_desc#set "";
+ Var.Add.ans_url#set "";
+ Var.Q.previous#set ""; (* ugly *)
+ with Display_error msg ->
+ error ("Error while adding a new response: "^msg)
+ )
+
+ | Button "ans_cancel" ->
+ dlg#set_variable "question.new-ans" (String_value "");
+ Var.Bool.new_ans#set false
+
+ | Button "nav_tgl" -> Var.Bool.nav#set (not Var.Bool.nav#get)
+ | Button "cancel_new_question" -> Var.Bool.new_question#set false
+ | Button "manage_tags" -> Var.Bool.tag_question#set true
+ | Button "tag_question_done" -> Var.Bool.tag_question#set false
+
+ | Button "another_ans" ->
+ let f x = x#set (x#get @ [string_of_int (List.length x#get + 1), ""]) in
+ List.iter f [Var.Add.ans_desc_list; Var.Add.ans_url_list;
+ Var.Err.ans_desc_list; Var.Err.ans_url_list]
+
+ | Button "submit_question" ->
+ let question, answers = prepare_question_addition dlg in
+ let tag_id = int_of_string Var.T.selected#get in
+ (try
+ if Var.Err.general#get <> "" then raise (Failure "won't commit this");
+ do_in_server (fun srv ->
+ let q_id = ppe "Cannot add question: " srv#new_question question in
+ let f (desc, url) =
+ if desc <> "" then
+ ppe "Cannot add response: " (srv#add_response q_id desc) url
+ in List.iter f answers;
+ ppe "Cannot set question status: " (srv#set_question_status q_id) 2;
+ ppe "Cannot tag question: " (srv#tag_question q_id) tag_id;
+ );
+ Var.Bool.new_question#set false
+ with Misc.Display_error msg -> error msg;
+ | Failure "won't commit this" -> ()
+ )
+
+ | Indexed_button ("remove_tag", id) ->
+ let applied = Var.Q.tags#get in
+ if not (List.exists (fun (x,_) -> x = id) applied) then
+ error ("Can't remove tag #"^id);
+ Var.Q.tags#set (List.filter (fun (x,_) -> x <> id) applied);
+ submit_tags dlg
+
+ | Button "tag_question_add" ->
+ (* TODO: use Add.tag instead of question.new-tag *)
+ let applied = Var.Q.tags#get in
+ let selected = dlg#string_variable "question.new-tag" in
+ let available = Var.T.list#get in
+ if List.exists (fun (x,_) -> x = selected) applied then
+ error "Tag already in use."
+ else (
+ let name = List.assoc selected available in
+ Var.Q.tags#set (applied @ [selected, name]);
+ submit_tags dlg
+ )
+
+ | Button str when String.sub str 0 5 = "vote_" ->
+ let vote_id = String.sub str 5 (String.length str - 5) in
+ let responses = Var.Q.responses#get in
+ let votes = Var.V.list#get in
+ let vote = List.find (fun (id,_) -> id = vote_id) responses in
+ let rec f = function
+ | [] -> [vote]
+ | v::[] -> if v = vote then [v] else v::[vote]
+ | v1::v2::tl -> if v1 = vote then v1::v2::tl else
+ if v2 = vote then v2::v1::tl else
+ v1 :: f (v2::tl)
+ in
+ Var.V.list#set (f votes);
+ Var.V.selected#set vote_id;
+ submit_vote dlg
+
+ | _ ->
+ ()
+end
+@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/templates.ui.nw
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/templates.ui.nw Tue Oct 03 22:42:36 2006 +0200
@@ -0,0 +1,60 @@
+<>=
+
+
+@
+
+The [[display-a-response]] template is used to print one question's
+response. It is used when displaying the set of responses to a
+question.
+
+<>=
+
+ $ext (vote)
+
+@
+
+Template [[display-error]] is used to display the
+[[session.error-message]] at the top of each web page of the web
+interface.
+
+<>=
+
+
+
+ Error:
+
+
+
+@
+
+Template [[make-hyperlink]] prints the hyperlink [[href]] and made it
+clickable.
+
+<>=
+
+ $href
+
+@
+
+<>=
+
+
+
+ $title
+
+
+
+ $title
+
+
+ $body
+
+
+
+
+
+@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/variables.ui.nw
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/web/variables.ui.nw Tue Oct 03 22:42:36 2006 +0200
@@ -0,0 +1,72 @@
+\chapter{\texttt{session} dialog}
+
+The [[session]] dialogs contains the variables used by all other
+dialogs. It allows to have persistency of the state between the dialogs.
+
+\section{\texttt{session} dialog definition}
+
+<>=
+
+
+@
+
+<>=
+
+
+ demo
+
+
+ demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ &true;
+ &false;
+ &false;
+ &false;
+ &false;
+ &false;
+ &false;
+
+
+@
+
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/addQuestion.ml.nw
--- a/web/addQuestion.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-\section{\texttt{addQuestion} backend code}
-
-<>=
-(* copyright 2005-2006 David MENTRE *)
-(* this software is under GNU GPL. See COPYING.GPL file for details *)
-
-open Wd_dialog
-open Wd_types
-open Messages_aux
-open Messages_clnt
-open Norm
-@
-
-Function [[prepare_question_addition]] normalizes question, responses
-and links, checks their correctness and asks the user for confirmation
-(through use of the [[ifvar]] mechanism in the [[add-question]] dialog).
-
-<>=
-let prepare_question_addition add_q_obj =
- let set_err_var var = add_q_obj#set_variable var (String_value "E") in
- let unset_err_var var = add_q_obj#set_variable var (String_value "") in
- let question =
- normalize_question (add_q_obj#string_variable "new-question") in
- let response1 =
- normalize_response (add_q_obj#string_variable "new-response1") in
- let response2 =
- normalize_response (add_q_obj#string_variable "new-response2") in
- let response3 =
- normalize_response (add_q_obj#string_variable "new-response3") in
- let link1 = normalize_link (add_q_obj#string_variable "new-link1") in
- let link2 = normalize_link (add_q_obj#string_variable "new-link2") in
- let link3 = normalize_link (add_q_obj#string_variable "new-link3") in
- try
- check_question question;
- try
- if response1 <> "" then check_response response1;
- if response2 <> "" then check_response response2;
- if response3 <> "" then check_response response3;
- check_link link1;
- check_link link2;
- check_link link3;
- add_q_obj#set_variable "question-to-add" (String_value question);
- add_q_obj#set_variable "response1-to-add" (String_value response1);
- add_q_obj#set_variable "response2-to-add" (String_value response2);
- add_q_obj#set_variable "response3-to-add" (String_value response3);
- add_q_obj#set_variable "link1-to-add" (String_value link1);
- add_q_obj#set_variable "link2-to-add" (String_value link2);
- add_q_obj#set_variable "link3-to-add" (String_value link3);
- (* clear possible error message *)
- unset_err_var "error-question";
- unset_err_var "error-response1"; unset_err_var "error-response2";
- unset_err_var "error-response3";
- unset_err_var "error-link1"; unset_err_var "error-link2";
- unset_err_var "error-link3";
- Session.display_error_message add_q_obj ""
- with Invalid_format ->
- Session.display_error_message add_q_obj
- "Invalid response or link format";
- if not (is_valid_response response1) then set_err_var "error-response1";
- if not (is_valid_response response2) then set_err_var "error-response2";
- if not (is_valid_response response3) then set_err_var "error-response3";
- if not (is_valid_link link1) then set_err_var "error-link1";
- if not (is_valid_link link2) then set_err_var "error-link2";
- if not (is_valid_link link3) then set_err_var "error-link3";
- (* avoid displaying Confirm button *)
- add_q_obj#set_variable "question-to-add" (String_value "")
- with Invalid_format ->
- set_err_var "error-question";
- Session.display_error_message add_q_obj "Invalid question format";
- (* avoid displaying Confirm button *)
- add_q_obj#set_variable "question-to-add" (String_value "")
-@
-
-Function [[add_question]] adds a new question (and its set of responses
-and link) on the server.
-
-<>=
-let add_question_to_server add_q_obj =
- let question = add_q_obj#string_variable "question-to-add" in
- let response1 = add_q_obj#string_variable "response1-to-add" in
- let response2 = add_q_obj#string_variable "response2-to-add" in
- let response3 = add_q_obj#string_variable "response3-to-add" in
- let link1 = add_q_obj#string_variable "link1-to-add" in
- let link2 = add_q_obj#string_variable "link2-to-add" in
- let link3 = add_q_obj#string_variable "link3-to-add" in
- let client, cookie = Session.connect_to_server add_q_obj in
- try
- let ret = Demexp.V1.new_question client (cookie, question) in
- if ret.question_id_return_code <> rt_ok then (
- let msg =
- Printf.sprintf "Cannot add question '%s': %s"
- question
- (Misc.string_of_return_code ret.question_id_return_code) in
- raise (Misc.Display_error msg)
- );
- let q_id = ret.question_id_id in
- (* add responses *)
- let register_response response link =
- let ret =
- Demexp.V1.add_response client (cookie, q_id, response, link) in
- if ret <> rt_ok then
- let msg =
- Printf.sprintf "Cannot add response '%s': %s"
- response (Misc.string_of_return_code ret) in
- raise (Misc.Display_error msg) in
- if response1 <> "" then register_response response1 link1;
- if response2 <> "" then register_response response2 link2;
- if response3 <> "" then register_response response3 link3;
- ServerConnection.close_connection_to_server client cookie
- with Misc.Display_error str ->
- Session.display_error_message add_q_obj str;
- ServerConnection.close_connection_to_server client cookie
-@
-
-The [[add_question]] class defines handling of events for the
-[[add-question]] dialog.
-
-<>=
-class add_question universe name env =
- object (self)
- inherit dialog universe name env
-
- method prepare_page () =
- self#set_variable "lang"
- (String_value (self#string_variable "session.lang"))
-
- method handle () =
- match self#event with
- | Button("add") -> prepare_question_addition self
-
- | Button("confirm") ->
- add_question_to_server self;
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg)
-
- | Button("cancel") ->
- Session.display_error_message self "";
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg)
-
- | _ ->
- ()
- end
-@
-
-
-At module start, we register creation function into the Registry.
-
-<>=
-let _ =
- Registry.new_add_question :=
- fun universe env session ->
- let dlg = universe#create env "add-question" in
- dlg#set_variable "session" (Dialog_value session);
- dlg
-@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/addQuestion.ui.nw
--- a/web/addQuestion.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,171 +0,0 @@
-\chapter{\texttt{addQuestion} dialog}
-
-\section{\texttt{addQuestion} dialog definition}
-
-<>=
-
-
-@
-
-
-Template [[print-error]] displays a [[Format error]] message on web page
-if [[var]] variable is not empty. This template is used to display an
-error information for each input field.
-
-<>=
-
-
- Invalid format
-
-
-@
-
-Variables [[new-*]] are filled in by the user on the web page. Once the
-user clicks on [[Add your question]], their content are normalized and
-put into corresponding [[*-to-add]] variables. This is the content of
-the latter variables that are used when registering the new question on
-the server.
-
-<>=
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
-The variables [[error-*]] are used to display an error message next to
-the corresponding field when not empty.
-
-<>=
-
-
-
-
-
-
-
-
-@
-
-The content of the web page: all the user input fields.
-
-<>=
-
-
-
-
- New question
-
-
-
- New question
-
-
-
-
-
-
- Your question:
-
-
-
-
- Response 1:
-
- With this link:
-
-
-
-
- Response 2:
-
- With this link:
-
-
-
-
- Response 3:
-
- With this link:
-
-
-
-
-
-
-
-@
-
-When all the field contents have been validated, they are printed for
-confirmation. We only print fields that are not empty.
-
-<>=
-
-
- Confirm to add question:
-
- With responses:
-
-
- -
-
- []
-
-
-
-
- -
-
- []
-
-
-
-
- -
-
- []
-
-
-
-
- You won't be able to modify it afterward!
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/addResponse.ml.nw
--- a/web/addResponse.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +0,0 @@
-\section{\texttt{addReponse} backend code}
-
-<>=
-(* copyright 2005-2006 David MENTRE *)
-(* this software is under GNU GPL. See COPYING.GPL file for details *)
-
-open Wd_dialog
-open Wd_types
-open Messages_aux
-open Messages_clnt
-@
-
-Function [[prepare_response_addition]] normalizes response and link
-correctness and ask the user for confirmation (through use of the
-[[ifvar]] mechanism in the [[add-response]] dialog).
-
-<>=
-let prepare_response_addition add_resp_obj =
- let response =
- Norm.normalize_response (add_resp_obj#string_variable "new-response") in
- let link =
- Norm.normalize_link (add_resp_obj#string_variable "new-link") in
- try
- Norm.check_response response;
- try
- if link <> "" then Norm.check_link link;
- add_resp_obj#set_variable "response-to-add" (String_value response);
- add_resp_obj#set_variable "link-to-add" (String_value link);
- (* clear possible error message *)
- Session.display_error_message add_resp_obj ""
- with Norm.Invalid_format ->
- Session.display_error_message add_resp_obj "Invalid link format";
- add_resp_obj#set_variable "response-to-add" (String_value "");
- add_resp_obj#set_variable "link-to-add" (String_value "")
- with Norm.Invalid_format ->
- Session.display_error_message add_resp_obj "Invalid response format";
- add_resp_obj#set_variable "response-to-add" (String_value "");
- add_resp_obj#set_variable "link-to-add" (String_value "")
-@
-
-Function [[add_response]] adds a new reponse to currently selected
-question.
-
-<>=
-let add_response_to_server add_resp_obj =
- let response = add_resp_obj#string_variable "response-to-add" in
- let link = add_resp_obj#string_variable "link-to-add" in
- let client, cookie = Session.connect_to_server add_resp_obj in
- try
- let q_id = int_of_string
- (add_resp_obj#string_variable "session.selected-question") in
- let ret = Demexp.V1.add_response client (cookie, q_id, response, link) in
- if ret <> rt_ok then (
- let msg = Printf.sprintf "Error while adding a new reponse: %s"
- (Misc.string_of_return_code ret) in
- raise (Misc.Display_error msg)
- );
- (* invalidate this entry in the cache *)
- let cache = Cache.create ServerConnection.cache_filename client cookie in
- Cache.invalidate cache (Cache.Question q_id);
- Cache.save cache;
- ServerConnection.close_connection_to_server client cookie
- with Misc.Display_error str ->
- Session.display_error_message add_resp_obj str;
- ServerConnection.close_connection_to_server client cookie
-@
-
-The [[add_response]] class defines handling of events for the
-[[add-response]] dialog.
-
-<>=
-class add_response universe name env =
- object (self)
- inherit dialog universe name env
-
- method prepare_page () =
- self#set_variable "lang"
- (String_value (self#string_variable "session.lang"))
-
- method handle () =
- match self#event with
- | Button("add") -> prepare_response_addition self
-
- | Button("confirm") ->
- add_response_to_server self;
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg)
-
- | Button("cancel") ->
- Session.display_error_message self "";
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg)
-
- | _ ->
- ()
- end
-@
-
-
-At module start, we register creation function into the Registry.
-
-<>=
-let _ =
- Registry.new_add_response :=
- fun universe env session title responses ->
- let dlg = universe#create env "add-response" in
- dlg#set_variable "session" (Dialog_value session);
- dlg#set_variable "title" title;
- dlg#set_variable "responses" responses;
- dlg
-@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/addResponse.ui.nw
--- a/web/addResponse.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-\chapter{\texttt{addResponse} dialog}
-
-\section{\texttt{addResponse} dialog definition}
-
-<>=
-
-
-@
-
-The variables [[title]] and [[responses]] respectively store the
-question title and its current responses. Those variables are
-initialized at [[add-response]] dialog creation.
-
-Variables [[new-response]] and [[new-link]] are filled in by the user on
-the web page. Once the user clicks on [[Add your response]], their
-content is normalized and put into [[response-to-add]] and
-[[link-to-add]]. This is the content of the latter two variables that is
-used when registering the new response on the server.
-
-<>=
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
-We use template [[display-a-reponse]] defined in [[session]] dialog (cf.
-\codechunkref{code:template-display-a-reponse}).
-
-<>=
-
-
-
-
-
- New response to
- (#)
-
-
-
-
-
-
- Add a new response to question
- (#)
-
-
-
-
-
-
-
-
- To question:
- (#)
-
- With responses:
-
-
-
-
- Add a new reponse:
-
- With this link:
-
-
-
-
-
-
-
-
-
- Confirm to add response:
-
- []
-
- You won't be able to modify it afterward!
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/browse.ml.nw
--- a/web/browse.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,212 +0,0 @@
-\section{browse backend code}
-
-<>=
-(* copyright 2005-2006 David MENTRE *)
-(* this software is under GNU GPL. See COPYING.GPL file for details *)
-
-open Wd_dialog
-open Wd_types
-open Messages_aux
-open Messages_clnt
-@
-
-Helper function [[fill_tag_selector]] get tags from the server and put
-them into the tag selector. It returns a hash table [[tags]] mapping tag
-id to label.
-
-\todo{The [[List.sort]] should handle UTF-8 encoded strings with
- a correct locale.}
-
-<>=
-let fill_tag_selector browse_object client cookie cache =
- let tags = Hashtbl.create 3 in
- Cache.update_tags_hash tags client cookie cache;
- let aggregate_non_question_tags id label tag_list =
- if not (Norm.is_question_specific_tag label) then
- (id, label) :: tag_list
- else tag_list in
- let tags_dyn_val =
- let raw_tags = Hashtbl.fold aggregate_non_question_tags tags [] in
- let sorted = List.sort (fun (_, l1) (_, l2) -> compare l1 l2) raw_tags in
- List.map (fun (id, label) ->
- (string_of_int id, Printf.sprintf "%s" label)) sorted in
- browse_object#set_variable "tags" (Dyn_enum_value tags_dyn_val);
- tags
-@
-
-Helper function [[fill_question_area]] selects the questions having the
-chosen tag. Moreover, if one of them is selected, it displays this
-question details.
-
-<>=
-let fill_question_area browse_object tags client cookie cache =
- (* get maximum question number *)
- let ret = Demexp.V1.max_question_id client cookie in
- if ret.max_question_id_rc <> rt_ok then
- raise (Misc.Display_error
- (Printf.sprintf "unable to get max_question_id (%s)"
- (Misc.string_of_return_code ret.max_question_id_rc)));
- (* get classification *)
- let questions = Hashtbl.create 3 in
- Cache.update_questions_hash
- questions ret.max_question_id client cookie cache;
- try
- let chosen_tag =
- int_of_string (browse_object#string_variable "session.selected-tag") in
- let select_question id (desc, tags) selection =
- if List.exists (fun tag_id -> tag_id = chosen_tag) tags then
- (string_of_int id, desc) :: selection
- else
- selection in
- let selected_questions = Hashtbl.fold select_question questions [] in
- browse_object#set_variable "questions"
- (Dyn_enum_value selected_questions);
-
- (* fill the question details *)
- try
- let q_id = int_of_string (browse_object#string_variable
- "session.selected-question") in
- let ret = Cache.question_info cache client (cookie, q_id, 1) in
- if ret.question_info_rc <> rt_ok then
- raise (Misc.Display_error
- (Printf.sprintf
- "Unable to load information on question:%d : %s"
- q_id (Misc.string_of_return_code ret.question_info_rc)))
- else if Array.length ret.question_info <> 1 then
- raise (Misc.Display_error
- (Printf.sprintf
- "Invalid array length for question_info:%d : %d"
- q_id (Array.length ret.question_info)))
- else (
- (* question descriptor *)
- browse_object#set_variable "title"
- (String_value ret.question_info.(0).q_desc);
- (* limit date *)
- let limit_date =
- match ret.question_info.(0).q_info_limit_date with
- | x when x = Int64.zero -> "no limit date"
- | x -> (* transform limit date in local time *)
- let offset = Int64.to_float x in
- Time.time_as_localtime_iso_string offset in
- browse_object#set_variable "limit-date" (String_value limit_date);
- (* tags *)
- let _, q_tags = Hashtbl.find questions q_id in
- let q_label_tags = List.map (fun id -> Hashtbl.find tags id) q_tags in
- let str =
- List.fold_left (fun str e -> str ^ e ^ " - ")
- " - " q_label_tags in
- browse_object#set_variable "question-tags" (String_value str);
- (* responses *)
- let make_url link =
- "" ^ link ^ "" in
- let string_of_response i r =
- let link =
- if r.r_info_link <> "" then ("[" ^ (make_url r.r_info_link) ^ "]")
- else "" in
- (string_of_int i,
- Printf.sprintf "%d. %s %s\n" i r.r_info_desc link) in
- let str_responses =
- Array.to_list (Array.mapi string_of_response
- ret.question_info.(0).q_info_responses) in
- browse_object#set_variable "responses" (Dyn_enum_value str_responses);
- (* number of votes *)
- browse_object#set_variable "number-of-votes"
- (String_value
- (string_of_int ret.question_info.(0).q_info_num_votes));
- (* winning response(s) *)
- let response_desc r_id =
- let desc =
- ret.question_info.(0).q_info_responses.(r_id).r_info_desc in
- Printf.sprintf "%d. %s" r_id desc in
- let str =
- Array.fold_left
- (fun str r_id -> str ^ (response_desc r_id) ^ ". ") ""
- ret.question_info.(0).q_info_elected_responses in
- browse_object#set_variable "winning-responses" (String_value str)
- )
- with Failure("int_of_string") -> ()
- with
- Failure("int_of_string") -> ()
- | Misc.Display_error str -> Session.display_error_message browse_object str
-@
-
-Helper function [[setup_dialog]] is used to prepare the browse dialog
-contained in [[browse_object]].
-
-\nextchunklabel{code:browse.ml:setup_dialog}
-<>=
-let setup_dialog browse_object =
- let client, cookie = Session.connect_to_server browse_object in
-
- let cache = Cache.create ServerConnection.cache_filename client cookie in
-
- let tags = fill_tag_selector browse_object client cookie cache in
- fill_question_area browse_object tags client cookie cache;
-
- Cache.save cache;
-
- ServerConnection.close_connection_to_server client cookie;
-
- browse_object#set_variable "lang"
- (String_value (browse_object#string_variable "session.lang"))
-@
-
-The [[browse]] class defines handling of events for the [[browse]] dialog.
-
-<>=
-class browse universe name env =
- object (self)
- inherit dialog universe name env
-
- method prepare_page() = setup_dialog self
-
- method handle() =
- match self#event with
- | Button("reset") ->
- self#set_variable "session.selected-tag" (String_value "");
- self#set_variable "questions" (Dyn_enum_value []);
- self#set_variable "session.selected-question" (String_value "")
-
- | Button("new_question") ->
- let session = self#dialog_variable "session" in
- let new_dlg =
- !Registry.new_add_question universe env session in
- raise(Change_dialog new_dlg)
-
- | Button("get-questions") ->
- self#set_variable "session.selected-question" (String_value "")
-
- | Button("add_response") ->
- let session = self#dialog_variable "session" in
- let new_dlg =
- !Registry.new_add_response universe env session
- (self#variable "title") (self#variable "responses") in
- raise(Change_dialog new_dlg)
-
- | Button("logout") ->
- let new_dlg = !Registry.new_login universe env in
- raise(Change_dialog new_dlg)
-
- | Button("vote_on_question") ->
- let session = self#dialog_variable "session" in
- let new_dlg =
- !Registry.new_vote universe env session
- (self#variable "title") in
- raise(Change_dialog new_dlg)
-
- | _ ->
- ()
- end
-@
-
-
-At module start, we register creation function into the Registry.
-
-<>=
-let _ =
- Registry.new_browse :=
- fun universe env session ->
- let dlg = universe#create env "browse" in
- dlg#set_variable "session" (Dialog_value session);
- dlg
-@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/login.ml.nw
--- a/web/login.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-\section{login backend code}
-
-<>=
-(* copyright 2005-2006 David MENTRE *)
-(* this software is under GNU GPL. See COPYING.GPL file for details *)
-
-open Wd_dialog
-open Wd_types
-open ServerConnection
-@
-
-Class [[login]] connects to the server and checks the given login is
-valid. If not, we stay on the same dialog, otherwise we go to browsing
-dialog.
-
-\fixme{RPC errors are not handled.}
-
-\nextchunklabel{code:login.ml:login}
-<>=
-class login universe name env =
- object (self)
- inherit dialog universe name env
-
- method prepare_page() =
- let session = new Session.session universe "session" env in
- self#set_variable "session" (Dialog_value (Some session));
- (* get requested lang and set it for the session and current dialog *)
- let lang = env.cgi#argument_value ~default:"en" "lang" in
- self#set_variable "lang" (String_value lang);
- self#set_variable "session.lang" (String_value lang)
-
- method handle() =
- let switch_to_browse_dialog client cookie =
- close_connection_to_server client cookie;
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg) in
-
- match self # event with
- Button("login") ->
- (try
- let login = self#string_variable "session.login"
- and password = self#string_variable "session.password" in
- let client, cookie =
- login_on_server web_default_server web_default_port
- login password in
- (* no exception raised until here, login has succeeded *)
- switch_to_browse_dialog client cookie
- with
- | Change_dialog d ->
- raise (Change_dialog d)
- | Change_page p ->
- raise (Change_page p)
- | Protocol_warning (_, client, cookie) ->
- (* todo: we don't display warning message. We should show it *)
- switch_to_browse_dialog client cookie
- | Login_error (msg, client, cookie) ->
- self#set_variable "session.error-message" (String_value msg);
- close_connection_to_server client cookie;
- raise(Change_page "login-page")
- | other_exception ->
- let msg =
- Printf.sprintf "Unknown error message (%s). Please report it to address@hidden"
- (Printexc.to_string other_exception) in
- self#set_variable "session.error-message" (String_value msg);
- raise(Change_page "login-page")
- )
-
- | _ ->
- ()
- end
-@
-
-At module start, we register creation function into the Registry.
-
-<>=
-let _ =
- Registry.new_login :=
- fun universe env ->
- let dlg = universe#create env "login" in
- dlg
-@
-
-\section{Setting of session language}
-
-In order to handle translation of dialogs, each dialog has a [[lang]]
-variable defined as [[lang-variable]] in the [[]] tag (see
-\codechunkref{code:login.ui:login}). Its value is set at dialog setup
-time in the [[prepare_page]] method by reading a session wide
-[[session.lang]] variable (see for example
-\codechunkref{code:browse.ml:setup_dialog}).
-
-The value of [[session.lang]] variable is set in the login dialog, by
-reading the value of [[?lang=]] CGI parameter (see
-\codechunkref{code:login.ml:login}).
-
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/registry.ml.nw
--- a/web/registry.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-\chapter{\texttt{Registry} module}
-
-Module [[Registry]] is a simple stub code to store a creation function
-for each dialog. Its purpose is to break circular dependencies between
-modules defining each dialog. Each creation function is stored in
-[[Registry]] at the end of each module containing the code of the
-corresponding dialog.
-
-<>=
-open Wd_types
-
-let new_session =
- ref (fun _ _ -> assert false
- : universe_type -> environment -> dialog_type)
-
-let new_login =
- ref (fun _ _ -> assert false
- : universe_type -> environment -> dialog_type)
-
-let new_browse =
- ref (fun _ _ _ -> assert false
- : universe_type -> environment -> dialog_type option -> dialog_type)
-
-let new_vote =
- ref (fun _ _ _ -> assert false
- : universe_type -> environment -> dialog_type option
- -> var_value -> dialog_type)
-
-let new_add_response =
- ref (fun _ _ _ _ _ -> assert false
- : universe_type -> environment -> dialog_type option
- -> var_value -> var_value -> dialog_type)
-
-let new_add_question =
- ref (fun _ _ _ -> assert false
- : universe_type -> environment -> dialog_type option -> dialog_type)
-
-@
-
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/session.ml.nw
--- a/web/session.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-\section{session backend code}
-
-<>=
-(* copyright 2005 David MENTRE *)
-(* this software is under GNU GPL. See COPYING.GPL file for details *)
-
-open Wd_dialog
-open Wd_types
-open ServerConnection
-@
-
-
-Function [[display_error_message]] update the [[session.error-message]]
-variable of dialog [[dialog_obj]] with [[str]] message.
-
-<>=
-let display_error_message dialog_obj str =
- dialog_obj#set_variable "session.error-message" (String_value str)
-@
-
-Helper function [[connect_to_server]] opens a new connection to the
-server and login onto it, using current session login and password. It
-returns client and cookie identifiying the connection.
-
-<>=
-let connect_to_server dialog_object =
- let login = dialog_object#string_variable "session.login"
- and password = dialog_object#string_variable "session.password" in
- login_on_server web_default_server web_default_port login password
-@
-
-The session backend code does nothing except creation of [[session]]
-dialog.
-
-<>=
-class session universe name env =
- object (self)
- inherit dialog universe name env
-
- method prepare_page () = ()
-
- method handle () = ()
- end
-
-let _ =
- Registry.new_session :=
- fun universe env ->
- universe#create env "session"
-@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/session.ui.nw
--- a/web/session.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-\chapter{\texttt{session} dialog}
-
-The [[session]] dialogs contains the variables used by all other
-dialogs. It allows to have persistency of the state between the dialogs.
-
-\section{\texttt{session} dialog definition}
-
-<>=
-
-
-@
-
-The [[display-a-response]] template is used to print one question's
-response. It is used when displaying the set of responses to a
-question.
-
-The [[ui:special]] tag is used in the [[display-a-response]] template so
-that generated hyperlinks are not escaped.
-
-\nextchunklabel{code:template-display-a-reponse}
-<>=
-
-
- $ext
-
-
-@
-
-Template [[display-error]] is used to display the
-[[session.error-message]] at the top of each web page of the web
-interface.
-
-<>=
-
-
-
- Error:
-
-
-
-@
-
-Template [[make-hyperlink]] prints the hyperlink [[href]] and made it
-clickable.
-
-<>=
-
- $href
-
-@
-
-We finally define the session variables themselves.
-
-<>=
-
-
-
-
-
-
-
-
-
-
- demo
-
-
-
- demo
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/voteWeb.ml.nw
--- a/web/voteWeb.ml.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,197 +0,0 @@
-\section{[[voteWeb]] backend code}
-
-<>=
-(* copyright 2005-2006 David MENTRE *)
-(* this software is under GNU GPL. See COPYING.GPL file for details *)
-
-open Wd_dialog
-open Wd_types
-open Messages_aux
-open Messages_clnt
-@
-
-Function [[initialize_selectors]] is called systematically at each
-dialog creation. If both the selectors of available and chosen answers
-are empty then the dialog has never been initialized. In that case, the
-set of available answers and the user vote are drawn from the server and
-the selectors are initialized accordingly.
-
-<>=
-let initialize_selectors vote_obj =
- let available = vote_obj#dyn_enum_variable "available" in
- let chosen = vote_obj#dyn_enum_variable "chosen" in
- if available = [] && chosen = [] then (
- (* dialog never initialized, so do it now *)
- let client, cookie = Session.connect_to_server vote_obj in
- try
- let cache = Cache.create ServerConnection.cache_filename client cookie in
- let q_id = int_of_string (vote_obj#string_variable
- "session.selected-question") in
- let ret = Cache.question_info cache client (cookie, q_id, 1) in
- if ret.question_info_rc <> rt_ok then
- raise (Misc.Display_error
- (Printf.sprintf
- "Unable to load information on question:%d : %s"
- q_id (Misc.string_of_return_code ret.question_info_rc)))
- else if Array.length ret.question_info <> 1 then
- raise (Misc.Display_error
- (Printf.sprintf
- "Invalid array length for question_info:%d : %d"
- q_id (Array.length ret.question_info)));
- (* we have information about our question, so now get our vote *)
- let login = vote_obj#string_variable "session.login" in
- let vote = Demexp.V1.get_vote client (cookie, q_id, login) in
- if vote.get_vote_rc <> rt_ok then
- raise (Misc.Display_error
- (Printf.sprintf "Cannot get my own vote. Error: %s"
- (Misc.string_of_return_code vote.get_vote_rc)));
- let my_vote = Array.to_list vote.get_vote in
- let q_responses =
- Array.to_list (Array.mapi (fun i r -> (i, r.r_info_desc))
- ret.question_info.(0).q_info_responses) in
- let my_vote_with_desc, others_with_desc =
- Misc.split_responses my_vote q_responses in
- let string_id (id, desc) = (string_of_int id, desc) in
- vote_obj#set_variable "available"
- (Dyn_enum_value (List.map string_id others_with_desc));
- vote_obj#set_variable "chosen"
- (Dyn_enum_value (List.map string_id my_vote_with_desc));
- ServerConnection.close_connection_to_server client cookie
- with Misc.Display_error str ->
- Session.display_error_message vote_obj str;
- ServerConnection.close_connection_to_server client cookie
- )
-@
-
-Function [[up_selected_item]] moves the selected item in the vote
-selector one item up.
-
-<>=
-let up_selected_item vote_obj =
- let selected_id = vote_obj#string_variable "selected-chosen" in
- let chosen = vote_obj#dyn_enum_variable "chosen" in
- let rec move_up_selected selected_id previous remaining =
- match remaining with
- | [] -> previous
- | item :: (id, desc) :: tail when id = selected_id ->
- previous @ ((id, desc) :: item :: tail)
- | item :: tail ->
- move_up_selected selected_id (previous @ [item]) tail in
- vote_obj#set_variable "chosen"
- (Dyn_enum_value (move_up_selected selected_id [] chosen))
-@
-
-Function [[down_selected_item]] moves the selected item in the vote
-selector one item down.
-
-<>=
-let down_selected_item vote_obj =
- let selected_id = vote_obj#string_variable "selected-chosen" in
- let chosen = vote_obj#dyn_enum_variable "chosen" in
- let rec move_down_selected selected_id previous remaining =
- match remaining with
- | [] -> previous
- | (id, desc) :: item :: tail when id = selected_id ->
- previous @ (item :: (id, desc) :: tail)
- | item :: tail ->
- move_down_selected selected_id (previous @ [item]) tail in
- vote_obj#set_variable "chosen"
- (Dyn_enum_value (move_down_selected selected_id [] chosen))
-@
-
-Function [[move_from_a_to_b]] moves selected [[item]] from selector
-[[a]] to selector [[b]].
-
-<>=
-let move_from_a_to_b vote_obj ~item ~a ~b =
- let selected_id = vote_obj#string_variable item in
- let a_items = vote_obj#dyn_enum_variable a in
- let b_items = vote_obj#dyn_enum_variable b in
- let removed, new_a_items =
- List.partition (fun (id, desc) -> id = selected_id) a_items in
- vote_obj#set_variable a (Dyn_enum_value new_a_items);
- vote_obj#set_variable b (Dyn_enum_value (b_items @ removed))
-@
-
-Function [[do_vote]] get the vote from the [[chosen]] selector and send
-it to the server. It invalidates the cache entry to take into account
-the new vote.
-
-<>=
-let do_vote vote_obj =
- let chosen_items = vote_obj#dyn_enum_variable "chosen" in
- let vote = List.map (fun (id, _) -> int_of_string id) chosen_items in
- let client, cookie = Session.connect_to_server vote_obj in
- try
- let cache = Cache.create ServerConnection.cache_filename client cookie in
- let q_id = int_of_string (vote_obj#string_variable
- "session.selected-question") in
- let ret = Demexp.V1.vote client (cookie, q_id, Array.of_list vote) in
- if ret <> rt_ok then
- raise (Misc.Display_error
- (Printf.sprintf
- "Vote as user failed on question #%d. Error: %s."
- q_id (Misc.string_of_return_code ret)));
- Cache.invalidate cache (Cache.Question q_id);
- Cache.save cache;
- ServerConnection.close_connection_to_server client cookie
- with Misc.Display_error msg ->
- Session.display_error_message vote_obj msg;
- ServerConnection.close_connection_to_server client cookie
-@
-
-Class [[vote]] handles event dispatch.
-
-<>=
-class vote universe name env =
- object (self)
- inherit dialog universe name env
-
- method prepare_page() =
- self#set_variable "lang"
- (String_value (self#string_variable "session.lang"));
- initialize_selectors self
-
- method handle() =
- match self#event with
- | Button("cancel_vote") ->
- Session.display_error_message self "";
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg)
-
- | Button("to_chosen") ->
- move_from_a_to_b self
- ~item:"selected-available" ~a:"available" ~b:"chosen"
-
- | Button("to_available") ->
- move_from_a_to_b self
- ~item:"selected-chosen" ~a:"chosen" ~b:"available"
-
- | Button("preferred") -> up_selected_item self
-
- | Button("disliked") -> down_selected_item self
-
- | Button("do_vote") ->
- do_vote self;
- let session = self#dialog_variable "session" in
- let new_dlg = !Registry.new_browse universe env session in
- raise(Change_dialog new_dlg)
-
- | _ ->
- ()
- end
-@
-
-At module start, we register creation function into the Registry.
-
-\nextchunklabel{code:voteWeb.ml:registering}
-<>=
-let _ =
- Registry.new_vote :=
- fun universe env session title ->
- let dlg = universe#create env "vote" in
- dlg#set_variable "session" (Dialog_value session);
- dlg#set_variable "title" title;
- dlg
-@
diff -r eb62ed1a54f1 -r a66569eaf6b1 web/voteWeb.ui.nw
--- a/web/voteWeb.ui.nw Sun Oct 01 17:31:20 2006 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-\chapter{\texttt{voteWeb} dialog}
-
-
-\section{\texttt{voteWeb} dialog definition}
-
-<>=
-
-
-@
-
-The variable [[title]] stores the description of question we are voting
-on. Its content is set at dialog creation (cf.
-\codechunkref{code:voteWeb.ml:registering}).
-
-The four remaining variables are used to handle the two selection boxes.
-[[available]] and [[chosen]] contain the available and chosen choices,
-[[selected-available]] and [[selected-chosen]] contain the user
-selection in those selectors.
-
-
-<>=
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-
-<>=
-
-
-
-
-
- Vote on question
- (#)
-
-
-
-
-
-
- Vote on question
- (#)
-
-
-
-
-
-
-
-
- Available responses:
-
-
-
-
-
- Your vote:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@
-