Overview
JOTWeb uses HTML with TAL tagging for the page markup and templating, and Python modules for the logic to build dynamic web-sites. This creates an effective separation between presentation and logic. Python programmers will feel right at home writing the web-site code, because code is contained in standard python modules. Web-site designers are aided by the use of TAL markup, which is quickly becoming a standard for creation of dynamic web-sites and interoperates well with many HTML and XML editors and other tools.
Currently (July, 2003), JOTWeb is in development. It already has nice modules for form processing and validation and session management. These are heavily oriented towards making the common use of these tasks as easy as possible, while allowing for more complex use. It is planned that this trend will continue as more modules are added.
The goal is to have a system which both HTML and Python coders can feel comfortable with and is simple, elegant and powerful.
Below you will find information using TAL and Python within the JOTWeb framework.
Dynamic Pages and Templates Using TAL
Introduction to TAL
TAL, the Template Attribute Language, uses attributes on existing tags to change their behavior. This allows normal HTML to be extended to produce dynamic output. TAL has been implemented in the Zope framework as "ZPT" or "Zope Page Templates". While ZPT provides a different name-space and some slightly different functionality, the basics of both TAL and ZPT are very similar. The bulk of the ZPT documentation is relavent to TAL.
This document will not go into the specifics of TAL, except to note where the JOTWeb implementation is different other implementations, in particular Zope's ZPT. It is recommended that JOTWeb users review the Zope ZPT/TAL documentation to learn TAL/TALES/METAL.
What are ZPT, TAL, TALES and METAL?
- ZPT (Zope Page Templates) is an implementation of TAL, TALES, and METAL.
- TAL (Template Attribute Language) specifies tags which may be used for creation of dynamic pages. For example, "tal:replace" is the "TAL" part of a statement.
- TALES (TAL Expression Syntax) defines how you can specify expressions for TAL statements. TALES is the part that comes after the equals sign in "tal:replace=".
- METAL (Macro Expansion for TAL) allows you to create macros which can contain common parts of pages, and have "slots" which can be filled in with different values depending on the context in which the macro is expanded. For example, you may create a header that has a slot for the page title in it, or indeed a macro that represents the standard structure of your page with slots for the title and body.
Please see the following ZPT/TAL/TALES/METAL docuemntation for learning to use JOTWeb's templating:
- The next section below is a "Quickstart" that gives some examples of TAL, TALES, and METAL.
- Quick description of TAL.
- TAL 1.4 Specification, a quick set of examples of statement types and use. Terse, but will give technical users a quick introduction to TAL. Does not cover METAL, etc.
- METAL (Macro Expansion for TAL), Quick description.
- METAL 1.0 Specification, a quick set of examples. Terse, but will give technical users a quick introduction to METAL.
- TALES 1.3 Specification, a quick set of examples of TALES. TALES is how you specify values to TAL. Terse, but will give technical users a quick introduction to TALES.
- A Simple TAL Tutorial
- Chapter 5 of The Zope Book, Using Zope Page Templates. Extensive examples of using ZPT -- includes manu Zope-specifics, but much of it is applicable to JOTWeb as well.
- Chapter 9 of the Zope Book, Advanced Page Templates. An extensive set of examples of using ZPT.
Quickstart
Let's assume that you hvae the following Python code in a file named "code.py":
import random def iswinner(): return(random.random() > 0.5) sitename = 'My Test Site' subdirlist = [ 'foo', 'bar', 'baz' ]
Dynamic Tag Contents: You can use TAL to dynamically modify your HTML to include the title in your document:
<title tal:content="code/sitename">Title Placeholder</title>
Which would be rendered as:
<title">My Test Site</title>
Tag replacement: You can use tal:replace to replace the full tag, instead of just setting it's content:
Welcome to: <span tal:replace="code/sitename">Name Placeholder</span>
Which would be rendered as:
Welcome to: My Test Site
Conditionals: Parts of your document can be conditionally included by using the tal:condition tag. When the results of the expression is true (using the standard Python idea of "trueness"), the tag is expanded, otherwise it is supressed. For example, the "code/iswinner" function uses the random number generator and there is a 50% chance that it will return true and display "You are a winner!"
<h1>Welcome to my page</h1> <span tal:condition="code/iswinner">You may already be a winner!</span>
Which would 50% of the time render to the following:
<h1>Welcome to my page</h1> You may already be a winner!
Variables: Variables can be defined in both the local and global name-spaces. For example:
<span tal:define="global sitename string:My Site"></span> <span tal:define="pagename string:A Page"></span> You are on page "<span tal:replace="pagename"></span>" of site "<span tal:replace="sitename"></span>".
Which will be rendered as:
You are on page "A Page" of site "My Site".
Iteration: A list or other iteratable type can be looped over using the "tal:repeat" construct. This works like the "tal:define" tag, which creates a variable. The current tag is repeated, with each item of the list. For example, the following creates a "subdir" variable and then displays the name with a line break after it:
<span tal:repeat="subdir code/subdirlist"> <span tal:replace="subdir">Subdir Placeholder</span><br /></span>
Which will be rendered as:
foo<br />bar<br />baz<br />
Dynamic Attributes: Attributes of existing tags can be set by using "tal:attributes". For example, to create a dynamic list like the above (which combines many of the above):
<span tal:repeat="subdir code/subdirname">
Go to: <a href="placeholder" tal:attributes="href subdir"
tal:content="subdir">Link Placeholder</a>
</span>Which will be rendered as:
Go to: <a href="foo">foo</a> Go to: <a href="bar">bar</a> Go to: <a href="baz">baz</a>
Macros: Macros can be used to create templates for how parts of your site looks, with portions filled in later. For example, imagine that you create a file named "standard.html" with the conents:
<div metal:define-macro="header" tal:omit-tag="">
<head>
<title><div metal:define-slot="title">Title Placeholder</div></title>
<link REL="SHORTCUT ICON" HREF="/favicon.ico">
<link rel="STYLESHEET" href="/style.css" type="text/css">
</head>
</div>Then you can create pages which reference this macro and fill in the title element:
<div metal:use-macro="standard/macros/header"> <div metal:fill-slot="title" tal:omit-tag="">My Home Page</div> </div>
Which will be rendered as:
<head>
<title>My Home Page</title>
<link REL="SHORTCUT ICON" HREF="/favicon.ico">
<link rel="STYLESHEET" href="/style.css" type="text/css">
</head>Differences from ZPT
The following ZPT constructs do not work under JOTWeb:
- "python:" paths can only contain rather simple expressions.
- Python code cannot currently return "default" to a TAL tag, causing the default tag text to be used.
- "tal:on-error" does not seem to be implemented.
- The Zope "modules" symbol is not currently available.
Please let me know if other discrepencies are found
Name-spaces
TAL Name-space
TAL tags operate in their own name-space. There are two of these available, local and global. These are very similar in function to local and global variables in python. Local variables exist only in the current scope while global variables (defined with tal:define="global name value") exist for the remaineder of the rendering of the current request.
The TAL name-space can be accessed from Python code using the jotwebutils.getGlobal() and jotwebutils.getLocal() functions.
JOTWeb Root Name-space
JOTWeb includes a name-space in which names related to JOTWeb or the current request are located. In TAL, this is called "jotwebnsroot", and in Python it can be accessed via the "getNSRoot()" method of the "jotwebutils" module.
The following entries are available under "jotwebnsroot":
- "config" -- This is the Configuration name-space.
- "persistent" -- Entries placed in the Persistent name-space remain across requests.
Configuration Name-space
JOTWeb sets up a number of configuration entries before starting the page rendering. It then looks for a file named "jotweb.conf" in the JOTWeb document root directory and loads entries from that file. Those directives are all loaded before page rendering starts, and is a good place to set up values and objects or even potentially running code, before the page rendering starts.
See the jotweb.config section for more information on the configuration and the default values.
Persistent Name-space
Entries made in this name-space last across requests. Note that multiple requests in succession may hit different instances of the JOTWeb server-side code. For example, Apache may run multiple processes and each one may have it's own JOTWeb server-side instance. Each one of these would have it's own persistent space.
This may be useful for storing information such as database connections, preventing the database connections from having to be set up and torn down with every request.
Note that entries created by JOTWeb's standard library are prefixed with "jotweb_". This prefix should be avoided in your own code. Other libraries may wish to use their own unique prefix to prevent clashes with user code.
Specifying file type: HTML, XHTML, or XML
JOTWeb will compile the template as XML if the file name ends in ".xml". By default, but controlled with the "filetypemagic" config option, JOTWeb will also look at the first line of the file to determine the file type. The file type is set depending on what the first line of the file starts with:
- XML:
<?xmlor<!--jotwebtype=xml - HTML:
<!--jotwebtype=html - XHTML:
<!--jotwebtype=xhtml
Note that there can be leading space before these strings, but the strings must otherwise appear exactly as above.
See the JOTWeb config documentation for more information on configuration files and the entries available.
Entity Reference and Cascading
Entity Reference
Consider the TAL statement: <span tal:content="name1/name2">
The "name1/name2" references the "name2" entity of "name1". There are three places that TAL looks to find the entity "name1". First, it looks in it's own global name-space. It then looks in the local name-space. Both of these can be set with "tal:define" tags. Finally, it will look in the file-system for a file named "name1.py" or "name1.html".
Note that while in my example I'm using "name1/name2", a two-entity name, these can actually be composed of one or more entities. For example, "name1" and "name1/name2/name3/name4" are both valid entity names.
"name1.html" will mostly be used with METAL for accessing macros that reside in the specified file. Again, you do not list the .html extension when referencing the entity. If there is a file named "name1.py", it is loaded as a Python module and "name2" is looked for as a Python attribute of that module. If "name1" matches a directory, JOTWeb will begin looking for a "name2" entity within that directory (using the rules above).
If an entity resolves to a Python object, sub-entities are resolved (and called, if callable and the "nocall:" TALES path modifier is not set (for example: tal:replace="nocall:code/myfunc"). More on resolving with Python objects is in the following section.
Once a path is resolved, the value of the resulting object or return value of the function call is used in the TAL expression. In the event that an entity cannot be resolved, the path expansion fails. This may be tested for using the "exists:" or "not:" TALES path modifier (for example: tal:condition="exists: code/noexistfunc").
Python Types
In the resolution of a TALES expression, the result may be a python object. If the path in the TALES expression specifies sub-entities of this object, JOTWeb will continue resolving down this chain. For example, suppose you have a file "code.py", which contains:
data = { 'username' : 'jafo', 'homedir' : '/home/jafo' }The username can be obtained acessed with:
Username: <span tal:replace="code/data/username">Username Placeholder</span>
The following Python types may be accessed as entities in the following ways:
- Modules: Attributes of modules (names, functions, classes) may be referenced by name.
- Class Instances: Any class instance may have it's attributes used as element names. Methods will be called, if callable, and other objects may be directly referenced.
- Classes: A class itself may be instantiated by calling it.
- Dictionaries: Items and methods may be accessed by name. There is no way in TAL to clear up a collision between dictionary items and methods. Python code must be used to select one or the other. In the case of a collision, the item is referenced in preference to the method.
- Lists: Lists may be used for iteration, and the list methods may be directly called. Note that there is currently no syntax for directly accessing list sub-elements through TAL.
- Functions: If an entity resolves to a function or other callable object, it will be called unless the "nocall:" TALES path modifier is used. The result may be used for further path resolution.
Note that TAL expressions can contain ".." to explicitly reference entities in the file-system directory above this one, as long as that does not take the resolution out of the JOTWeb document root.
Cascading
Cascading is the mechanism JOTWeb uses to make entites available to directories below the current directory. It is similar in spirit to Acquisition or Inheritance or a Search Path, where successive elements are searched trying to resolve an entity. JOTWeb will try to resolve an element in the file-system by first looking in the current directory, then searching directories above it until it reaches the JOTWeb document root.
This can be useful for applying certain standard METAL macros, python objects or modules, and common code to a site, while allowing portions of the site to override certain parts of the standard elements.
For example, you may wish to make a "template.html" which contains a METAL macro that defines the framework that every page has. For example, maybe every page has a standard header and footer, but different body.
This can also be useful for "area menus", menus that change depending on the portion of the site you are in, and even for "site menus", a menu which applies to all pages (and cascades down to all sub-pages). Common code can be defined at the top-level directory, and be accessed by every page in the site (without having to know a relative path to the file), or functions and data can be overridden in certain parts of the site.
For example, maybe you have Canada and Europe directories, which have different implementations of "getdate()" that return differently formatted date strings.
Apache Integration
Overview
Most users will interact with JOTWeb through the Apache web server. JOTWeb includes code to allow it to be called as a content handler from Apache via mod_python. The sample Apache configuration provided with JOTWeb sets up Apache so that files with a ".html" extension are rendered through JOTWeb. This could be changed to ".jot", allowing for static ".html" pages being served up without going through JOTWeb.
httpd.conf Configuration Directives
Below is a list of configuration directives that may be used with JOTWeb. Please see the sample "jotweb-httpd.conf" file provided with JOTWeb for a sample.
- Directive: PythonOption JOTWebDocRoot <path>
Default: Apache DocumentRoot.
Description: This option allows you to specify the root directory that JOTWeb will use. JOTWeb will not internally allow accesses outside this directory, however python code can easily access outside the full file-system.
The default is to use the Apache DocumentRoot setting. In most cases this is acceptable if not optimal. If you use a Location tag with an Alias to set up a JOTWeb root outside the server's default root (as the sample config does), you will need to adjust this.
- Directive: PythonOption JOTWebAllowPython no
Default: yes
Description: This directive specifies whether TAL tags are allowed to use the "python:" path. Versions of SimpleTAL before 3.2 did not include a "python:" path at all, so this setting may be used to ensure compatibility.
Allowed true values are 1, '1', 'yes', 'y', 'true', and 'true'. Any other value is interpreted as false.
Note that there seem to be some issues with the "python:" path in SimpleTAL. Mostly I've found it better to avoid "python:" and simply access real python code in a module.
mod_python Configuration Directives
The directives below aren't directly JOTWeb provided, but are related to mod_python. Since JOTWeb uses mod_python, these may have an impact on JOTWeb operation. Please see the mod_python documentatoin for more information.
- Directive: AddHandler python-program .html
Description: This directive is part of what is required to enable JOTWeb processing of files. In this case, it is specifying that files with a ".html" extension are to be handled by JOTWeb. You may also wish to list ".xml" (by duplicating the above line). If you prefer that .html files remain static, ".html" in the above line may be changed to ".jot".
- Directive: PythonHandler jotweb.jotweb_mod_python::handler
Description: This directive, combined with the "AddHandler python-program" described above, causes JOTWeb's handler to be called to render pages.
- Directive:PythonAutoReload On
Description: This is a mod_python directive, more details can be found in the mod_python documentation. This enables/disables mod_python from re-loading modules that seem to have changed since the last load. Disabling this may produce a small performance improvement, but may require that Apache be re-started if any Python code is changed. It is recommended that this be left On, particularly in development systems.
- Directive: PythonDebug On
Default: Off
Description: This is a mod_python directive, more details can be found in the mod_python documentation. This directive enables the display of Python tracebacks to the client browser. This may expose part of your programming logic, and while useful for development may want to be disabled for production web-sites.
- Directive: PythonOptimize On
Description: This is a mod_python directive, more details can be found in the mod_python documentation. This directive will produce faster byte-code but at the expense of slightly slower initial start-up the first time a module is referenced from mod_python. This should only occur once per Apache process, at most, so several requests are likely to be handled by the same optimized code.
Controlling Headers Via Global Variables
The mod_python connector understands a few different global variables for causing different headers to be sent out based on the value at the end of the page render. You can, of course, use Python code to generate any headers you want. These variables are meant to allow you to easily set expiration headers in the TAL templates.
Here are a list of the available headers. Note that the headers that take a Date argument expect to have a number of seconds from now as the argument. This is because it's fairly hard to generate a relative HTTP Date in TAL.
- Global Variable: headerExpires
Valid Values: 0 (expires immediately) to 31536000 (never expire)
Description: This controls the Expires header. The argument is converted to an HTTP Date field in the appropriate format, indicating a date that many seconds in the future.
- Global Variable: headerCacheControl
Valid Values: Valid Cache-Control values.
Description: This controls the Cache-Control header. See the RFC2616 Section 14.9 discussion on Cache-Control for more information.
- Global Variable: headerContentLanguage
Valid Values: Valid Content-Language values.
Description: This controls the Content-Language header. See the RFC2616 Section 14.12 discussion on Content-Language for more information.