Code

*** empty log message ***
authorrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Fri, 26 Mar 2004 01:10:19 +0000 (01:10 +0000)
committerrichard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2>
Fri, 26 Mar 2004 01:10:19 +0000 (01:10 +0000)
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@2200 57a73879-2fb5-44c3-a270-3262357dd7e2

doc/templating.txt

index 0a750d9ae9539d958dbd98ce1199dac8b8d17ac7..940ae016452a54240003dce5aca1b203c0e13396 100644 (file)
@@ -1,359 +1,6 @@
-==========================
-HTML Templating Mechanisms
-==========================
+=========================
+Roundup Tracker Templates
+=========================
 
-:Version: $Revision: 1.14 $
-
-Current Situation and Issues
-============================
-
-Syntax
-------
-
-Roundup currently uses an element-based HTML-tag-alike templating syntax::
-
-   <display call="checklist('status')">
-
-The templates were initially parsed using recursive regular expression
-parsing, and since no template tag could encapsulate itself, the parser
-worked just fine. Then we got the ``<require>`` tag, which could have other
-``<require>`` tags inside. This forced us to move towards a more complete
-parser, using the standard python sgmllib/htmllib parser. The downside of this
-switch is that constructs of the form::
-
-   <tr class="row-<display call="plain('status')">">
-
-don't parse as we'd hope. We can modify the parser to work, but that doesn't
-another couple of issues that have arisen:
-
-1. the template syntax is not well-formed, and therefore is a pain to parse
-   and doesn't play well with other tools, and
-2. user requirements generally have to be anticipated and accounted for in
-   templating functions (like ``plain()`` and ``checklist()`` above), and
-   we are therefore artificially restrictive.
-
-Arguments for switching templating systems:
-
-*Pros*
-
-  - more flexibility in templating control and content
-  - we can be well-formed
-
-*Cons*
-
-  - installed user base (though they'd have to edit their templates with the
-    next release anyway)
-  - current templating system is pretty trivial, and a more flexible system
-    is likely to be more complex
-
-
-Templates
----------
-
-We should also take this opportunity to open up the flexibility of the
-templates through:
-
-1. allowing the tracker to define a "page" template, which holds the overall
-   page structure, including header and footer
-
-
-
-Possible approaches
-===================
-
-Zope's PageTemplates
---------------------
-
-Using Zope's PageTemplates seems to be the best approach of the lot.
-In my opinion, it's the peak of HTML templating technology at present. With
-appropriate infrastructure, the above two examples would read:
-
-  <span tal:replace="item/status/checklist">status checklist</span>
-
-  <tr tal:attributes="class string:row-${item/status/name}">
-
-... which doesn't look that much more complicated... honest...
-
-Other fun can be had when you start playing with stuff like:
-
-  <table>
-   <tr tal:repeat="message item/msg/list">
-    <td tal:define="from message/from">
-     <a href="" tal:attributes="href string:mailto:${from/address}"
-                tal:content="from/name">mailto link</a>
-    </td>
-    <td tal:content="message/title">subject</td>
-    <td tal:content="message/created">received date</td>
-   </tr>
-  </table>
-
-Note: even if we don't switch templating as a whole, this document may be
-applied to the ZRoundup frontend.
-
-PageTemplates in a Nutshell
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-PageTemplates consist of three technologies:
-
-TAL - Template Attribute Language
-  This is the syntax which is woven into the HTML using the ``tal:`` tag
-  attributes. A TAL parser pulls out the TAL commands from the attributes
-  runs them using some expression engine.
-
-TALES - TAL Expression Syntax
-  The expression engine used in this case is TALES, which runs the expressions
-  that form the tag attribute values. TALES expressions come in three
-  flavours:
-
-  Path Expressions - eg. ``item/status/checklist``
-   These are object attribute / item accesses. Roughly speaking, the path
-   ``item/status/checklist`` is broken into parts ``item``, ``status``
-   and ``checklist``. The ``item`` part is the root of the expression.
-   We then look for a ``status`` attribute on ``item``, or failing that, a
-   ``status`` item (as in ``item['status']``). If that
-   fails, the path expression fails. When we get to the end, the object we're
-   left with is evaluated to get a string - methods are called, objects are
-   stringified. Path expressions may have an optional ``path:`` prefix, though
-   they are the default expression type, so it's not necessary.
-
-  String Expressions - eg. ``string:hello ${user/name}``
-   These expressions are simple string interpolations (though they can be just
-   plain strings with no interpolation if you want. The expression in the
-   ``${ ... }`` is just a path expression as above.
-
-  Python Expressions - eg. ``python: 1+1``
-   These expressions give the full power of Python. All the "root level"
-   variables are available, so ``python:item.status.checklist()`` would be
-   equivalent to ``item/status/checklist``, assuming that ``checklist`` is
-   a method.
-
-PageTemplates
-  The PageTemplates module glues together TAL and TALES.
-
-
-Implementation
-~~~~~~~~~~~~~~
-
-I'm envisaging an infrastructure layer where each template has the following
-"root level" (that is, directly accessible in the TALES namespace) variables
-defined:
-
-*klass*
-  The current class of item being displayed as an HTMLClass instance. Name is
-  mangled so it can be used in Python expressions.
-
-*item*
-  The current item from the database, if we're viewing a specific item, as an
-  HTMLItem instance. If it doesn't exist, then we're on a new item page.
-
-(*classname*)
-  this is one of two things:
-
-  1. the *item* is also available under its classname, so a *user* item
-     would also be available under the name *user*. This is also an HTMLItem
-     instance.
-  2. if there's no *item* then the current class is available through this
-     name, thus "user/name" and "user/name/menu" will still work - the latter
-     will pull information from the form if it can.
-
-  this is a dangerous attribute, and may cause us pain the long run (its name
-  may clash with other top-level variables ... it already clashed with the
-  proposed *user* variable). It might be safer to go with just *class* and
-  *item*, actually...
-
-*form*
-  The current CGI form information as a mapping of form argument name to value
-
-*request*
-  Includes information about the current request, including:
-   - the url
-   - the current index information (``filterspec``, ``filter`` args,
-     ``properties``, etc) parsed out of the form. 
-   - methods for easy filterspec link generation
-   - *user*, the current user item as an HTMLItem instance
-
-*tracker*
-  The current tracker
-
-*db*
-  The current open database
-
-*config*
-  The current instance config
-
-*modules*
-  python modules made available (XXX: not sure what's actually in there tho)
-
-Accesses through a class (either through *klass* or *db.<classname>*)::
-
-    class HTMLClass:
-        def __getattr__(self, attr):
-            ''' return an HTMLItem instance '''
-        def classhelp(self, ...)
-        def list(self, ...)
-        def filter(self):
-            ''' Return a list of items from this class, filtered and sorted
-                by the current requested filterspec/filter/sort/group args
-            '''
-
-Accesses through an *item*::
-
-    class HTMLItem:
-        def __getattr__(self, attr):
-            ''' return an HTMLItem instance '''
-        def history(self, ...)
-        def remove(self, ...)
-
-Note: the above could cause problems if someone wants to have properties
-called "history" or "remove"...
-
-String, Number, Date, Interval HTMLProperty
- a wrapper object which may be stringified for the current plain() behaviour
- and has methods emulating all the current display functions, so
- ``item/name/plain`` would emulate the current ``call="plain()``". Also, 
- ``python:item.name.plain(name=value)`` would work just fine::
-
-    class HTMLProperty:
-        def __init__(self, instance, db, ...)
-        def __str__(self):
-            return self.plain()
-
-    class StringHTMLProperty(HTLProperty):
-        def plain(self, ...)
-        def field(self, ...)
-        def stext(self, ...)
-        def multiline(self, ...)
-        def email(self, ...)
-
-    class NumberHTMLProperty(HTMLProperty):
-        def plain(self, ...)
-        def field(self, ...)
-
-    class BooleanHTMLProperty(HTMLProperty):
-        def plain(self, ...)
-        def field(self, ...)
-
-    class DateHTMLProperty(HTMLProperty):
-        def plain(self, ...)
-        def field(self, ...)
-        def reldate(self, ...)
-
-    class IntervalHTMLProperty(HTMLProperty):
-        def plain(self, ...)
-        def field(self, ...)
-        def pretty(self, ...)
-
-Link HTMLProperty
- the wrapper object would include the above as well as being able to access
- the class information. Stringifying the object itself would result in the
- value from the item being displayed. Accessing attributes of this object
- would result in the appropriate entry from the class being queried for the
- property accessed (so item/assignedto/name would look up the user entry
- identified by the assignedto property on item, and then the name property of
- that user)::
-    
-    class LinkHTMLProperty(HTMLProperty):
-        ''' Be a HTMLItem too '''
-        def __getattr__(self, attr):
-            ''' return a new HTMLProperty '''
-        def download(self, ...)
-        def checklist(self, ...)
-
-Multilink HTMLProperty
- the wrapper would also be iterable, returning a wrapper object like the Link
- case for each entry in the multilink::
-
-    class MultilinkHTMLProperty(HTMLProperty):
-        def __len__(self):
-            ''' length of the multilink '''
-        def __getitem(self, num):
-            ''' return a new HTMLItem '''
-        def checklist(self, ...)
-        def list(self, ...)
-*request*
- the request object will handle::
-
-    class Request:
-        def __init__(self, ...)
-        def filterspec(self, ...)
-
-Accesses through the *user* attribute of *request*::
-
-    class HTMLUser(HTMLItem):
-        def hasPermission(self, ...)
-
-(note that the other permission check implemented by the security module may
- be implemented easily in a tal:condition, so isn't needed here)
-
-Template files
-~~~~~~~~~~~~~~
-
-Each instance will have the opportunity to supply the following templates:
-
-page
- This is the overall page look template, and includes at some point a TAL
- command that includes the variable "content". This variable causes the actual
- page content to be generated.
-
-[classname].[template type]
- Templates that have this form are applied to item data. There are three forms
- of special template types:
-
- [classname].index
-  This template is used when the URL specifies only the class, and not an item
-  designator.  It displays a list of [classname] items from the database, and
-  a "filter refinement" form.
-  Would perform a TAL ``repeat`` command using the list supplied by
-  ``class/filter``. This deviates from the current situation in that currently
-  the index template specifies a single row, and the filter part is
-  automatically generated.
-
- [classname].item
-  This template is used when the URL specifies an item designator. It's the
-  default template used (when no template is explicitly given). It displays
-  a single item from the database using the *classname* variable (that
-  is, the variable of the same name as the class being displayed. If 
-
- These two special template types may be overridden by the :template CGI
- variable.
-
-Note that the "newitem" template doesn't exist any more because the item
-templates may determine whether the page has an existing item to render. The
-new item page would be accessed by "/tracker/url/issue?:template=item".
-The old "filter" template has been subsumed by the index template.
-
-
-Integrating Code
-~~~~~~~~~~~~~~~~
-
-We will install PageTemplates, TAL and ZTUtils in site-packages. If there is a
-local Zope installation, it will use its own PageTemplates code (Zope modifies
-the module search path to give precedence to its own module library).
-
-We will then install the trivial MultiMapping and ComputedAttribute modules in
-the Roundup package, and have some import trickery that determines whether
-they are required, and if so they will be imported as if they were at the
-"top level" of the module namespace.
-
-New CGI client structure
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Handling of a request in the CGI client will take three phases:
-
-1. Determine user, pre-set "content" to authorisation page if necessary
-2. Render main page, with callback to "content"
-3. Render content - if not pre-set, then determine which content to render
-
-
-Use Cases
-~~~~~~~~~
-
-Meta/parent bug
-  Can be done with addition to the schema and then the actual parent heirarchy
-  may be displayed with a new template page ":dependencies" or something.
-
-Submission wizard
-  Can be done using new templates ":page1", ":page2", etc and some additional
-  actions on the CGI Client class in the instance.
+:Version: $Revision: 1.15 $