forked from franzinc/aserve
-
Notifications
You must be signed in to change notification settings - Fork 0
/
webactions.html
1375 lines (1375 loc) · 68.1 KB
/
webactions.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Allegro Webactions</title>
</head>
<body>
<h1 style="text-align: center;">Allegro Webactions<br>
<small><small>v1.7</small></small><br>
</h1>
<h4><strong><small>copyright(c) 2003-2006. Franz Inc</small></strong></h4>
<h1>Table of Contents</h1>
<a href="#introduction">Introduction<br>
</a><a href="#Loading_Webactions">Loading Webactions</a><br>
<a href="#Common_Lisp_Server_Pages_">Common Lisp Server Pages</a><br>
<a href="#Processing_a_clp_file">Processing a clp file</a><br>
<a href="#clp_elements_with_bodies">clp elements with bodies</a><br>
<a href="#parsing_clp_files">parsing clp files</a><br>
<a href="#clp_tag_and_function_names">clp tag and function names</a><br>
<a href="#The_Lisp_side_of_clp">The Lisp side of clp</a><br>
<a href="#Webactions">Webactions</a><br>
<a href="#Webaction_project">Webaction project</a><br>
<a href="#Classes_used_in_webactions">classes used in
Webactions<br>
</a> <a href="#storing_values">Storing Values</a><br>
<a href="#Lisp_functions_for_webactions">Lisp functions
for Webactions</a><br>
<a href="#Library_of_clp_functions"> Library of clp
functions</a><br>
<br>
<h1><a name="introduction"></a>Introduction</h1>
<p> 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 <b>publish</b>
function which allows the programmer to completely generate the
response
to an http request. Also the <b>publish-multi</b> 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 <span
style="font-weight: bold;">C</span>ommon <span
style="font-weight: bold;">L</span>isp Server <span
style="font-weight: bold;">P</span>ages (or clp) and Webactions.<br>
</p>
<p>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.<br>
</p>
<p>Please see the document <a href="using-webactions.html"><span
style="font-style: italic;">Using
Webactions</span></a> for further background information on webactions.<br>
</p>
<h2><br>
</h2>
<h1><a name="Loading_Webactions"></a>Loading Webactions</h1>
In order to load in Webactions you should <span
style="font-family: courier new,courier,monospace;">(require
:webactions)</span>.
This will load in AllegroServe if it isn't already loaded. The
functions in Webactions are exported from the <span
style="font-weight: bold;">net.aserve</span> package.<br>
<br>
<h1><a name="Common_Lisp_Server_Pages_"></a>Common Lisp Server Pages <br>
</h1>
<p> Common Lisp Server Pages is modeled after similar designs for
other
languages: Java Server Pages, Active Server Pages, Personal Home Pages
(PHP) and others. </p>
<p> 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. <br>
</p>
<p>The key features of clp are: </p>
<ul>
<li>A clp file can be edited with an html editor (such as FrontPage
or Mozilla's built-in html editor named Composer). </li>
<li>clp files can contain javascript or other scripting code. </li>
<li>a clp file can contain illegal html. The clp file processor does
not attempt to verify that the file contains valid html syntax.
While we don't recommend that you use illegal html we do know
that
some people do and that won't prevent you from using clp files. </li>
<li>A clp file does not contain any lisp code. A clp file may
contain tags that cause Lisp code to be run to render html, but it does
not contain Lisp code. The html designers editing the clp files
will likely not know Lisp. The presence of code in the file that they
don't understand would confuse them and they may inadvertently modify
the Lisp code and cause it to fail. </li>
<li>The clp file processor, when used with the webactions framework,
supports the notion of sessions which are automatically maintained by
cookies (preferably) or by url rewriting.<br>
</li>
</ul>
<h2><a name="Processing_a_clp_file"></a>Processing a clp file</h2>
This is a sample clp file:<br>
<br>
<pre><html><br><body><br>You are using this browser: <http_header-value name="User-Agent"/><br><br><br>How do you like it?<br></body><br></html></pre>
You'll notice two unusual things in the file. One is the tag <span
style="font-weight: bold;">http_header-value</span>, which you've
never seen before. The other is that tag ends in "<span
style="font-weight: bold;">/></span>", which is the xml and xhtml
way of specifying that this html element has no body. It's
equivalent to writing the pure html:<br>
<div style="text-align: center;">
<pre><http_header-value name="User-Agent"></http_header-value></pre>
</div>
The tag <span style="font-weight: bold;">http_header-value</span>
names
a <span style="font-style: italic;">clp function<span
style="font-weight: bold;">. </span></span>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.<br>
<br>
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.<br>
<br>
A user accessing this page would see something like this in his browser:<br>
<br>
<pre>You are using this browser: Firefox 1.5<br>How do you like it?</pre>
<br>
<br>
<h2><a name="clp_elements_with_bodies"></a>clp elements with bodies</h2>
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<br>
<br>
<pre><clp_ifdef name="winner" session> You are the Winner! </clp_ifdef><br><clp_ifndef name="winner" session > Sorry, you lost </clp_ifndef></pre>
<br>
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.<br>
<br>
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.<br>
<br>
<h2><a name="parsing_clp_files"></a>parsing clp files</h2>
<br>
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.<br>
<br>
<h2><a name="clp_tag_and_function_names"></a>clp tag and function names</h2>
<br>
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 <span
style="font-weight: bold;">http_header-value</span> is the <span
style="font-weight: bold;">header-value</span> function in the <span
style="font-weight: bold;">http</span> 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. <br>
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).<br>
<br>
<br>
<h2><a name="The_Lisp_side_of_clp"></a>The Lisp side of clp</h2>
<br>
<span
style="font-family: courier new,courier,monospace; font-weight: bold;">(def-clp-function
name (req ent args body) &rest function-body)</span><br>
<br>
This macro defines a clp function with the given name. <span
style="font-weight: bold;">Name</span> 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.
<span style="font-weight: bold;">req</span> and <span
style="font-weight: bold;">ent</span> 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<br>
<pre><mod_sample name="foo" value="24"></pre>
the value of <span style="font-weight: bold;">args</span> would be<br>
<pre>(("name" . "foo") ("value" . "24"))</pre>
The fourth argument, <span style="font-weight: bold;">body</span>, 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 <span
style="font-weight: bold;">body. </span>It should do one of two
things with <span style="font-weight: bold;">body</span>. 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 <span style="font-weight: bold;">body</span> to be sent back
as part of the response by calling <span style="font-weight: bold;">(emit-clp-entity
req ent body)</span>.<br>
<br>
The <span style="font-weight: bold;">function-body</span> 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 <span style="font-weight: bold;">html</span> macro.
The value returned by this function is unimportant.<br>
<br>
<br>
<br>
<span
style="font-weight: bold; font-family: courier new,courier,monospace;">(emit-clp-entity
req ent body)</span><br
style="font-weight: bold; font-family: courier new,courier,monospace;">
<br>
This is used inside a clp function to cause the contents of <span
style="font-weight: bold;">body</span> 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.<br>
<br>
<br>
<br>
<h1><a name="Webactions"></a>Webactions</h1>
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<br>
<ol>
<li>sessions - the web site must follow each user as they move around
the site, perhaps picking up products and placing them in their virtual
shopping cart. This is accomplished by associating a session
object in the web server with each distinct user.</li>
<li>database backed - the dynamic web site is often just a user
interface to a database. Shopping sites display products
found in the store's database and add orders to the store's database.
It's useful to separate out the code that operates on the
database
and the code that displays the current state of the database.</li>
<li>complex linking - there are many paths through an online store as
goods are selected and finally an order is made. Keeping track
of
these links is very hard as the site gets large. You need some way of
keeping track of the layout of the whole site.<br>
</li>
</ol>
The webactions framework supports a programming methodology called
Model View Controller (or MVC). In a dynamic web application the
pieces are these:<br>
<ol>
<li>Model - this is the code that implements the data objects
being manipulated by users of the web site. In an online store
the
model includes the notions of a shopping cart and orders and so on.
This is the code that must be written specifically for the
objects being modeled.</li>
<li>View - the html pages that show the user a view of the model.
These pages are usually clp pages in the webaction framework.
There will be some clp functions to implement the dynamic parts
of the pages.</li>
<li>Controller - the code that accepts user input and passes control
to the model to process the input and finally selects a view to
send back to the user. This code is supplied with the webaction
framework.<br>
</li>
</ol>
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.<br>
<br>
<br>
<h2><a name="Webaction_project"></a> Webaction project</h2>
<br>
A Webaction based web site is defined by a call to the
webaction-project macro. <br>
<br>
<span
style="font-weight: bold; font-family: courier new,courier,monospace;">
(webaction-project name &key project-prefix clp-suffixes map
destination index <br>
sessions session-lifetime reap-interval reap-hook-function server
authorizer<br>
host access-file clp-content-type default-actions external-format)</span><br>
<br>
<span style="font-weight: bold;">webaction-project</span> creates
project data structures and executes calls to <span
style="font-weight: bold;">publish</span> to make the project exist on
the server. <br>
<br>
The arguments are<br>
<span style="font-weight: bold;">name</span> - a string naming the
project. The name is used to ensure that if <span
style="font-weight: bold;">webaction-project</span> 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.<br>
<span style="font-weight: bold;">project-prefix</span> - 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.<br>
<span style="font-weight: bold;">clp-suffixes</span> - 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.<br>
<span style="font-weight: bold;">map</span> - an assoc list of the
symbolic page name and how that page is generated. This will be
described in detail below.<br>
<span style="font-weight: bold;">destination</span> - 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 "/".<br>
<span style="font-weight: bold;">index</span> - 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"<br>
<span style="font-weight: bold;">sessions</span> - 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.<br>
<span style="font-weight: bold;">session-lifetime</span> - 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).<br>
<span style="font-weight: bold;">reap-interval</span> - 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 webactions projects share the same session reaping process).
The default value is 300 seconds (i.e. five minutes).<br>
<span style="font-weight: bold;">reap-hook-function</span> - 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 <span style="font-style: italic;">not</span>
be reaped and instead the session will be treated as if it was just
referenced 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.<br>
<span style="font-weight: bold;">server</span> - this is the wserver
object into which this project will be published. It defaults to
the value of *wserver*.<br>
<span style="font-weight: bold;">authorizer</span> - an authorizer
object that will be associated with all entities created in this
project.<br>
<span style="font-weight: bold;">host</span> - the value for the host
argument to the publish function used to establish the project<br>
<span style="font-weight: bold;">access-file</span> - 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.<br>
<span style="font-weight: bold;">clp-content-type</span> - a string
holding the content type for all clp files published in this
project. The default is "text/html".<br>
<span style="font-weight: bold;">default-actions</span> -
the actions to invoke if there are no map-entry matches in the
map. The default is <span style="font-weight: bold;">nil </span>
(meaning no default actions).<br>
<span style="font-weight: bold;">external-format</span> - the external
format used when sending data back to the browser. The
default is the value of *default-aserve-external-format*<br>
<br>
<br>
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 <span
style="font-style: italic;">managed<span style="font-weight: bold;"> </span></span>pages
from <span style="font-style: italic;">unmanaged</span> 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.<br>
<br>
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 <span style="font-weight: bold;">locate-action-path</span>
function to
compute the url's for href and action attributes in the page it
generates. Otherwise it's an unmanaged page.<br>
<br>
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.<br>
<br>
Every managed page is named by a distinct Lisp string we call the <span
style="font-style: italic;">symbolic name<span
style="font-weight: bold;"> </span></span>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 <span style="font-weight: bold;">map</span> argument to
webaction-project
associates the symbolic name of a page with the steps needed to render
the page.<br>
<br>
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. <br>
<br>
<h3>Action and View functions</h3>
When processing a request user-written functions may be called to
either specify the control flow through the webaction project or to
display html. We refer to the former as <span
style="font-style: italic;">action functions</span> and to the latter
as <span style="font-style: italic;">view functions</span>.
We make the distinction between action and view to make the roles of
these functions clear with in the MVC framework. Webactions does
not know nor need to know the role of the user defined functions it
calls. Webactions simply calls the function and responds in
a certain way to the value returned by the user defined functions.<br>
<br>
Both action and view functions are passed the standard two
arguments: a request object and an entity object.<br>
At the time these functions are called it's <span
style="font-weight: bold;">not</span> possible to immediately emit
html as with-http-response and with-http-body have not been
done. <br>
The action function will return one of three values<br>
<ul>
<li>a string giving the symbolic page name to jump to.</li>
<li>a string naming a file name whose contents are to be sent to the
browser</li>
<li>the symbol :continue meaning process the next item in the map
clause</li>
</ul>
<br>
A view function will call with-http-response and with-http-body and
will send a response back to the web server (possibly using the html
macro).<br>
A view function returns <span style="font-weight: bold;">nil </span>indicating
that a response has been sent back to the http client and no further
processing should be done by webactions for this request.<br>
<br>
<h3>Maps</h3>
The <span style="font-weight: bold;">map</span> 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. <br>
<br>
<br>
The value of the map argument is a list of map entries:<br>
<br>
<font size="+1"><span style="font-family: monospace;">( map-entry1
map-entry2 map-entry3 ....)</span></font><br>
<br>
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:<br>
<br>
<font size="+1"><span style="font-family: monospace;">("symbolic page
name" item* [ (:flag1 value1 ... ...)
] )</span></font><br>
<br>
In our psuedo-bnf, the '*' means zero or more occurrences. The
square brackets mean
zero or one occurrence.<br>
<br>
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.<br>
<br>
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<span
style="font-weight: bold;"> :continue</span> meaning go on to the next
item in the map entry.<br>
A view function will generate a response to the http request (which
usually means sending html to the browser). A view function
returns <span style="font-weight: bold;">nil</span> meaning that this
request has been processed and there's nothing more for webactions to
do.<br>
<br>
These are some typical map entries<br>
<br>
<font size="+1"><span style="font-family: monospace;">("home"
"homepage.clp")</span><br style="font-family: monospace;">
<span style="font-family: monospace;">("login" action-do-login "home")</span><br
style="font-family: monospace;">
<span style="font-family: monospace;">("letter"
action-check-login get-named-letter (:prefix t))</span></font><br>
<br>
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:<br>
<br>
<big><span style="font-family: monospace;">("myletter.clp"
(:content-type "text/plain"))</span><br style="font-family: monospace;">
</big><br>
<br>
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.<br>
<h3><br>
</h3>
<h3>Simple Maps</h3>
<br>
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.<br>
<br>
Here's an example of a map argument value<br>
<br>
<pre>(("home" "home.clp")<br> ("signin" "signin.clp")<br> ("newuser" action-new-user)<br> ("existinguser" action-existing-user)<br> ("failedlogin" user-failed-login)<br> ("storefront" "storefront.clp"))<br></pre>
<br>
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.<br>
<br>
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.<br>
<br>
In a clp file relative urls that follow "href=" or "action=" are
considered to be the symbolic names of pages. Thus you can write<br>
<pre><a href="home"> blah blah </a></pre>
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
symbolic name "home".<br>
It's still possible to reference pages outside the project using
absolute paths in url, such as<br>
<br>
<small><span style="font-family: courier new,courier,monospace;">
<a href="/othersite/index.html>check this out too</a>
or <a href="http://www.cnn.com"> read the latest
news</a></span></small><br>
<br>
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. <br>
<br>
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.<br>
<br>
<pre>(webaction-project "sample" :project-prefix "/mysamp/" :destination "/usr/proj/sample/" :index "main"<br> :map '(("main" "main.clp")<br> ("signin" action-sign-in)<br> ("choice" "choice.clp")<br> ("vote" action-vote)<br> ("thanks" "thanks.clp")))</pre>
<br>
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:<br>
<pre><html><br><body><br><h1>Sign In Please</h1><br><br><span
style="font-weight: bold;"><mysamp_showerrors/></span><br><br><form <span
style="font-weight: bold;">action="signin"</span> method="POST"><br>name: <input type="text" <br> name="name" value="<span
style="font-weight: bold;"><clp_value name=name session/></span>"><br><br>password <input type="password" name="password"><br><br><input type="submit"><br></form><br><br></body><br></html><br></pre>
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 <span
style="font-weight: bold;">action=</span> 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 <span style="font-weight: bold;">clp_value</span>
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.<br>
<br>
The clp function mysamp_showerrors is this:<br>
<pre>(def-clp-function mysamp_showerrors (req ent args body)<br> (declare (ignore ent args body))<br> (let ((error (request-variable-value req "error")))<br> (if* error<br> then (html :br<br> ((:font :color "red")<br> (:princ-safe error))<br> :br :br))))<br><br> <br></pre>
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 user problems found by the code that checks
the name and password for validity. We'll next see how that
"error" variable is set.<br>
<br>
<br>
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 <span
style="font-weight: bold;">action-sign-in</span> to be called.
Here is that function:<br>
<br>
<pre>(defun action-sign-in (req ent)<br> (declare (ignore ent))<br> (let ((name (request-query-value "name" req))<br> (password (request-query-value "password" req))<br> (websession (websession-from-req req))<br> )<br> (setf (websession-variable websession "name") name)<br> (if* (equal password<br> (cdr (assoc name '(("joe" . "eoj")<br> ("fred" . "derf"))<br> :test #'equal)))<br> then ; success!<br> (setf (websession-variable websession "signed-in") t)<br> "choice" ; show choice<br> else ; failure<br> (setf (request-variable-value req "error")<br> "name and password are invalid")<br> <br> "main" ; go back and try again<br> )))<br></pre>
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.<br>
<br>
This is choice.clp:<br>
<br>
<pre><html><br><body><br><h1>Vote</h1><br>Ok <span
style="font-weight: bold;"><clp_value name="name" session/></span>, what do you like?<br><br><br><form <span
style="font-weight: bold;">action="vote"</span> method="POST"><br>favorite food: <input type="text" name="food"><br><br></form><br></body><br></html><br></pre>
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 symbolic page
"vote" is invoked. From the map we see that that causes the
function action-vote to be invoked.<br>
<br>
<pre>(defvar *votes* nil)<br><br>(defun action-vote (req ent)<br> (declare (ignore ent))<br> (let* ((food (request-query-value "food" req))<br> (websession (websession-from-req req))<br> (name (websession-variable websession "name")))<br> (if* (websession-variable websession "signed-in")<br> then (let ((ent (assoc name *votes* :test #'equal)))<br> (if* ent<br> then (setf (cdr ent) food)<br> else (push (cons name food) *votes*)))<br> "thanks"<br> else ; not signed in, can't vote<br> "main")))<br></pre>
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.<br>
<br>
If the vote was recorded this function returns "thanks" which the map
causes thanks.clp to be returned:<br>
<br>
<pre><html><br><body><br><h2>Thanks for voting</h2><br></body><br></html<br><br></pre>
<h3><br>
</h3>
<h3>Extended Maps</h3>
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 <span
style="font-style: italic;">extended maps<span
style="font-weight: bold;">. </span></span>Let's
look at the map from the project mentioned above:<br>
<pre> (("main" "main.clp")<br> ("signin" action-sign-in)<br> ("choice" "choice.clp")<br> ("vote" action-vote)<br> ("thanks" "thanks.clp")))</pre>
In this map we would like the symbolic pages "choice", "vote" and
"thanks" to be reachable <span style="font-style: italic;">only</span>
if the current session has a logged in
user.<br>
We can accomplish this with<br>
<pre> (("main" "main.clp")<br> ("signin" action-sign-in)<br> ("choice" action-check-login choice.clp")<br> ("vote" action-check-login action-vote)<br> ("thanks" action-check-login "thanks.clp")))</pre>
Where we define action-check-login as:<br>
<pre>(defun action-check-login (req ent)<br> (declare (ignore ent))<br> (if* (websession-variable (websession-from-req req) "signed-in")<br> then ; logged in<br> :continue<br> else ; not logged in<br> "main"))<br></pre>
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:<br>
<ol>
<li>if the first item is a string then consider it to be a symbolic
page name and start processing from the top.<br>
</li>
<li>if
the first item is a symbol then run the function value of that
symbol. The return value from that function will be either<br>
<ul>
<li>string - consider this to be a symbolic page name to render
and start the processing from the beginning with this symbolic name</li>
<li>nil - assume that the function called has already done the
html response for this request so do nothing further.</li>
<li>:continue - if this particular keyword is returned then pop
the list of items specified to process this symbolic page and go back
to step 1.</li>
</ul>
</li>
</ol>
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.<br>
<br>
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).<br>
<br>
<h3>Prefix Maps</h3>
It is possible to have one map entry specify how to handle a whole set
of symbolic page names. The syntax is this<br>
<br>
<span style="font-family: monospace;">("name" action-or-view ....
(:prefix t))</span><br>
<br>
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. .<br>
<br>
The meaning of <span style="font-weight: bold;">:prefix t</span> 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:<br>
<br>
<span style="font-family: monospace;">http://localhost/foo/name</span><br
style="font-family: monospace;">
<span style="font-family: monospace;">http://www.foo.com/foo/named</span><br
style="font-family: monospace;">
<span style="font-family: monospace;">http://www.bar.com/foo/namexxx/yyyy/zzz</span><br>
<br>
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.<br>
<br>
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. <br>
<br>
To continue our example above if there were also a map entry<br>
<br>
<span style="font-family: monospace;">("name" action-do-name)</span><br
style="font-family: monospace;">
<br>
then this url<br>
<br>
<span style="font-family: monospace;">http://localhost/foo/name</span><br>
<br>
would be handled by the single symbolic name map entry rather than the
prefix entry.<br>
<br>
Also if there were a file "name.clp" in the directory of files for this
project then the url<br>
<br>
<span style="font-family: monospace;">http://localhost/foo/name.clp</span><br>
<br>
would return this file rather than invoke the "name" as a prefix map
entry.<br>
<br>
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<br>
<br>
<span style="font-family: monospace;">("" handle-undefined-page
(:prefix t))<br>
<br>
</span>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.<br>
<br>
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.<br>
<br>
Thus if this url<br>
<br>
<span style="font-family: monospace;">http://www.foo.com/myproj/sendfile/mypic.jpg</span><br>
<br>
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<br>
<br>
<span style="font-family: monospace;">
("sendfile/" return-filecontents (:prefix t))</span><br>
<br>
<br>
<h3>Redirection Maps</h3>
<br>
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. <br>
<br>
This is counterintuitive and user-unfriendly behavior on IE's part but
still you must handle it in some way.<br>
<br>
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.<br>
<br>
You can specify this behavior in a map entry in this way<br>
<br>
("getdata" action-do-getdata "showresult.clp" (:redirect t))<br>
<br>
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".<br>
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. <span style="font-style: italic;">This
feature is still under development -- we may change this in the future
to allow query and request variables to survive the redirect.</span> <br>
<br>
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.<br>
<br>
<h3>Content-Type maps</h3>
<br>
You can specify the content type of a file return by Webactions by
adding a map entry of the following form<br>
<br>
<big><span style="font-family: monospace;">("myfile.clp" (:content-type
"text/plain"))</span><br style="font-family: monospace;">
</big><br>
By default clp files are given the "text/html"
content-type. You can override this for all clp files using
the <span style="font-weight: bold;">clp-content-type</span> argument
to <span style="font-weight: bold;">webaction-project</span>.
Specifying the content type in a map entry overrides all other
specifications. <br>
<br>
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.<br>
<br>
<h2><a name="Classes_used_in_webactions"></a>Classes used in webactions</h2>
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.<br>
<br>
<span style="font-weight: bold;">clp-entity</span> - 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.<br>
<br>
<span style="font-weight: bold;">webaction-entity</span> - 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.<br>
<br>
<br>
<span style="font-weight: bold;">webaction</span> - 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.<br>
<br>
<span style="font-weight: bold;">websession-master</span> - 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.<br>
<br>
<span style="font-weight: bold;">websession</span> - 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.<br>
<br>
<br>
<h2><a name="storing_values"></a>Storing Values</h2>
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).<br>
<br>
<table style="text-align: left; width: 955px; height: 153px;" border="1"
cellpadding="5" cellspacing="2">
<tbody>
<tr>
<th style="vertical-align: top; text-align: center; width: 33%;"><span
style="font-weight: bold;">Session</span><br>
</th>
<th style="vertical-align: top; text-align: center; width: 33%;">Request
Variable<br>
</th>
<th style="vertical-align: top; text-align: center;">Request Query<br>
</th>
</tr>
<tr>
<td style="vertical-align: top;"><span
style="font-family: monospace;">(websession-variable session "name")</span><br>