|
Allegro CL version 8.1 |
This is the Webactions documentation as of the current release of Allegro CL. It is provided for convenience but the Webactions documentation is regularly updated. We recommend that users use the latest version, available at http://opensource.franz.com/aserve/aserve-dist/webactions/doc/webactions.html. That version is always the most up-to-date. AllegroServe, including Webactions, is available from http://opensource.franz.com/. The Webactions tutorial is using-webactions.html. See also the AllegroServe documentation aserve/aserve.html.
Introduction
Most of the web sites people visit again and again are dynamic. These pages range from
pages containing static data and a dynamically selected banner ad to pages full of
personalized content, such as a search page returned by Google. AllegroServe offers
two different ways of creating dynamic pages. The most general is the publish
function which allows the programmer to completely generate the response to an http
request. Also the publish-multi function creates pages that are a mixture of
static data and data generated by a lisp function. It's possible to build a large dynamic
web site using just these functions but it does require that you have a lot of programming
talent at your disposal. It's far easier to find html designers than Lisp programmers and
so we designed a method of building large dynamic web sites in AllegroServe that uses
mainly html designers with some support from Lisp programmers. This is the purpose of Common Lisp
Server Pages (or clp) and Webactions.
Webactions is the framework used to describe the whole site and to control access to
pages in the site. clp files provide a way of mixing static and dynamic html
that works well with the webaction framework. We'll describe clp pages first and
then the webaction framework.
Please see the document Using Webactions for further background information
on webactions.
In order to load in Webactions you should (require :webactions).
This will load in AllegroServe if it isn't already loaded. The functions in
Webactions are exported from the net.aserve
package.
Common Lisp Server Pages is modeled after similar designs for other languages: Java Server Pages, Active Server Pages, Personal Home Pages (PHP) and others.
A Common Lisp Server Page file looks just like an html file. In fact the format
is the same as html. We've extended html by allowing new tags which cause extended
functionality to be invoked.
The key features of clp are:
This is a sample clp file:
<html> <body> You are using this browser: <http_header-value name="User-Agent"/> <br> How do you like it? </body> </html>
You'll notice two unusual things in the file. One is the tag http_header-value, which you've never seen before.
The other is that tag ends in "/>",
which is the xml and xhtml way of specifying that this html element has no body.
It's equivalent to writing the pure html:
<http_header-value name="User-Agent"></http_header-value>
The tag http_header-value names a clp function. A
clp function is written in Lisp and is run during the time this page is sent back to the
browser. Thus when this page is retrieved by a browser, the result is that
all the text in the file up to the http_header-value tag would be sent directly to the
browser. Next the http_header-value function would be run and it will emit html
which will be sent back to the browser. Finally the text after the http_header-value
tag will be sent to the browser.
The clp function http_header-value is supplied with AllegroServe. As its name
suggests it retrieves the value from an http header in the request and then emits the
value as html. In our sample file we're retrieving the value of the User-Agent
header which describes which http client is making the request.
A user accessing this page would see something like this in his browser:
You are using this browser: Firefox 1.5 How do you like it?
The example above uses a clp element with no body. This is what you'll typically
find in use. However there are situations where you want to give the element a
body, the most notable one being when you want a clp function to determine which parts of
a clp file are sent back to the browser. For example
<clp_ifdef name="winner" session> You are the Winner! </clp_ifdef> <clp_ifndef name="winner" session > Sorry, you lost </clp_ifndef>
This clp file fragment checks to see if the session state has a variable named winner
defined and if it does it includes the appropriate text. If winner is not defined
then it includes the loser message. The clp_ifdef and clp_ifndef functions
are supplied with AllegroServe.
One problem with using conditional inclusion of text is that an html editor's view of the
clp file will include both versions of the text since it ignores unknown tags like
clp_ifdef. Thus you'll have to balance the power of using conditional text
inclusion against the problems it creates in that your html editor can't display the
final product.
Before a clp file can be used to generate a response to an http request it must be parsed.
The parsing function is very simple and is in fact more like pattern matching
than traditional parsing. The parser simply locates all calls to clp functions in
the file and separates them from the text that is sent back verbatim as part of the
response. A clp file is parsed when it's first referenced in an http request
and the results of the parse are cached The clp file is not parsed again
unless the file is updated on disk.
The clp parser has to be able to distinguish clp tags from html tags and from tags
that are neither clp tags nor valid html tags. The parser may encounter a clp tag
referencing a clp function that's not yet defined (in Lisp we don't require that a
function be defined before we'll recognize a call to that function). The problem
then is determining whether a given tag in the file is a call to a clp function or a name
that could be used as a clp function in the future. The strategy employed is the
following: A clp function name has two parts: a module name and the name of the
function within the module. These names are separated by an underscore. Thus http_header-value is the header-value function in the http module. The clp parser only has to look
closely at tags with an underscore in them. In order to tell if such a tag is a clp
function name the parser looks only at the module name. If the module name is of a
currently known module then that name is considered to be a clp function name.
Thus before you start running your web site you should define at least one clp function in
each module you intend to use. You can define the other functions later (and
you likely will if you are building your site and testing it incrementally).
(def-clp-function
name (req ent args body) &rest function-body)
This macro defines a clp function with the given name. Name can be a string or a symbol (in which case the
downcased version of the symbol-name is used). The name must include an underscore
between two of the characters (this separates the module name from the
function-with-the-module name). When called the function takes four arguments
and we've shown above the names we suggest be used for those four arguments. req and ent are
the familiar request and entity values passed to all http response functions.
Args is an alist of attribute names and values found in the start tag that invoked
this function. For example given the tag
<mod_sample name="foo" value="24">
the value of args would be
(("name" . "foo") ("value" . "24"))
The fourth argument, body, is passed the parsed
version of the of the body of the element, that is the text that appears between the start
and end tags in the clp file. A clp function is not supposed to examine the
value of body. It should do one of two
things with body. It can just ignore the
value in which case the text and calls to clp functions between the start and end
tags are ignored and not sent as part of the response to the web browser.
Alternatively it can cause the body to be sent
back as part of the response by calling (emit-clp-entity
req ent body).
The function-body is the code that's run when the
clp function is called. It should emit html just like the code in a normal http
response function. Often this is done with the html
macro. The value returned by this function is unimportant.
(emit-clp-entity
req ent body)
This is used inside a clp function to cause the contents of body to be processed as a parsed clp file.
This will result in strings being sent to the html stream (and thus back to the
browser) and it will cause clp functions to be run. The only place this
function should be used is inside a clp function.
Webactions is a framework for building dynamic web sites. Dynamic webs sites are
difficult to build as they require more than html can provide, such as
The webactions framework supports a programming methodology called Model View
Controller (or MVC). In a dynamic web application the pieces are these:
A designer of a webactions-based web site will write or reuse Lisp code to implement
the Model. For the View he'll write clp functions in Lisp to support the clp
pages written in html. He'll use the the Controller code supplied with
Webactions.
A Webaction based web site is defined by a call to the webaction-project macro.
(webaction-project
name &key project-prefix clp-suffixes map destination index
sessions
session-lifetime reap-interval reap-hook-function server authorizer
host access-file clp-content-type external-format)
webaction-project creates project data structures
and executes calls to publish to make the project
exist on the server.
The arguments are
name - a string naming the project. The
name is used to ensure that if webaction-project
is called again with the same name, the project by that name is redefined rather
than creating a new project. Also the name is used as the name of the cookie that's
used to track sessions in this project. Since this is a cookie name too the name
should use just alphabetic and numeric characters.
project-prefix - a string which specifies
the prefix of all urls in the project. The prefix should be a string
beginning and ending in the forward-slash character, such as
"/myproject/" . It's legal to use the one character string
"/". What this means is that all url's beginning with this prefix
are assumed to be part of this project and are treated specially.
clp-suffixes - a list of strings naming the
suffixes of files, which if found inside the project, are assumed to be clp files.
By default the value of clp-suffixes is a list of the string "clp". You
may wish to add "htm" or "html" to this list if you want those files
to be parsed as clp files when referenced inside this project.
map - an assoc list of the symbolic page name and
how that page is generated. This will be described in detail below.
destination - the location on the filesystem where
the files that make up this project are to be found. This can be the empty
string "" or the name of a directory on the machine. If you name a
directory be sure that the name ends in "/".
index - the symbolic page name of the main
page of this project (also known as the index or root page). webaction-project will
redirect to this page if a request comes in for a url which is just the project-prefix.
If the project-prefix is "/foo/" and the index is
"home" then a request for "/foo" or "/foo/" will be
redirected to "/foo/home"
sessions - if true (which is the default value)
then track access to the pages of the project so that all accesses from a given web
browser are assigned a unique websession object.
session-lifetime - if the session hasn't been
accessed in this many seconds, remove the session object from the internal table of
sessions, allowing it to be garbage collected. The default session lifetime is 18000
seconds (i.e. five hours).
reap-interval - the number of seconds between
checks to see if any sessions have expired and should be removed. Specifying this
variable sets a global variable (since all webaction projects share the same session
reaping process). The default value is 300 seconds (i.e. five minutes).
reap-hook-function - a function of one argument, a
websession object. This is called when a session is about to be destroyed due to no
reference to this session for the session-lifetime. If this function returns a
non-nil value then the session will not be reaped
and instead the session will be treated as if it was just reference and thus it will be
kept alive for another session-lifetime. One common use for this function is
to deallocate objects associated with the session that won't be recovered by the Lisp
garbage collector when the session is garbage collected.
server - this is the wserver object into which
this project will be published. It defaults to the value of *wserver*.
authorizer - an authorizer object that will be
associated with all entities created in this project.
host - the value for the host argument to the
publish function used to establish the project
access-file - the filename of the access file(s)
found in the project's directory. See the documentation for AllegroServe's
publish-directory for details on access-files.
clp-content-type - a string holding the content
type for all clp files published in this project. The default is
"text/html".
external-format - the external format used when
sending data back to the browser. The default is the value of
*default-aserve-external-format*
A web site is a collection of pages and images, each page with references to
images and with links to other pages. The pages may be static or may be generated
by programs. The links and image references may be within the web site or to
other web sites. In a webaction web site we further distinguish managed pages
from unmanaged pages. Session information
is maintained as long as the user visits a sequence of managed pages. If a user
visits an unmanaged page and then follows a link to a managed page, the user may end up in
a new session. Thus when designing a webaction web site it's important to
keep the user on managed pages until the session information is no longer important.
A clp file is a managed page. An html file that's not a clp file is an unmanaged
page. A page generated by a Lisp function is a managed page if it uses the locate-action-path function to compute the url's for
href and action attributes in the page it generates. Otherwise it's an unmanaged
page.
The reason that there's a distinction between managed and unmanaged pages is that if the
browser doesn't accept cookies then the only way that session information can be
maintained is through url rewriting, and only managed pages have the support for url
rewriting.
Every managed page is named by a distinct Lisp string we call the symbolic name of
the page. All references to pages in clp files and in clp functions is to the
symbolic name of the page rather than the url of the page. The map argument to webaction-project associates the
symbolic name of a page with the steps needed to render the page.
A symbolic page name can refer to a managed page, an unmanaged page or an action.
An action is a Lisp function which performs some operation and returns a symbolic page
name. Earlier we talked about the Model View Controller methodology. In MVC
terms, the action functions are operations on the Model. An action function
should not generate any html, that's the responsibility of the View code.
The map argument specifies what steps are taken
to process a request for a symbolic page. In this section we'll describe
the complete syntax of the map value. If you're just learning Webactions you'll
probably want to skip to the Simple Maps section and then come back to this section
when you're done reading the rest of the document.
The value of the map argument is a list of map entries:
( map-entry1 map-entry2
map-entry3 ....)
Each map entry is a list beginning with a symbolic page name and followed by items
which specify which actions to run, which view to render and a set of flag values that
apply to this entry. In pseudo-bnf the form of a map entry is:
("symbolic page
name" item* [ (:flag1 value1 ... ...) ] )
In our psuedo-bnf, the '*' means zero or more occurrences. The square brackets mean
zero or one occurrence.
An item can be a symbol or a string. If an item is a symbol it names a lisp
function which is either an action function or a view function. If the item is a
string then it either names a file to send back to the browser or a symbolic page name.
An action function may modify the Model behind the website and then it returns
either a string naming a symbolic page name, as a string naming a file to send back
to the browser, or the keyword symbol :continue
meaning go on to the next item in the map entry.
A view function will generate a response to the http request (which usually means sending
html to the browser). A view function returns nil
meaning that this request has been processed and there's nothing more for webactions to
do.
These are some typical map entries
("home"
"homepage.clp")
("login" action-do-login "home")
("letter" action-check-login
get-named-letter (:prefix t))
As noted above a string can represent either a file to send back to the browser or a
symbolic page name. If a given string is equal to the first value in a
map entry then it is symblic page name, otherwise it's a file to send back to the
browser. There is one exception: if a map entry doesn't have any items in it then
the string that's the first value of that map entry is the name of a file to
return. This special form is used when you wish to add flags to the processing
of a file. This is an example:
("myletter.clp" (:content-type
"text/plain"))
You need not understand the complete map entry syntax in order to use
Webactions. In fact you can build useful web sites using only a fraction
of the features of map entries. Next we'll gradually introduce you to maps and
show when you would want to use the advanced features.
In its simplest form, the map is a list of two element lists. Each two
element list begins with the symbolic name of the page. This is followed by either
the location of the page or a function name. A function name can either name a
function that will generate a managed or unmanaged page, or it will be the name of
an action function. Usually a function name will not name a page
generating function, instead clp files will be used for each page, however in some
situations it may prove useful to have totally dynamic pages generated by a lisp
function.
Here's an example of a map argument value
(("home" "home.clp") ("signin" "signin.clp") ("newuser" action-new-user) ("existinguser" action-existing-user) ("failedlogin" user-failed-login) ("storefront" "storefront.clp"))
In the example we have three symbolic pages that refer to clp files. These are thus
managed pages. Two symbolic pages refer to functions whose names suggest they are
action functions. One symbolic page refers to a function user-failed-login which is
a function to dynamically create a page describing the failed login attempt.
You can't distinguish an action function from a dynamic page generation function based on
what's in the map argument. If you're wise you'll use a naming convention such as
used above to make the distinction clear. The way that webactions determines
which is which when running the function is that an action function will return a string
and a dynamic html generation function will return nil.
In a clp file relative urls that follow "href=" or "action=" are
considered to be the logical names of pages. Thus you can write
<a href="home"> blah blah </a>
and that href will be transformed into the appropriate url such that the link will be
directed to the page associated with the page with the logical name "home".
It's still possible to reference pages outside the project using absolute paths in url,
such as
<a
href="/othersite/index.html>check this out too</a> or <a
href="http://www.cnn.com"> read the latest news</a>
Now we have the background to describe exactly what webaction-project does.
webaction-project does a publish-prefix for the path that's the value of
project-prefix. This means that any url in that url-space will be processed as a
reference to an object in this project.
Let's create an example of a simple site that asks you to sign in and once that's
successful it lets you vote for your favorite food. The sign in process ensures
that at most one vote is recorded for each user.
(webaction-project "sample" :project-prefix "/mysamp/" :destination "/usr/proj/sample/" :index "main" :map '(("main" "main.clp") ("signin" action-sign-in) ("choice" "choice.clp") ("vote" action-vote) ("thanks" "thanks.clp")))
The first page of the site has the logical name "main" and that causes the file
"main.clp" to be returned to the browser. Here is main.clp:
<html> <body> <h1>Sign In Please</h1> <mysamp_showerrors/> <form action="signin" method="POST"> name: <input type="text" name="name" value="<clp_value name=name session/>"><br> password <input type="password" name="password"><br> <input type="submit"> </form> </body> </html>
There are just a few items to note in this page, and we've shown them in bold.
The first is the element <mysamp_showerrors/>. This will cause a clp function
to be invoked, which we'll describe below. The next item to note is that the value
of action= is a symbolic page named
"signin". The clp processor will transform "signin" to
the appropriate value that depends on whether your browser is accepting cookies.
The final item to note is that the default value of the text field for "name" is
given by a clp_value tag. This clp_value
retrieves the value of the session variable "name", if it has a value.
We'll see later how this session variable is set. The idea is that if the user typed
in his name but failed to type in the correct password, we'll prompt him again for his
password and will fill in the name field for him. Note how the clp_value element
can be placed inside an html string value. This is because the clp file parser
doesn't parse the html, it just looks for clp element tags.
The clp function mysamp_showerrors is this:
(def-clp-function mysamp_showerrors (req ent args body) (declare (ignore ent args body)) (let ((error (request-variable-value req "error"))) (if* error then (html :br ((:font :color "red") (:princ-safe error)) :br :br))))
This function looks on the request object for a variable named "error" and if
found prints that value of that variable in red. This is used to communicate to the
users problems found by the code that checks the name and password for validity.
We'll next see how that "error" variable is set.
When the user enters his name and password and clicks on the submit button control is
passed to the logical page "signin". Looking at the map above you'll see
that this causes the function action-sign-in
to be called. Here is that function:
(defun action-sign-in (req ent) (declare (ignore ent)) (let ((name (request-query-value "name" req)) (password (request-query-value "password" req)) (websession (websession-from-req req)) ) (setf (websession-variable websession "name") name) (if* (equal password (cdr (assoc name '(("joe" . "eoj") ("fred" . "derf")) :test #'equal))) then ; success! (setf (websession-variable websession "signed-in") t) "choice" ; show choice else ; failure (setf (request-variable-value req "error") "name and password are invalid") "main" ; go back and try again )))
This function retrieves the values of the "name" and "password"
values from the set of form values. It retrieves the current session object which is
stored on the request object by the webaction framework code. Next it stores the
name given as the value of session variable "name". This means that
the clp_value form shown in main.clp will be able to retrieve it should we get to that
page again. Next it checks if the password is valid. We have a very
simple test in our example, a real web site would use some kind of database to store the
password information. If the password matches we set the session variable
"signed-in" to true and return the string "choice". The
webaction framework then consults the map for a page named "choice" and finds
that choice.clp should be returned. If the name and password are not valid then
action-sign-in returns the string "main" causing main.clp to be returned
and the user prompted again for a name and password. Before returning
"main" this function sets the request variable "error" to a string to
print when main.clp is sent back to the browser.
This is choice.clp:
<html> <body> <h1>Vote</h1> Ok <clp_value name="name" session/>, what do you like? <br> <form action="vote" method="POST"> favorite food: <input type="text" name="food"><br> </form> </body> </html>
Here we ask the user for their favorite food. We personalize the page by
displaying the user's name on the page using clp_value. When the user types in the
food and presses enter the logical page "vote" is invoked. From the map we
see that that causes the function action-vote to be invoked.
(defvar *votes* nil) (defun action-vote (req ent) (declare (ignore ent)) (let* ((food (request-query-value "food" req)) (websession (websession-from-req req)) (name (websession-variable websession "name"))) (if* (websession-variable websession "signed-in") then (let ((ent (assoc name *votes* :test #'equal))) (if* ent then (setf (cdr ent) food) else (push (cons name food) *votes*))) "thanks" else ; not signed in, can't vote "main")))
The vote action checks to see if the user is logged in in which case it stores the last
value the user voted for in an assoc list in the variable *votes*. The logged
in test is important since a user may try to bypass the signing-in process by just
directing his web browser to /mysamp/vote which would run this action function as
well.
If the vote was recorded this function returns "thanks" which the map causes
thanks.clp to be returned:
<html> <body> <h2>Thanks for voting</h2> </body> </html
When designing a web application you usually want to force the user to login first and
then you open up the site to him. When a user enters the correct name and
password you modify the current session object to include an object that identifies the
user so that subsequent visits to this site during the same session will be associated
with the user who just logged in. What if a new user doesn't come to the 'front
door' of the web site but instead jumps right into the middle of it? How can
you protect the site so that a non-logged-in user is forced to start at the login page
before visiting any other page of the site? The answer is using extended maps.
Let's look at the map from the project mentioned above:
(("main" "main.clp") ("signin" action-sign-in) ("choice" "choice.clp") ("vote" action-vote) ("thanks" "thanks.clp")))
In this map we would like the symbolic pages "choice", "vote" and
"thanks" to be reachable only if
the current session has a logged in user.
We can accomplish this with
(("main" "main.clp") ("signin" action-sign-in) ("choice" action-check-login choice.clp") ("vote" action-check-login action-vote) ("thanks" action-check-login "thanks.clp")))
Where we define action-check-login as:
(defun action-check-login (req ent) (declare (ignore ent)) (if* (websession-variable (websession-from-req req) "signed-in") then ; logged in :continue else ; not logged in "main"))
As you can see, a symbolic page name has a sequence of function names or strings
associated with it. The rule for processing a symbolic page name is to
process the list of items after the symbolic page name in this way:
If the map doesn't contain an entry for the symbolic page name then assume that the
symbolic page name is the actual name of a page on the site and return that.
In our example the function action-check-login tests to see if the user is logged in and
if he is returns :continue so that the next item in the list will be used to process the
symbolic page request. If the user is not logged in then the string
"main" is returned which causes the login page to be displayed (and subsequent
items in the list to handle the symbolic page request are ignored).
It is possible to have one map entry specify how to handle a whole set of symbolic page
names. The syntax is this
("name" action-or-view .... (:prefix t))
What distinguishes this syntax is that the last item is a list. That list contain a
sequence of flag names and values, in a property list format. .
The meaning of :prefix t is that this map entry
applies to all symbolic pages name beginning with the string
"name". For example if the project has a prefix of
"/foo/" then the following urls will be handled by this map entry:
http://localhost/foo/name
http://www.foo.com/foo/named
http://www.bar.com/foo/namexxx/yyyy/zzz
Prefix map entries are the last ones considered when Webactions looks for a map entry to
handle a symbolic page name. Webactions first looks for a specific symbolic page
name entry. Then Webactions sees if the symbolic page name names an
actual file in the project directory. And finally if those first two searches fail
to find a map entry, Webactions looks for a prefix entry.
The precedence of the prefix entries search is "last mentioned first".
That is the last prefix map value in the map argument to webaction-project is tested
first, and then the second to last, and so on.
To continue our example above if there were also a map entry
("name" action-do-name)
then this url
http://localhost/foo/name
would be handled by the single symbolic name map entry rather than the prefix entry.
Also if there were a file "name.clp" in the directory of files for this project
then the url
http://localhost/foo/name.clp
would return this file rather than invoke the "name" as a prefix map entry.
We'll show two important uses for prefix map entries. The first is that you can
catch references to non-existent symbolic page names and return a nicer error message than
the standard one AllegroServe returns. The map entry
("" handle-undefined-page (:prefix t))
will catch all symbolic page references that don't have a handler. You'll
want to list this entry before any other prefix entry in the map since you want this entry
to be checked last.
The second important use for prefix map entries arises when you wish to send a file to the
browser and you would like to suggest the filename for the file. Browers usually use
the name that appears after the last "/" in a url as the default name of the
file to store.
Thus if this url
http://www.foo.com/myproj/sendfile/mypic.jpg
resulted in an "image/jpeg" being returned then the browser would prompt to
store this as "mypic.jpg". In a webaction project
(with the project prefix of "/myproj/") you would have a map entry such as
this
("sendfile/"
return-filecontents (:prefix t))
Suppose you click on the submit button in a form, and the method for that form is
"POST". The webserver will respond with another page.
Now you click on a link on that page to go to a new page. Now suppose you click on
the Back button on the web browser, which should take you to the page that was the result
of submitting the form. The browser should just show you the previous
page from its cache. Most browsers will do this except that Internet Explorer
will often tell you that the page has expired and it refuses to show you the page.
This is counterintuitive and user-unfriendly behavior on IE's part but still you must
handle it in some way.
One way to handle this is that whenever a POST is done the webserver processes the posted
data and then returns a Redirect response to the browser which then does a GET of the page
that's the target of the redirect. Thus the user ends up looking at a page
that was fetched with a GET, and thus IE will have no problem returning to this page if
the Back button is clicked.
You can specify this behavior in a map entry in this way
("getdata" action-do-getdata "showresult.clp" (:redirect t))
The redirect flag says that rather than simply return the contents of showresult.clp to
the browser, Webactions will return a Redirect response to the browser which will then
fetch "showresult.clp".
The consequence of this redirect is that clp functions invoked by showresult.clp will not
have access to the query values from the first request to "getdata" and they
will not have access to the request-variables that may have been set by
action-do-getdata. This feature is still
under development -- we may change this in the future to allow query and request variables
to survive the redirect.
If you're concerned about making your site work in IE you'll likely want to do this
redirect for all symbolic pages that are reached by a POST to a form.
You can specify the content type of a file return by Webactions by adding a map entry of
the following form
("myfile.clp" (:content-type
"text/plain"))
By default clp files are given the "text/html" content-type. You can
override this for all clp files using the clp-content-type
argument to webaction-project.
Specifying the content type in a map entry overrides all other specifications.
Specifying the content-type in this way only works in this case where there are no actions
or views following the name of the file.
These classes are used by the webaction framework. Except as noted the classes
should be considered opaque. Use only the documented functions to operate on
instances of these classes.
clp-entity - when clp files are
"discovered" in a webaction by being referenced from a request url,
a clp-entity instance is created to describe the page. This entity is then
published so that the discovery process doesn't have to happen again. When the
clp-entity is created a pointer to the webaction object is placed in the entity so that
clp functions run during the processing of the clp file can find the webaction project
they are running inside.
webaction-entity - there is one entity of this
class for each webaction project. This entity is published to capture all urls that
begin with the project prefix (if no other more specific entity captures them). The
webaction entity holds a pointer to a webaction object.
webaction - An instance of this class contains the
information on a webaction project. It contains the information passed as arguments
to webaction-project as well as a websession-master instance if sessions are to be
maintained.
websession-master - An instance of this
object is associated with a webaction object if that project wants session support.
The webaction-master object contains the information for creating session ids and
for automatically deleting unused sessions. It also contains a map from cookie value
to websession object.
websession - An instance of this class denotes a
single session for a site denoted by a webaction. The websession contains a
"last used" time and will be automatically removed after a certain amount of
time without being used.
A webaction project consists of cooperating independent objects: action
functions, clp functions and clp files. These objects often have a need to pass data
from one to another. Webactions offers three places to store data but of
course application specific code can also use other data repositories (such as
databases).
Session |
Request Variable |
Request Query |
---|---|---|
(websession-variable
session "name") |
(request-variable-value
req "name") |
(request-query-value
"name" req) |
<clp_value
name="name" session/> |
<clp_value
name="name" request/> |
<clp_value
name="name" query/> |
Lasts for the lifetime of a session. You can obtain the session object using (websession-from-req req) |
Lasts for the lifetime of a request |
Lasts for the lifetime of a request. Initialized from the query string of the url of the request and from form data. |
The values can be set using setf of the
corresponding accessor.
These functions are useful inside clp functions and webactions action functions.
(locate-action-path
webaction action-name websession)
Returns a url path to the action named action-name
in the given webaction and websession. This is used in a clp function when
you wish to provide a value for a href or action attribute that should be directed to an action in
the current project.
(html "go to " ((:a href (locate-action-path wa "signup" session)) "here") " to sign up.")
You can find the current webaction object using webaction-from-ent
and the current session object using websession-from-req.
(webaction-from-ent
ent)
Returns the webaction object associated with this entity. This function will return
the webaction object for clp-entity and webaction-entity objects. This
function is rarely used since there is little that can be done by user code with a
webaction object.
(websession-from-req req)
Returns the websession object associated with this request (which is an http-request
object).
(websession-data websession)
The following clp functions are supplied with AllegroServe.
<clp_include
name="filename"/>
insert the contents of the given file at this point. A relative filename will be
relative to the location of the file containing this clp_include element.
<clp_base/>
This emits a <base href="xxx"> tag into the html stream. xxx
is the location of the clp page being emitted. Due to the automatic internal
redirecting done by the webaction processor the initial url can be much different than the
url which describes the page being sent to the browser. If the page contains
relative references to images then the base tag will allow the browser to turn those
relative references into the correct absolute references.
This tag must be within the <head> .... </head> part of the page.
<clp_value
name="xxx" [safe] [query | request | session]/>
Retrieve the value of the variable named xxx from the location specified and emit it to
the html stream. If location is query then the value is retrieved from the query
string of a GET or the body and query string of the POST. If the
location is request then the value is retrieved from the request variables. If the
location is session then the values are retrieved from the session variables.
If safe is given then the value will be
printed in such a way to escape any characters that would be interpreted as html
(e.g. as (html (:princ-safe xxx)) would
print the value).
Example:
The value of query variable foo is <clp_value name="foo" safe query/>.
<clp_set
name="xxx" value="yyy" [query | request | session]/>
Sets the value of variable xxx to yyy in the given location. If you want to
pass values from one clp function to another storing them in the request location is best
as the value will be isolated to this one http request.
<clp_ifgt
name="xxx" value="yyy" [query | request | session]>body</clp_ifgt>
If the value of the variable xxx found at the given location is greater than the
value yyy then the body will be emitted to the html stream. The value yyy
should be an integer value.
<clp_iflt
name="xxx" value="yyy" [query | request | session]>body</clp_iflt>
If the value of the variable xxx found at the given location is less than the value
yyy then the body will be emitted to the html stream. The value yyy should be
an integer value.
<clp_ifeq name="xxx" value="yyy" [query | request | session]>body</clp_ifeq>
If the value of the variable xxx found at the given location is not eql to the
value yyy then the body will be emitted to the html stream. The value yyy
should be an integer value.
<clp_ifneq
name="xxx" value="yyy" [query | request | session]>body</clp_ifneq>
If the value of the variable xxx found at the given location is eql to the
value yyy then the body will be emitted to the html stream. The value yyy
should be an integer value.
<clp_ifdef name="xxx" [query | request | session]>body</clp_ifdef>
If the value of the variable xxx found at the given location is not nil then
the body will be emitted to the html stream.
<clp_ifndef
name="xxx" [query | request | session]>body</clp_ifdef>
If the value of the variable xxx found at the given location is nil then the
body will be emitted to the html stream. If the variable xxx has never been set
then this is the same as it having a value of nil.
<clp_ifequal
name="xxx" value="yyy" [query | request | session]>body</clp_ifequal>
If the value of the variable xxx found at the given location is equal to the value
yyy then the body will be emitted to the html stream. The value yyy can be
any value, but is likely to be a string.
<clp_ifnequal
name="xxx" value="yyy" [query | request | session]>body</clp_ifnequal>
If the value of the variable xxx found at the given location is not equal to the
value yyy then the body will be emitted to the html stream. The value yyy can
be any value, but is likely to be a string.
<clp_options
name="xxx" [query | request | session]>
"opt1"
"opt2" ..."optn"</clp_options>
This function helps build a dynamically defaulted option list for the html <select> element. Inside a
<select> element are a sequence of <option> elements with the default value
denoted <option selected>. When generating an option list the default value
may not be known when the author writes the page. The default can be based on some
other value entered in the session. Thus this form allows the default to be
computed where the default value is found at runtime from the value of the variable whose
name is given as the value of the name attribute.
In the form shown above the variable name is xxx.
Between <clp_options> and </clp_options> are a sequence of lisp strings.
When this is processed the lisp strings are read with the lisp reader in order to
create a list of option values.
If a name= attribute isn't present then the first lisp string is made the default
Example:
<select name="color"> <clp_options name="defcolor" session> "blue" "green" "yellow" "red" "purple" "gold" </clp_options> </select>
<clp_select args>
body</clp_select>You are using browser <http_header-value name="User-Agent"/>.
<wa_link name="xxx" extra="yyy"/>
<wa_link href="main" extra="?user=joe&password=123"/>
<wa_showerrors
name="xxx" [query | request | session] [clear]/>
If the the variable named xxx in the location
specified (query, request
or session) has a value then display that value in
red in the html being returned as part of the request. If clear is given then set the value of variable xxx to nil. This is commonly used to
display error messages on a page, such as when a form wasn't filled out correctly and
you're redisplaying the page and asking the user to try again. The default location
is request.
|
Allegro CL version 8.1 |