summaryrefslogtreecommitdiff
path: root/docs/users_guide_src/webware.tex
diff options
context:
space:
mode:
Diffstat (limited to 'docs/users_guide_src/webware.tex')
-rwxr-xr-xdocs/users_guide_src/webware.tex575
1 files changed, 575 insertions, 0 deletions
diff --git a/docs/users_guide_src/webware.tex b/docs/users_guide_src/webware.tex
new file mode 100755
index 0000000..a5702d8
--- /dev/null
+++ b/docs/users_guide_src/webware.tex
@@ -0,0 +1,575 @@
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\section{Using Cheetah with Webware}
+\label{webware}
+
+{\bf Webware for Python} is a 'Python-Powered Internet Platform' that runs
+servlets in a manner similar to Java servlets. {\bf WebKit} is the name of
+Webware's application server. For more details, please visit
+\url{http://webware.sourceforge.net/}.
+
+All comments below refer to the official version of Webware, the DamnSimple!
+offshoot at ?, and the now-abandoned WebwareExperimental implementation at
+\url{http://sourceforge.net/projects/expwebware/}, except where noted. All the
+implementations are 95\% identical to the servlet writer: their differences lie
+in their internal structure and configuration files. One difference is that
+the executable you run to launch standard Webware is called \code{AppServer},
+whereas in WebwareExperimental it's called \code{webkit}. But to servlets
+they're both "WebKit, Webware's application server", so it's one half dozen to
+the other. In this document, we generally use the term {\bf WebKit} to refer
+to the currently-running application server.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Installing Cheetah on a Webware system}
+\label{webware.installing}
+
+Install Cheetah after you have installed Webware, following the instructions in
+chapter \ref{gettingStarted}.
+
+The standard Cheetah test suite ('cheetah test') does not test Webware features.
+We plan to build a test suite that can run as a Webware servlet, containing
+Webware-specific tests, but that has not been built yet. In the meantime, you
+can make a simple template containing something like "This is a very small
+template.", compile it, put the *.py template module in a servlet
+directory, and see if Webware serves it up OK.
+
+{\em You must not have a Webware context called "Cheetah".} If you do, Webware
+will mistake that directory for the Cheetah module directory, and all
+template-servlets will bomb out with a "ImportError: no module named Template".
+(This applies only to the standard Webware; WebwareExperimental does not have
+contexts.)
+
+If Webware complains that it cannot find your servlet, make sure
+'.tmpl' is listed in 'ExtensionsToIgnore' in your 'Application.config' file.
+
+% @@MO: Should explain extension cascading and how without it, standard
+% Webware pretends a file doesn't exist if it finds two or more servable files
+% that match the URL.
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Containment vs Inheritance}
+\label{webware.background}
+
+Because Cheetah's core is flexible, there are many ways to integrate it with
+Webware servlets. There are two broad strategies: the {\bf Inheritance
+approach} and the {\bf Containment approach}. The difference is
+that in the Inheritance approach, your template object \code{\em is} the
+servlet, whereas in the Containment approach, the servlet is not a template but
+merely {\em uses} template(s) for portion(s) of its work.
+
+The Inheritance approach is recommended for new sites because it's simpler, and
+because it scales well for large sites with a
+site->section->subsection->servlet hierarchy. The Containment approach is
+better for existing servlets that you don't want to restructure. For instance,
+you can use the Containment approach to embed a discussion-forum table at the
+bottom of a web page.
+
+However, most people who use Cheetah extensively seem
+to prefer the Inheritance approach because even the most analytical servlet
+needs to produce {\em some} output, and it has to fit the site's look and feel
+{\em anyway}, so you may as well use a template-servlet as the place to put the
+output. Especially since it's so easy to add a template-servlet to a site once
+the framework is established. So we recommend you at least evaluate the
+effort that would be required to convert your site framework to template
+superclasses as described below, vs the greater flexibility and manageability
+it might give the site over the long term. You don't necessarily have to
+convert all your existing servlets right away: just build common site templates
+that are visually and behaviorally compatible with your specification, and use
+them for new servlets. Existing servlets can be converted later, if at all.
+
+Edmund Liam is preparing a section on a hybrid approach, in which the
+servlet is not a template, but still calls template(s) in an inheritance
+chain to produce the output. The advantage of this approach is that you
+aren't dealing with \code{Template} methods and Webware methods in the
+same object.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{The Containment Approach}
+\label{webware.containment}
+
+In the Containment approach, your servlet is not a template. Instead, it
+it makes its own arrangements to create and use template object(s) for whatever
+it needs. The servlet must explicitly call the template objects'
+\code{.respond()} (or \code{.\_\_str\_\_()}) method each time it needs to fill
+the template. This does not present the output to the user; it merely gives
+the output to the servlet. The servlet then calls its
+\code{\#self.response().write()} method to send the output to the user.
+
+The developer has several choices for managing her templates. She can store the
+template definition in a string, file or database and call
+\code{Cheetah.Template.Template} manually on it. Or she can put the
+template definition in a *.tmpl file and use {\bf cheetah compile} (section
+\ref{howWorks.cheetah-compile}) to convert it to a Python class in a *.py
+module, and then import it into her servlet.
+
+Because template objects are not thread safe, you should not store one
+in a module variable and allow multiple servlets to fill it simultaneously.
+Instead, each servlet should instantiate its own template object. Template
+{\em classes}, however, are thread safe, since they don't change once created.
+So it's safe to store a template class in a module global variable.
+
+% @@MO: Example of containment.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{The Inheritance Approach}
+\label{webware.inheritance}
+
+In the Inheritance approach, your template object doubles as as Webware
+servlet, thus these are sometimes called {\bf template-servlets}. {\bf cheetah
+compile} (section \ref{howWorks.cheetah-compile}) automatically creates modules
+containing valid Webware servlets. A servlet is a subclass of Webware's
+\code{WebKit.HTTPServlet} class, contained in a module with the same name as
+the servlet. WebKit uses the request URL to find the module, and then
+instantiates the servlet/template. The servlet must have a \code{.respond()}
+method (or \code{.respondToGet()}, \code{.respondToPut()}, etc., but the
+Cheetah default is \code{.respond()}). Servlets created by \code{cheetah
+compile} meet all these requirements.
+
+(Cheetah has a Webware plugin that automatically converts a \code{.tmpl servlet
+file} into a \code{.py servlet file} when the \code{.tmpl servlet file} is
+requested by a browser. However, that plugin is currently unavailable because
+it's being redesigned. For now, use \code{cheetah compile} instead.)
+
+What about logic code? Cheetah promises to keep content (the placeholder
+values), graphic design (the template definition and is display logic), and
+algorithmic logic (complex calculations and side effects) separate. How?
+Where do you do form processing?
+
+The answer is that your template class can inherit from a pure Python class
+containing the analytical logic. You can either use the \code{\#extends}
+directive in Cheetah to indicate the superclass(es), or write a Python
+\code{class} statement to do the same thing. See the template
+\code{Cheetah.Templates.SkeletonPage.tmpl} and its pure Python class
+\code{Cheetah.Templates.\_SkeletonPage.py} for an example of a template
+inheriting logic code. (See sections \ref{inheritanceEtc.extends} and
+\ref{inheritanceEtc.implements} for more information about \code{\#extends} and
+\code{\#implements}. They have to be used a certain right way.)
+
+If \code{\#WebKit.HTTPServlet} is not available, Cheetah fakes it with a
+dummy class to satisfy the dependency. This allows servlets to be tested on
+the command line even on systems where Webware is not installed. This works
+only with servlets that don't call back into WebKit for information about the
+current web transaction, since there is no web transaction. Trying to access
+form input, for instance, will raise an exception because it depends on a
+live web request object, and in the dummy class the request object is
+\code{None}.
+
+Because Webware servlets must be valid Python modules, and ``cheetah compile''
+can produce only valid module names, if you're converting an existing site that
+has .html filenames with hyphens (-), extra dots (.), etc, you'll have to
+rename them (and possibly use redirects).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Site frameworks}
+\label{webware.siteFrameworks}
+
+Web sites are normally arranged hierarchically, with certain features common
+to every page on the site, other features common to certain sections or
+subsections, and others unique to each page. You can model this easily with
+a hierarchy of classes, with specific servlets inheriting from their more
+general superclasses. Again, you can do this two ways, using Cheetah's
+{\bf Containment} approach or {\bf Inheritance} approach.
+
+In the Inheritance approach, parents provide \code{\#block}s and children
+override them using \code{\#def}. Each child \code{\#extend}s its immediate
+parent. Only the leaf servlets need to be under WebKit's document root
+directory. The superclass servlets can live anywhere in the filesystem
+that's in the Python path. (You may want to modify your WebKit startup
+script to add that library directory to your \code{PYTHONPATH} before starting
+WebKit.)
+
+% @@MO Examples: simple, IronSite, SkeletonPage.
+
+Section \ref{libraries.templates.skeletonPage} contains information on a stock
+template that simplifies defining the basic HTML structure of your web
+page templates.
+
+In the Containment approach, your hierarchy of servlets are not templates, but
+each uses one or more templates as it wishes. Children provide callback
+methods to to produce the various portions of the page that are their
+responsibility, and parents call those methods. Webware's \code{WebKit.Page}
+and \code{WebKit.SidebarPage} classes operate like this.
+
+% @@MO Show examples of WebKit.Page and WebKit.SidebarPage.
+
+Note that the two approaches are not compatible! \code{WebKit.Page} was not
+designed to intermix with \code{Cheetah.Templates.SkeletonPage}. Choose either
+one or the other, or expect to do some integration work.
+
+If you come up with a different strategy you think is worth noting in this
+chapter, let us know.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Directory structure}
+\label{webware.directoryStructure}
+
+Here's one way to organize your files for Webware+Cheetah.
+
+\begin{verbatim}
+www/ # Web root directory.
+ site1.example.com/ # Site subdirectory.
+ apache/ # Web server document root (for non-servlets).
+ www/ # WebKit document root.
+ index.py # http://site1.example.com/
+ index.tmpl # Source for above.
+ servlet2.py # http://site1.example.com/servlet2
+ servlet2.tmpl # Source for above.
+ lib/ # Directory for helper classes.
+ Site.py # Site superclass ("#extends Site").
+ Site.tmpl # Source for above.
+ Logic.py # Logic class inherited by some template.
+ webkit.config # Configuration file (for WebwareExperimental).
+ Webware/ # Standard Webware's MakeAppWorkDir directory.
+ AppServer # Startup program (for standard Webware).
+ Configs/ # Configuration directory (for standard Webware).
+ Application.config
+ # Configuration file (for standard Webware).
+ site2.example.org/ # Another virtual host on this computer....
+\end{verbatim}
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Initializing your template-servlet with Python code}
+\label{webware.calculations}
+
+If you need a place to initialize variables or do calculations for your
+template-servlet, you can put it in an \code{.awake()} method because WebKit
+automatically calls that early when processing the web transaction. If you
+do override \code{.awake()}, be sure to call the superclass \code{.awake}
+method. You probably want to do that first so that you have access to the
+web transaction data \code{Servlet.awake} provides. You don't have to worry
+about whether your parent class has its own \code{.awake} method, just call
+it anyway, and somebody up the inheritance chain will respond, or at minimum
+\code{Servlet.awake} will respond. Section
+\ref{tips.callingSuperclassMethods} gives examples of how to call a
+superclass method.
+
+As an alternative, you can put all your calculations in your own method and
+call it near the top of your template. (\code{\#silent}, section
+\ref{output.silent}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Form processing}
+\label{webware.form}
+
+There are many ways to display and process HTML forms with Cheetah.
+But basically, all form processing involves two steps.
+\begin{enumerate}
+\item{} Display the form.
+\item{} In the next web request, read the parameters the user submitted,
+check for user errors, perform any side effects (e.g., reading/writing a
+database or session data) and present the user an HTML response or another
+form.
+\end{enumerate}
+
+The second step may involve choosing between several templates to fill (or
+several servlets to redirect to), or a big if-elif-elif-else construct to
+display a different portion of the template depending on the situation.
+
+In the oldest web applications, step 1 and step 2 were handled by separate
+objects. Step 1 was a static HTML file, and step 2 was a CGI script.
+Frequently, a better strategy is to have a single servlet handle both steps.
+That way, the servlet has better control over the entire situation, and if
+the user submits unacceptable data, the servlet can redisplay the form with a
+"try again" error message at the top and and all the previous input filled in.
+The servlet can use the presence or absence of certain CGI parameters (e.g.,
+the submit button, or a hidden mode field) to determine which step to take.
+
+One neat way to build a servlet that can handle both the form displaying and
+form processing is like this:
+
+\begin{enumerate}
+\item Put your form HTML into an ordinary template-servlet. In each input
+ field, use a placeholder for the value of the \code{VALUE=} attribue.
+ Place another placeholder next to each field, for that field's error
+ message.
+\item Above the form, put a \code{\$processFormData} method call.
+\item Define that method in a Python class your template \code{\#extend}s. (Or
+ if it's a simple method, you can define it in a \code{\#def}.) The method
+ should:
+ \begin{enumerate}
+ \item Get the form input if any.
+ \item If the input variable corresponding to the submit field is empty,
+ there is no form input, so we're showing the form for the first time.
+ Initialize all VALUE= variables to their default value (usually ""),
+ and all error variables to "". Return "", which will be the value for
+ \code{\$processFormData}.
+ \item If the submit variable is not empty, fill the VALUE= variables with
+ the input data the user just submitted.
+ \item Now check the input for errors and put error messages in the error
+ placeholders.
+ \item If there were any user errors, return a general error message
+ string; this will be the value for \code{\$processFormData}.
+ \item If there were no errors, do whatever the form's job is (e.g., update
+ a database) and return a success message; this will be the value for
+ \code{\$processFormData}.
+ \end{enumerate}
+\item The top of the page will show your success/failure message (or nothing
+the first time around), with the form below. If there are errors, the user
+will have a chance to correct them. After a successful submit, the form will
+appear again, so the user can either review their entry, or change it and
+submit it again. Depending on the application, this may make the servlet
+update the same database record again, or it may generate a new record.
+\end{enumerate}
+
+% @@MO: Example of a template that shows a form and then processes the input.
+
+\code{FunFormKit} is a third-party Webware package that makes it easier to
+produce forms and handle their logic. It has been successfully been used with
+Cheetah. You can download FunFormKit from
+\url{http://colorstudy.net/software/funformkit/} and try it out for yourself.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Form input, cookies, session variables and web server variables}
+\label{webware.input}
+
+General variable tips that also apply to servlets are in section
+\ref{tips.placeholder}.
+
+To look up a CGI GET or POST parameter (with POST overriding):
+\begin{verbatim}
+$request.field('myField')
+self.request().field('myField')
+\end{verbatim}
+These will fail if Webware is not available, because \code{\$request}
+(aka \code{self.request()} will be \code{None} rather than a Webware
+\code{WebKit.Request} object. If you plan to read a lot of CGI parameters,
+you may want to put the \code{.fields} method into a local variable for
+convenience:
+\begin{verbatim}
+#set $fields = $request.fields
+$fields.myField
+\end{verbatim}
+But remember to do complicated calculations in Python, and assign the results
+to simple variables in the searchList for display. These \code{\$request}
+forms are useful only for occasions where you just need one or two simple
+request items that going to Python for would be overkill.
+
+To get a cookie or session parameter, subsitute ``cookie'' or ``session'' for
+``field'' above. To get a dictionary of all CGI parameters, substitute
+``fields'' (ditto for ``cookies''). To verify a field exists,
+substitute ``hasField'' (ditto for ``hasCookie'').
+
+Other useful request goodies:
+\begin{verbatim}
+## Defined in WebKit.Request
+$request.field('myField', 'default value')
+$request.time ## Time this request began in Unix ticks.
+$request.timeStamp ## Time in human-readable format ('asctime' format).
+## Defined in WebKit.HTTPRequest
+$request.hasField.myField ## Is a CGI parameter defined?
+$request.fields ## Dictionary of all CGI parameters.
+$request.cookie.myCookie ## A cookie parameter (also .hasCookie, .cookies).
+$request.value.myValue ## A field or cookie variable (field overrides)
+ ## (also .hasValue).
+$request.session.mySessionVar # A session variable.
+$request.extraURLPath ## URL path components to right of servlet, if any.
+$request.serverDictionary ## Dict of environmental vars from web server.
+$request.remoteUser ## Authenticated username. HTTPRequest.py source
+ ## suggests this is broken and always returns None.
+$request.remoteAddress ## User's IP address (string).
+$request.remoteName ## User's domain name, or IP address if none.
+$request.urlPath ## URI of this servlet.
+$request.urlPathDir ## URI of the directory containing this servlet.
+$request.serverSidePath ## Absolute path of this servlet on local filesystem.
+$request.serverURL ## URL of this servlet, without "http://" prefix,
+ ## extra path info or query string.
+$request.serverURLDir ## URL of this servlet's directory, without "http://".
+$log("message") ## Put a message in the Webware server log. (If you
+ ## define your own 'log' variable, it will override
+ ## this; use $self.log("message") in that case.
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection{.webInput()}
+\label{webware.webInput}
+
+From the method docstring:
+
+\begin{verbatim}
+ def webInput(self, names, namesMulti=(), default='', src='f',
+ defaultInt=0, defaultFloat=0.00, badInt=0, badFloat=0.00, debug=False):
+
+This method places the specified GET/POST fields, cookies or session variables
+into a dictionary, which is both returned and put at the beginning of the
+searchList. It handles:
+ * single vs multiple values
+ * conversion to integer or float for specified names
+ * default values/exceptions for missing or bad values
+ * printing a snapshot of all values retrieved for debugging
+All the 'default*' and 'bad*' arguments have "use or raise" behavior, meaning
+that if they're a subclass of Exception, they're raised. If they're anything
+else, that value is substituted for the missing/bad value.
+
+The simplest usage is:
+
+ #silent $webInput(['choice'])
+ $choice
+
+ dic = self.webInput(['choice'])
+ write(dic['choice'])
+
+Both these examples retrieves the GET/POST field 'choice' and print it. If you
+leave off the "#silent", all the values would be printed too. But a better way
+to preview the values is
+
+ #silent $webInput(['name'], $debug=1)
+
+because this pretty-prints all the values inside HTML <PRE> tags.
+
+Since we didn't specify any coversions, the value is a string. It's a "single"
+value because we specified it in 'names' rather than 'namesMulti'. Single
+values work like this:
+ * If one value is found, take it.
+ * If several values are found, choose one arbitrarily and ignore the rest.
+ * If no values are found, use or raise the appropriate 'default*' value.
+
+Multi values work like this:
+ * If one value is found, put it in a list.
+ * If several values are found, leave them in a list.
+ * If no values are found, use the empty list ([]). The 'default*'
+ arguments are *not* consulted in this case.
+
+Example: assume 'days' came from a set of checkboxes or a multiple combo box
+on a form, and the user chose "Monday", "Tuesday" and "Thursday".
+
+ #silent $webInput([], ['days'])
+ The days you chose are: #slurp
+ #for $day in $days
+ $day #slurp
+ #end for
+
+ dic = self.webInput([], ['days'])
+ write("The days you chose are: ")
+ for day in dic['days']:
+ write(day + " ")
+
+Both these examples print: "The days you chose are: Monday Tuesday Thursday".
+
+By default, missing strings are replaced by "" and missing/bad numbers by zero.
+(A "bad number" means the converter raised an exception for it, usually because
+of non-numeric characters in the value.) This mimics Perl/PHP behavior, and
+simplifies coding for many applications where missing/bad values *should* be
+blank/zero. In those relatively few cases where you must distinguish between
+""/zero on the one hand and missing/bad on the other, change the appropriate
+'default*' and 'bad*' arguments to something like:
+ * None
+ * another constant value
+ * $NonNumericInputError/self.NonNumericInputError
+ * $ValueError/ValueError
+(NonNumericInputError is defined in this class and is useful for
+distinguishing between bad input vs a TypeError/ValueError
+thrown for some other reason.)
+
+Here's an example using multiple values to schedule newspaper deliveries.
+'checkboxes' comes from a form with checkboxes for all the days of the week.
+The days the user previously chose are preselected. The user checks/unchecks
+boxes as desired and presses Submit. The value of 'checkboxes' is a list of
+checkboxes that were checked when Submit was pressed. Our task now is to
+turn on the days the user checked, turn off the days he unchecked, and leave
+on or off the days he didn't change.
+
+ dic = self.webInput([], ['dayCheckboxes'])
+ wantedDays = dic['dayCheckboxes'] # The days the user checked.
+ for day, on in self.getAllValues():
+ if not on and wantedDays.has_key(day):
+ self.TurnOn(day)
+ # ... Set a flag or insert a database record ...
+ elif on and not wantedDays.has_key(day):
+ self.TurnOff(day)
+ # ... Unset a flag or delete a database record ...
+
+'source' allows you to look up the variables from a number of different
+sources:
+ 'f' fields (CGI GET/POST parameters)
+ 'c' cookies
+ 's' session variables
+ 'v' "values", meaning fields or cookies
+
+In many forms, you're dealing only with strings, which is why the
+'default' argument is third and the numeric arguments are banished to
+the end. But sometimes you want automatic number conversion, so that
+you can do numeric comparisons in your templates without having to
+write a bunch of conversion/exception handling code. Example:
+
+ #silent $webInput(['name', 'height:int'])
+ $name is $height cm tall.
+ #if $height >= 300
+ Wow, you're tall!
+ #else
+ Pshaw, you're short.
+ #end if
+
+ dic = self.webInput(['name', 'height:int'])
+ name = dic[name]
+ height = dic[height]
+ write("%s is %s cm tall." % (name, height))
+ if height > 300:
+ write("Wow, you're tall!")
+ else:
+ write("Pshaw, you're short.")
+
+To convert a value to a number, suffix ":int" or ":float" to the name. The
+method will search first for a "height:int" variable and then for a "height"
+variable. (It will be called "height" in the final dictionary.) If a numeric
+conversion fails, use or raise 'badInt' or 'badFloat'. Missing values work
+the same way as for strings, except the default is 'defaultInt' or
+'defaultFloat' instead of 'default'.
+
+If a name represents an uploaded file, the entire file will be read into
+memory. For more sophisticated file-upload handling, leave that name out of
+the list and do your own handling, or wait for Cheetah.Utils.UploadFileMixin.
+
+This mixin class works only in a subclass that also inherits from
+Webware's Servlet or HTTPServlet. Otherwise you'll get an AttributeError
+on 'self.request'.
+
+EXCEPTIONS: ValueError if 'source' is not one of the stated characters.
+TypeError if a conversion suffix is not ":int" or ":float".
+\end{verbatim}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{More examples}
+\label{webware.examples}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Example A -- a standalone servlet}
+%\label{}
+
+% @@MO:
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Example B -- a servlet under a site framework}
+%\label{}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsubsection*{Example C -- several servlets with a common template}
+%\label{}
+
+
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\subsection{Other Tips}
+\label{webware.otherTips}
+
+If your servlet accesses external files (e.g., via an \code{\#include}
+directive), remember that the current directory is not necessarily directory
+the servlet is in. It's probably some other directory WebKit chose. To find a
+file relative to the servlet's directory, prefix the path with whatever
+\code{self.serverSidePath()} returns (from \code{Servlet.serverSidePath()}.
+
+If you don't understand how \code{\#extends} and \code{\#implements} work, and
+about a template's main method, read the chapter on inheritance (sections
+\ref{inheritanceEtc.extends} and \ref{inheritanceEtc.implements}). This may
+help you avoid buggy servlets.
+
+% Local Variables:
+% TeX-master: "users_guide"
+% End:
+%# vim: sw=4 ts=4 expandtab