(*zap* this is the ocsigen Tutorial. It is mainly written in html. You can find a more readable version of comments on http://www.ocsigen.org *zap*) (*zap* ~/bin/caml2html -css -hc2 -oc tutorial.ml *zap*) (*html*
This is the tutorial for Ocsigen (development version). (Please report any error).
Warning: This tutorial assumes you know the Objective Caml language.
With Ocsigen, you don't write one file for each URL. You write a caml module (cmo or cma) for your whole website.
The XHTML.M module defines functions to construct html.
The Ocsigen module gives access to all the
functions you need to communicate with the Ocsigen server,
and Ocsigen.Xhtml defines all the functions you need
to create new services that use the XHTML.M module.
As the second one redefines some functions of the first one,
open them in this order:
Lwt is the cooperative thread library used by Ocsigen
(see later).
Pages are generated by OCaml functions called services.
To associate a service to an URL, use the function
register_new_service:
return is a function from Lwt.
Use it as this for now, and see later for more advanced use.
Now you can compile your file (here tutorial.ml) by doing:
ocamlc -I /path_to/ocsigen/ -c tutorial.ml
(Replace /path_to/ocsigen/
by the directory where ocsigen is installed).
Add the following lines to Ocsigen's config file
(usually /etc/ocsigen/ocsigen.conf):
<host> <site dir="examples"> <module file="/path_to/tutorial.cmo" /> </site> </host>
Then run ocsigen. You should see your page at url
http://your_server/examples/coucou.
See this example $a Tutorial.coucou sp <:xmllist< here >> ()$.
NB: See the default config file to see how to set the port on which your server is running, the user who runs it, the path of the log files, etc.
Here is a sample >>>>>>>>vérifier $a (static_dir sp) sp [pcdata "Makefile"] "Makefile"$ for your modules.
Typing of html is very strict and forces you to respect xhtml 1.1 standard (with some limitations). For example if you write:
(html (head (title (pcdata "")) []) (body [pcdata "Hallo"]))
You have the following error message:
This expression has type ([> `PCDATA ] as 'a) XHTML.M.elt but is here used with type ([< XHTML.M.block ] as 'b) XHTML.M.elt Type 'a is not compatible with type 'b = [< `Address | `Blockquote | `Del | `Div | `Dl | `Fieldset | `Form | `H1 | `H2 | `H3 | `H4 | `H5 | `H6 | `Hr | `Ins | `Noscript | `Ol | `P | `Pre | `Script | `Table | `Ul ]
'b is the list of tags allowed in a
block tag (here <body>), but PCDATA
(i.e. raw text) is not allowed here.
In XHTML, some tags cannot be empty. For example
<table> must contains at least one row.
To enforce this, the table function takes two parameters:
the first one is the first row, the second one is a list
containig all the other rows.
(same thing for <tr> <form>
<dl> <ol> <ul>
<dd> <select> ...)
If you prefer using a syntax closer to html, you can write:
*html*) let coucou1 = register_new_service ~url:["coucou1"] ~get_params:unit (fun _ () () -> return <<To compile this syntax, you need a camlp4 syntax extension:
ocamlc -I /path_to/ocsigen/ -pp "camlp4o /path_to/ocsigen/xhtmlsyntax.cma -loc loc" -c tutorial.ml
(Replace /path_to/ocsigen/
by the directory where ocsigen is installed).
See this example $a Tutorial.coucou1 sp <:xmllist< here >> ()$.
You can mix the two syntaxes (see later).
Warning: The two syntaxes are not equivalent for typing.
Using the syntax extension will do less verifications.
For example the following code is accepted but not valid
with respect to the xhtml's dtd (because <head>
must contain a title):
<< <html> <head></head> <body><h1>plop</h1></body> </html> >>
We recommand to use preferably
the functions from XHTML.M, as you will (almost)
always get valid xhtml.
Use the syntax extension for example to enclose already created pieces
of html, and verify the validity of your pages with the
$a (new_external_service ["http://validator.w3.org"] unit unit ())
sp <:xmllist< W3C validator >> ()$.
$a (new_external_service
["doc/0.6.0/Ocsigen.html"]
(* ["http://theorie.physik.uni-wuerzburg.de/~ohl/xhtml/"] *)
unit unit ())
sp <:xmllist< More info >> ()$
on XHTML.M.
$a xhtmlsyntax sp <:xmllist< More info >> ()$ on the syntax extension.
If OCamlDuce is installed on your system, it is now possible to use it instead of XHTML.M to typecheck your pages. You get a stronger typing and more flexibility (easier to use other XML types, easier to parse incoming XML data, etc.).
To use it, make sure that you have Ocsigen compiled with OCamlDuce
support. Then dynlink ocamlduce.cma and
ocsigenduce.cma from the configuration file
(after ocsigenmod.cma).
Here is an example:
open Ocsigen open Ocsigenduce.Xhtml open Lwt let s = register_new_service ~url:[""] ~get_params:unit (fun sp () () -> return {{ <html> [<head> [<title> ""] <body> [<h1> "This page has been type checked by OcamlDuce"]] }})
Services registered with register_new_service
are available for all users. We call them public services.
Page generation may have side-effects:
*html*) let compt = let next = let c = ref 0 in (fun () -> c := !c + 1; !c) in register_new_service ~url:["compt"] ~get_params:unit (fun _ () () -> return (html (head (title (pcdata "counter")) []) (body [p [pcdata (string_of_int (next ()))]]))) (*html*See this example $a Tutorial.compt sp <:xmllist< here >> ()$.
As usual in OCaml, you can forget labels when the application is total:
*html*) let hello = register_new_service ["dir";"hello"] (* the url dir/hello *) unit (fun _ () () -> return (html (head (title (pcdata "Hello")) []) (body [h1 [pcdata "Hello"]]))) (*html*See this example $a Tutorial.hello sp <:xmllist< here >> ()$.
The last example shows how to define the default page for
a directory. (Note that ["rep";""] is equivalent to
["rep";"index"].)
See $a Tutorial.default sp <:xmllist< default >> ()$.
The parameter labelled
~get_params
indicates the type of GET parameters for the page.
unit means that the page does not take any GET parameter.
Functions implementing services take three parameters. The first
one has type Ocsigen.server_params and
corresponds to server informations (user-agent, ip, current-url, etc.
- see later), the second one is for GET parameters
(that is, parameters in the URL) and the third one
for POST parameters (parameters in the body of the HTTP request).
Here is an example of a service with GET parameters:
*html*) let writeparams _ (i1, (i2, s1)) () = return (html (head (title (pcdata "")) []) (body [p [pcdata "You sent: "; strong [pcdata (string_of_int i1)]; pcdata ", "; strong [pcdata (string_of_int i2)]; pcdata " and "; strong [pcdata s1]]])) (*zap* you can register twice the same service, with different parameters names *zap*) let coucou_params = register_new_service ~url:["coucou"] ~get_params:(int "i" ** (int "ii" ** string "s")) writeparams (*zap* If you register twice exactly the same URL, the server won't start *zap*) (*html*Note that the URLs of coucou
and coucou_params
differ only by parameters. Url
$a Tutorial.coucou sp <:xmllist< http://your_server/examples/coucou >> ()$
will run the first one,
$a Tutorial.coucou_params sp <:xmllist< http://your_server/examples/coucou?i=42&ii=17&s=krokodile >> (42, (17, "krokodile")) $
will run the second one.
If entier is not an integer,
the server displays an error-message.
Warning:
The infix function ( ** ) is to be used to
construct pairs (not tuples).
The following examples shows how to create a service with "prefix" service (taking the end of the URL as a parameter, as wikis do very often) and how to get values from the http header:
*html*) let uaprefix = register_new_service ~url:["uaprefix"] ~get_params:(suffix (string "s")) ~prefix:true (fun sp (suff, s) () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "The suffix of the url is "; strong [pcdata suff]; pcdata ", your user-agent is "; strong [pcdata (get_user_agent sp)]; pcdata ", your IP is "; strong [pcdata (get_ip sp)]; pcdata " and s is "; strong [pcdata s]]]))) (*html*See $a Tutorial.uaprefix sp <:xmllist< uaprefix >> ("suffix", "gni")$, $a Tutorial.iprefix sp <:xmllist< iprefix >> ("mm/aa/gg", 22)$.
The following example shows how to use your own types:
*html*) type mysum = A | B let mysum_of_string = function "A" -> A | "B" -> B | _ -> raise (Failure "mysum_of_string") let string_of_mysum = function A -> "A" | B -> "B" let mytype = register_new_service ["mytype"] (user_type mysum_of_string string_of_mysum "valeur") (fun _ x () -> let v = string_of_mysum x in return (html (head (title (pcdata "")) []) (body [p [pcdata v]]))) (*html*See $a Tutorial.mytype sp <:xmllist< mytype >> Tutorial.A$.
You can catch errors using the optional parameter
error_handler:
error_handler takes as parameters the usual
sp, and a list of pairs (n,ex),
where n is the name of the wrong parameter, and
ex is the exception that has been raised while
parsing its value.
To create a link (anchor), use the function
Ocsigen.Xhtml.a
See $a Tutorial.links sp <:xmllist< links >> ()$.
Note that to create a (relative) link we need to know the current URL.
That's why the page has a sp parameter.
The link to Wikipedia shows how to define an external service (here it
uses a prefix URL).
You give page parameters as additional parameters to the
function Ocsigen.Xhtml.a.
If you want to create (mutually or not) recursive pages, first create the service, then register it in the table:
*html*) let linkrec = new_service ["linkrec"] unit () let _ = register_service linkrec (fun sp () () -> return (html (head (title (pcdata "")) []) (body [p [a linkrec sp [pcdata "click"] ()]]))) (*zap* If some url are not registered, the server will not start: let essai = new_url ~path:["essai"] ~server_params:no_server_param ~get_params:no_get_param () *zap*) (*html*See $a Tutorial.linkrec sp <:xmllist< linkrec >> ()$.
The server won't accept to start if there are unregistered services.
Ocsigen is using the concept of continuation. A continuation represents the future of a program (what to do after). When a user clicks on a link or a form, he chooses the future of the computation. When he uses the "back" button of the browser, he chooses to go back to an old continuation. Continuations for Web programming have been introduced by $a (new_external_service ["http://www-spi.lip6.fr/~queinnec/PDF/www.pdf"] unit unit ()) sp <:xmllist< Christian Queinnec >> ()$, and are a big step in the understanding of Web interaction.
Some programming languages (Scheme...) allow to manipulate
continuations using control operators (like
call/cc). The style of programming used by Ocsigen
is called Continuation Passing Style (CPS), and has the
advantage that it does not need control operators, and fits
very well Web programming.
In the following, we will see how to create dynamically new continuations dedicated to a particular user (auxiliary services in session table).
The function Ocsigen.get_form allows to create a form
that uses the GET method (parameters in the URL).
It works like Ocsigen.Xhtml.a but takes as parameter
a function that creates the form from parameters names.
Write an int: $int_input entier$
Write a string: $password_input chaine$
Write a string: $string_input chaine2$
$submit_input "Click"$
See the function $a Tutorial.form sp <:xmllist< form >> ()$ in action.
By default parameters of a web page are in the URL (GET parameters). A web page may expect parameters from the http header (POST parameters, that is, parameters which are not in the URL but in the body of the HTTP request). Use this if you don't want the user to be able to bookmark the URL with parameters, for example if you want to post some data that will change the state of the server (database, etc). When you register a service with POST parameters, you must register before a service (fallback) without these parameters (for example that will answer if the page is reloaded without the hidden parameters, or if it is bookmarked).
*html*) let no_post_param_service = register_new_service ~url:["post"] ~get_params:unit (fun _ () () -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Version of the page without POST parameters"]]))) let my_service_with_post_params = register_new_post_service ~fallback:no_post_param_service ~post_params:(string "value") (fun _ () value -> return (html (head (title (pcdata "")) []) (body [h1 [pcdata value]]))) (*html*Services may take both GET and POST parameters:
*html*) let get_no_post_param_service = register_new_service ~url:["post2"] ~get_params:(int "i") (fun _ i () -> return (html (head (title (pcdata "")) []) (body [p [pcdata "No POST parameter, i:"; em [pcdata (string_of_int i)]]]))) (*html* To create a POST form, use the post_form function,
possibly applied to GET parameters (if any).
Here form2 is a page containig a form
to the service post (using XHTML.M's functions)
and form3 (defined using the syntax extension)
contains a form to post2, with a GET parameter.
form4 is a form to an external page.
Write a string: $string_input chaine$
>>) 222) in return <<Write a string: $string_input chaine$
>>) 222) in return (html (head (title (pcdata "form")) []) (body [f]))) (*html*See the url
$a Tutorial.no_post_param_service sp <:xmllist< post without parameter >> ()$,
$a Tutorial.get_no_post_param_service sp <:xmllist< post2 without POST parameter >> 123$,
$a Tutorial.form2 sp <:xmllist< form2 >> ()$,
$a Tutorial.form3 sp <:xmllist< form3 >> ()$,
$a Tutorial.form4 sp <:xmllist< form4 >> ()$.
An auxiliary service is a service that uses the same URL as a public service, but distinguished only by a (hidden) special parameter, called state parameter. Use this if you want to particularize a link, but not the URL it points to. More precisely, auxiliary services are mainly used in two situations:
To create an auxiliary service, use
new_auxiliary_service and
new_post_auxiliary_service.
Like register_new_post_service,
they take a public service as parameter
(labelled fallback)
to be used as fallback when the user comes back without the state
parameter (for example if he put a bookmark on the page).
The auxiliary service has the same GET parameters as its fallback, but may have different POST parameters.
Try $a Tutorial.auxiliaryserv sp <:xmllist< auxiliaryserv >> ()$.
Note that with links or GET forms, it is not possible to send the state parameter in the body of the HTTP request. That's why we send the state parameter in the URL (using GET method). This may cause some problems because the state parameter will be saved in bookmarks. Note that the state parameter is ignored if the auxiliary service does not exist in the table of services.
Ocsigen allows to replace a public service by a service valid only for
one user.
To create a "session service", register the service in
a "session table" (valid only for one client)
instead of the global table. To do that,
use register_service_for_session.
Use this for example if you want two versions of each page,
one public, one for connected users.
To close a session, use
close_session.
Note that register_for_session
and close_session take sp as parameter
(because sp contains the session table).
The following is an example of web site that behaves differently when users are connected. We first define the main page, with a login form:
*html*) let public_session_without_post_params = new_service ~url:["session"] ~get_params:unit () let public_session_with_post_params = new_post_service ~fallback:public_session_without_post_params ~post_params:(string "login") let accueil sp () () = let f = post_form public_session_with_post_params sp (fun login -> [p [pcdata "login: "; string_input login]]) () in return (html (head (title (pcdata "")) []) (body [f])) let _ = register_service ~service:public_session_without_post_params accueil (*html*When the page is called with login parameters,
it runs the function launch_session
that replaces some services already defined by new ones:
See the $a Tutorial.public_session_without_post_params sp <:xmllist< result >> ()$.
You can register auxiliary services in session tables to create dynamically new services dedicated to an user. Here is an example of pages that add two integer. Once the first number is sent by the user, an auxiliary service is created and registered in the session table. This service takes the second number as parameter and displays the result of the sum with the first one. Try to duplicate the pages and/or to use the back button of your navigator to verify that it has the expected behaviour.
*html*) (*zap* ------------------------------------------------------------------ *) (* You can register auxiliary services in session tables. Use this if you want a link or a form which depends precisely on an instance of the web page, for example to buy something on an internet shop. UPDATE: Actually it is not a good example, because what we want in a shop is the same shoping basket for all pages. SEE calc example instead. *) let shop_without_post_params = new_service ~url:["shop"] ~get_params:unit () let shop_with_post_params = new_post_service ~fallback:shop_without_post_params ~post_params:(string "article") let write_shop shop url = (post_form shop url (fun article -> let sb = string_input article in <:xmllist<What do you want to buy? $sb$
>>) ()) let shop_public_main_page sp () () = let f = write_shop shop_with_post_params sp in return << $f$ >> let _ = register_service shop_without_post_params shop_public_main_page let write_shopping_basket shopping_basket = let rec aux = function [] -> [ <<You are going to pay: $list:write_shopping_basket shopping_basket$
>>); return <<$a auxiliary_pay sp <:xmllist< pay >> ()$
>> let _ = register_service ~service:shop_with_post_params (fun sp () article -> page_for_shopping_basket sp [article]) (* *zap*) (*zap* Queinnec example: *zap*) let calc = new_service ~url:["calc"] ~get_params:unit () let calc_post = new_post_service ~fallback:calc ~post_params:(int "i") let _ = let create_form is = (fun entier -> [p [pcdata (is^" + "); int_input entier; br (); submit_input "Sum"]]) in register_service ~service:calc_post (fun sp () i -> let is = string_of_int i in let calc_result = register_new_post_auxiliary_service_for_session sp ~fallback:calc ~post_params:(int "j") (fun sp () j -> let js = string_of_int j in let ijs = string_of_int (i+j) in return (html (head (title (pcdata "")) []) (body [p [pcdata (is^" + "^js^" = "^ijs)]]))) in let f = post_form calc_result sp (create_form is) () in return (html (head (title (pcdata "")) []) (body [f]))) (*html*See the $a Tutorial.calc sp <:xmllist< result >> ()$.
Services registered in session tables are called session services. Services registered in the global table are called public (or global).
Remember that the server will try to find the page, in this order:
All the services defined (but auxiliary services) must be registered in the global table. If not, the server will not start (with an error message in the logs). This is to ensure that all the URLs the user can bookmark will always give an answer, even if the session has expired.
Do not register public services after the initialisation phase.
Do not register twice the same service, and do not replace an URL by a directory (or vice versa). If this happens during the initialisation phase, the server won't start. If this happens after, it will be ignored (with a warning in the logs).
Actions are like services but they do not generate any page.
Use them to perform an effect on the server (connection/disconnection
of a user, etc.). By default, the current page is redisplayed
after the action.
For ex, when you have the same form (or link) on several pages
(for ex a connection form),
instead of making a version with post params of all these pages,
you can use only one action.
Create and register an action with
new_action,
register_action,
register_new_action,
register_action_for_session.
Make a form or a link to an action with
action_form or
action_a.
By default, they reload the current page again after having done the action.
But you can give the optional boolean parameter
~reload to action_form
or action_a to prevent reloading the page.
Here we rewrite the example session using actions.
See these $a Tutorial.action_session sp <:xmllist< pages >> ()$.
news_headers_list_box
that writes the beginning of messages, and message_box
that write a full message.
*html*)
(*zap* from ocsexample1 - attention la section Construction of pages a été simplifiée *zap*)
(*html*
(* All the services: *) let main_page = new_service ~url:[""] ~get_params:unit () let news_page = new_service ["msg"] (StringMessage.index "num") () (* Construction of pages *) let accueil sp () () = page sp [h1 [pcdata "Mon site"]; news_headers_list_box sp anonymoususer news_page] let print_news_page sp i () = page sp [h1 [pcdata "Info"]; message_box i anonymoususer] (* Services registration *) let _ = register_service ~service:main_page accueil let _ = register_service ~service:news_page print_news_page
Now the same with a login box on each page.
We now have two versions of each page: connected and not connected.
We need two actions (for connection and disconnection).
Suppose we have the functions login_box,
connected_box,
and connect.
(* All the urls: *) let main_page = new_service ~url:[""] ~get_params:unit () let news_page = new_service ["msg"] (StringMessage.index "num") () let connect_action = new_action ~post_params:(string "login" ** string "password" ) (* Construction of pages *) let accueil sp () () = page sp [h1 [pcdata "Mon site"]; p [pcdata "(user : toto and password : titi)"]; login_box sp connect_action; news_headers_list_box sp anonymoususer news_page] let print_news_page sp i () = page sp [h1 [pcdata "Info"; login_box sp connect_action; message_box i anonymoususer] let user_main_page user sp () () = page sp [h1 [pcdata "Mon site"]; text_box "Bonjour !"; connected_box sp user disconnect_action; news_headers_list_box sp user news_page] let user_news_page user sp i () = page sp [h1 [pcdata "Info"]; connected_box sp user disconnect_action; message_box i user] (* Services registration *) let _ = register_service ~service:main_page accueil let _ = register_service ~service:news_page print_news_page let launch_session user = register_service_for_session ~service:main_page (user_main_page user); register_service_for_session ~service:news_page (user_news_page user) let _ = register_action ~action:connect_action (fun h (login, password) -> launch_session (connect login password))
Remember that a Web site written with Ocsigen is an OCaml application.
This application must be able to handle several requests at the same
time, if one of the requests takes time. To make this possible, Ocsigen
is using cooperative threads,
implemented in monadic style
by Jérôme Vouillon (Lwt module), which make them really easy
to use.
With respect to preemptive threads, cooperative threads are not using a scheduler to distribute processor time between threads. Instead of this, each thread must tell the others that he wants to let them work. If a thread does not cooperate, the others will be blocked.
As it does not cooperate, the following page will stop the server for 5 seconds. No one will be able to do a request during this delay:
let looong = register_new_service ~url:["looong"] ~get_params:unit (fun sp () () -> Unix.sleep 5; return (html (head (title (pcdata "")) []) (body [h1 [pcdata "Ok now, you can read the page."]])))
To solve this problem, use a cooperative version of
sleep:
The >>= operator (from Lwt) is used to
specify a sequence of computations that depend one from another.
It is a kind of let binding.
e1 >>= (fun r -> return e2)
will try to evaluate e1, and once e1
is evaluated, it will give the result to the function given as second
parameter.
If the left handside (e1)
takes time (for example because it is waiting for a read on a socket),
the whole computation will be saved in a table and the program will
continue to the next instruction that does not depend on e1.
The computation will resume at a future
cooperation point, if it is ready to continue.
Instead of e1 >>= (fun r -> return e2),
you can write bind e1 (fun r -> return e2).
See $a Tutorial.looong sp <:xmllist< looong >> ()$.
Lwt.bind, (or >>=) has type
'a Lwt.t -> ('a -> 'b Lwt.t) -> 'b Lwt.t
Lwt.return has type
'a -> 'a Lwt.t
'a Lwt.t is the type of threads returning
a result of type 'a.
Cooperation points are inserted when you call cooperative functions
such as Lwt_unix.read or Lwt_unix.write.
You can add other cooperation points by calling
Lwt_unix.yield (). The thread will suspend itself,
let other threads run, and resume as soon as possible.
You must be careful when catching exception with Lwt.
If you use the try ... with construct for an expression
of type 'a Lwt.t, it may not work (as the computation
may happen later).
Remember the following: if e has type 'a Lwt.t
(where 'a is any type), do not write:
try e with ...
but write:
catch (fun () -> e) (function ... | exn -> fail exn)
Ocsigen implements a way to make a non cooperative computation be
executed automatically by a another preemptive thread (for example
a database request using a non-cooperative database library, such as
postgresql-ocaml or pgocaml). To do this,
use the detach function. For example:
See $a Tutorial.looong2 sp <:xmllist< looong2 >> ()$.
A pool of preemptive threads is waiting for such "detached functions". You can specify the number of threads in the pool in the configuration file.
Warning: Detached functions must be thread-safe! Be careful to concurrent access to data. Be sure to use mutexes for your own functions, and use only thread-safe libraries. The libraries from Ocsigen are NOT thread-safe for now. Let us know if you need them to be thread-safe.
If you want to use a function that takes time to execute but it not written in thread-safe way, consider rewriting it in cooperative manner, or delegate the work to another process.
Just add the following lines to your program:
let rec f () = print_endline "hello"; Lwt_unix.sleep 10. >>= f in f ();
let w = wait () in (w >>= (fun v -> return (print_endline v)); ... wakeup w "HELLO");
To each ocsigen module is associated a static directory where
you can put all the static (non generated) parts of your web-site
(for examples images).
See the default config file ocsigen.conf to
learn how to do that.
There is a predefined service called
static_dir to make links to
static files. It takes as string parameter the name of the file.
For example
Ocsigen.a
(static_dir sp)
sp
[pcdata "download image"]
"$str:small_logo$"
creates this link: $a (static_dir sp) sp [pcdata "download image"] small_logo$
To include an image, use simply the function XHTML.M.img:
img ~alt:"Ocsigen" ~src:(make_uri (static_dir sp) sp "ocsigen1024.jpg") ()
The function make_uri
creates the relative URL string from current URL (in sp)
(see above) to the URL of the image in the static directory
configured in the configuration file.
To simplify the creation of <link> tags
for CSS or <script> tags for Javascript,
use the following functions:
css_link (make_uri (static_dir sp) sp "style.css")
js_script (make_uri (static_dir sp) sp "funs.js")
To make a menu an your web page, you can use the function
Ocsigenboxes.menu.
First, define your menu like this:
let mymenu current sp = Ocsigenboxes.menu ~classe:["menuprincipal"] [ (home, <:xmllist< Home >>); (infos, <:xmllist< More infos >>); (tutorial, <:xmllist< Documentation >>) ] current sp
Here, home, infos,
and tutorial are your three pages (generated for example
by Ocsigen.new_url).
Then mymenu home sp will generate the following
code:
<ul class="menu menuprincipal"> <li class="current first">Home </li> <li><a href="infos">More infos</a> </li> <li class="last"><a href="tutorial">Documentation</a> </li> </ul>
Personalise it in your CSS style-sheet.
You need to coerce each of them. For example
[(home :> (('a,'b,Ocsigen.service_kind,'c,'d,'e) service))]
You sent: $list:ll$
>>) (* http://localhost:8080/coucou?a=2&a.entier[0]=6&a.entier[1]=7 *) (* Advanced forms *) (* Form with list: *) let create_listform f = (* Here, f.it is an iterator like List.map, but it must be applied to a function taking 2 arguments (and not 1 as in map), the first one being the name of the parameter. The last parameter of f.it is the code that must be appended at the end of the list created *) f.it (fun intname v -> <:xmllist<Write the value for $str:v$: $int_input intname$
>>) ["one";"two";"three";"four"] <:xmllist<$submit_input "Click"$
>> let listform = register_new_service ["listform"] unit (fun sp () () -> let f = get_form coucou_list sp create_listform in return <<Write the suffix: $string_input suff$
Write an int: $int_input i$
$submit_input "Click"$
$pcdata (if case then "checked" else "not checked")$
>>) let create_form_bool casename = <:xmllist<check? $bool_checkbox casename$
$submit_input "Click"$
A simple page: $a coucou sp <:xmllist< coucou >> ()$
A page with a counter: $a compt sp <:xmllist< compt >> ()$
A page in a directory:
$a hello sp <:xmllist< dir/hello >> ()$
Default page of a directory:
$a default sp <:xmllist< rep/ >> ()$
A page with GET parameters:
$a coucou_params sp <:xmllist< coucou with params >> (45,(22,"krokodile"))$ (what if the first parameter is not an integer?)
A page with "prefix" URL that knows the IP and user-agent of the client:
$a uaprefix sp <:xmllist< uaprefix >> ("suf", "toto")$
A page with "prefix" URL and GET parameters :
$a iprefix sp <:xmllist< iprefix >> ("popo", 333)$
A page with a parameter of user-defined type :
$a mytype sp <:xmllist< mytype >> A$
A page with links: $a links sp <:xmllist< links >> ()$
A page with a link towards itself:
$a linkrec sp <:xmllist< linkrec >> ()$
The $a main sp <:xmllist< default page >> ()$
of this directory (myself)
A page with a GET form that leads to the "coucou" page with parameters:
$a form sp <:xmllist< form >> ()$
A POST form towards the "post" page:
$a form2 sp <:xmllist< form2 >> ()$
The "post" page, when it does not receive parameters:
$a no_post_param_service sp <:xmllist< post wihtout post_params >> ()$
A POST form towards a service with GET parameters:
$a form3 sp <:xmllist< form3 >> ()$
A POST form towards an external page:
$a form4 sp <:xmllist< form4 >> ()$
Auxiliary services:
$a auxiliaryserv sp <:xmllist< auxiliary >> ()$ (problem with bookmarked GET parameters ..s...)
A session based on cookies:
$a public_session_without_post_params sp <:xmllist< session >> ()$
A session based on cookies, implemented with actions:
$a action_session sp <:xmllist< actions >> ()$
Auxuiliary services in the session table:
$a calc sp <:xmllist< calc >> ()$
A page that is very slow, implemented in cooperative way:
$a looong sp <:xmllist< looong >> ()$
A page that is very slow, using preemptive threads:
$a looong sp <:xmllist< looong2 >> ()$
Catching errors:
$a catch sp <:xmllist< catch >> 22$ (change the value in the URL)