.. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% .. % Copyright 2001 by Object Craft P/L, Melbourne, Australia. .. % LICENCE - see LICENCE file distributed with this software for details. .. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% .. _tag-ref: ******************* Templates Reference ******************* Albatross provides a templating system that applications use to generate HTML. Albatross templates are loosely based on XML, but are a little more forgiving. The primary features are: * All standard Albatross tags use an ``al-`` prefix, with the ``alx-`` prefix available applications and extensions to implement their own tags. * For Albatross tags that enclose content you can use the XML empty tag syntax to indicate that there is no content. * All Albatross tag and attribute names are transformed to lowercase. * Attributes may appear over multiple lines, and attribute values can be broken over multiple lines. * Attribute values can be enclosed with either single or double quotes. * The enclosing quote character can be used within the attribute string if it is escaped by a backslash (``\``). * The template parser uses regular expressions to locate all of the template tags. All text that is not recognised as an Albatross tag or associated trailing whitespace is passed unchanged through the parser. In practice this means that you can use the templating system for non-HTML files. For example the following two constructs are identical. : .. code-block:: albatross .. code-block:: albatross The Albatross templating system includes enhanced versions of many HTML tags, as well as tags that control the flow of execution of the template. These are described in detail in subsequent sections. The templating system also allows any other HTML tag to be prefixed with ``al-`` to gain access to the attribute evaluation system. Attributes of Albatross tags can be processed in a number of ways: * Some attributes control the behaviour of the enhanced and flow control tags. * Appending ``expr`` to any attribute name causes the value of the attribute to be evaluated and the results of the evaluation substituted for the attribute value. For example: .. code-block:: albatross would produce .. code-block:: html * Appending ``bool`` to any attribute name causes the attribute to be being evaluated in a boolean context. If the result is ``True``, a boolean HTML attribute is emitted, and if the result is ``False``, no attribute is emitted. For example: .. code-block:: albatross If ``abc.isdisabled()`` evaluates as ``True``, then the ``disabled`` attribute is emitted: .. code-block:: html But if ``abc.isdisabled()`` evaluates as ``False``, then the ``disabled`` attribute is suppressed entirely: .. code-block:: html * Any other attributes are passed through unchanged. The following example demonstrates all of these to change the styling of alternate rows in a table: .. literalinclude:: doctest/tags-anytag-tr :language: pycon .. _tag-escaping: Escaping generated content ========================== Whenever template execution results in evaluated data being written, any characters that have special meaning in HTML are escaped to protect against cross-site scripting attacks [#]_: ==== ====== From To ==== ====== & & < < > > " " ' ' ==== ====== For example: .. literalinclude:: doctest/tags-escape :language: pycon You may occasionally have valid reasons to want to write content without escaping. In these cases you can use ``htmlsafe()`` to signal that the content has already been rendered safe for HTML use: .. literalinclude:: doctest/tags-escape-safe :language: pycon ``htmlsafe()`` is a simple sub-class of the `str()` type, used to signal that escaping is not necessary; it has no methods of it's own, and performing operations such as string concatenation on it will result in a regular `str()`. An alternative mechanism is to use the ``noescape`` attribute: .. literalinclude:: doctest/tags-escape-noescape :language: pycon One potential pitfall to ``noescape`` is that the flag is separated from the code that generated the data and the security implications are not immediately apparent to anyone editing the code. .. [#] `XSS or Cross-site scripting on Wikipedia `_ .. _tag-fakeapp: Fake Application Harness ======================== Some of the explanatory examples in this chapter require application functionality. The following fake application is used as an execution harness to drive the interactive examples. .. literalinclude:: fakeapp.py :language: python .. _tag-enhhtml: Enhanced HTML Tags ================== Tags in this section are used in place of standard HTML tags to access values from the execution context. All attributes that are not recognised by Albatross are passed without modification to the generated HTML. .. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% .. % .. _tag-form: ```` ------------- Albatross browser request merging depends upon the functionality provided by the ```` tag. If you do no use this tag in applications then the standard request merging will not work. The tag will automatically generate the HTML ``
`` ``action`` (:ref:`tag-form-action`) and ``enctype`` (:ref:`tag-form-enctype`) attributes if they are not supplied in the template. If you are using an execution context that inherits from the :class:`NameRecorderMixin` (nearly all do --- see chapter :ref:`pack-overview`) then the execution context will raise a :exc:`ApplicationError` exception if multiple instances of some types of input tag with the same name are added to a form. The ``list`` attribute of the ```` tag is used indicate that multiple instances are intentional. .. _tag-form-action: ``action="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^^^ If you do not supply an ``action`` attribute the tag will generate one with based on the value returned by the :meth:`current_url` method of the execution context. .. literalinclude:: doctest/tags-form-action :language: pycon Note that the generated ``action`` attribute is relative to the document root. .. _tag-form-enctype: ``enctype="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you include any file input fields then the open tag will automatically supply an ``enctype="multipart/form-data"`` attribute. .. literalinclude:: doctest/tags-form-file :language: pycon .. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% .. % .. _tag-input: ```` -------------- Albatross browser request merging depends upon the functionality provided by the ```` tag. If you do no use this tag in applications then the standard request merging will not work. In the most common usage a ``value`` (:ref:`tag-input-value`) attribute is generated by evaluating the ``name`` (:ref:`tag-input-name`) attribute in the execution context. This can be overridden by supplying a ``value`` (:ref:`tag-input-value`) or ``expr`` (:ref:`tag-input-expr`) attribute. There are a number of attributes that automatically generate the ``name`` attribute. When merging browser requests the application object places the browser supplied value back into the execution context value referenced by the ``name`` attribute. The application request merging will not merge variable names prefixed by underscore. Use this to protect application values from browser modification. .. _tag-input-alias: ``alias="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^^ This attribute is ignored is any of the following attributes are present; ``prevpage``, ``nextpage`` (:ref:`tag-input-page`), ``treefold``, ``treeselect``, or ``treeellipsis`` (:ref:`tag-input-tree`). The value of the ``alias`` attribute is passed to the :meth:`make_alias` method of the execution context. The return value is then used as the generated ``name`` (:ref:`tag-input-name`) attribute. The execution context :meth:`make_alias` method splits the ``alias`` attribute at the last '.' and resolves the left hand side to an object reference. The :meth:`albatross_alias` method is then called on that object and the result is combined with the '.' and the right hand side of the of the ``alias`` attribute to produce the generated ``name`` attribute. The resolved object is entered in the the local namespace and the session using the name returned by the :meth:`albatross_alias` method. The template ``samples/tree/tree1.html`` contains an example of this method for generating a ``name`` attribute. .. code-block:: albatross - Note that each node in the tree has a checkbox that controls whether or not the node is selected. When processing the ``alias`` attribute the toolkit isolates the left hand side (``n.value()``) which happens to be the current tree node of :class:`TreeIterator` ``n``. To generate the alias the :meth:`albatross_alias` method of the current node is called. In ``samples/tree/tree1.py`` the implementation of that method looks like: .. code-block:: python class Node: def albatross_alias(self): return 'node%d' % self.node_num When the template is executed a unique name is generated for each checkbox. The exact HTML produced by the above fragment from the sample looks like this: .. code-block:: html -a |-a | |-a | \-b \-b |-a | \-a | |-a | \-b |-b \-c |-a \-b The ``alias`` handling uses the fact that all Python objects are stored by reference. It obtains a reference to an existing object by resolving an expression and stores that reference under a new name. Since both the original expression and the new name are the same reference, the toolkit can modify the object referenced by the original expression by using the new name. Looking further into the ``samples/tree/tree1.py`` code you will note that the tree being iterated is generated once and placed into the session. This ensures that the alias names generated always contain references to the nodes in the tree. If the tree was not entered into the session but was generated from scratch every request, the nodes referenced in the alias names would not be the same nodes as those in the tree so all input would be lost. .. _tag-input-checked: ``checked`` attribute ^^^^^^^^^^^^^^^^^^^^^ This attribute is generated in ``radio`` and ``checkbox`` input field types if the generated ``value`` (:ref:`tag-input-value`) attribute matches the comparison value from ``valueexpr`` (:ref:`tag-input-valueexpr`) (or the literal ``'on'`` for the ``checkbox`` input field type). Refer to the documentation for individual input types for more details. .. _tag-input-expr: ``expr="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^ For ``text``, ``password``, ``submit``, ``reset``, ``hidden``, and ``button`` input field types the expression in the ``expr`` attribute is evaluated and the result is used to generate the ``value`` (:ref:`tag-input-value`) attribute. If the result is ``None`` then no ``value`` attribute will be written. For ``radio`` and ``checkbox`` input field types the expression in the ``expr`` attribute is evaluated and the result is compared with the generated ``value`` attribute to determine whether or not the ``checked`` (:ref:`tag-input-checked`) attribute should be written. Refer to the documentation for individual input types for more details. .. _tag-input-list: ``list`` attribute ^^^^^^^^^^^^^^^^^^ If you are using an execution context that inherits from the :class:`NameRecorderMixin` (nearly all do --- see chapter :ref:`pack-overview`) then the execution context will raise a :exc:`ApplicationError` exception if multiple instances of some types of input tag with the same name are added to a form. The ``list`` attribute of the ```` tag is used indicate that multiple instances are intentional. The presence of the ``list`` attribute on an ```` tag makes the request merging in the :class:`NameRecorderMixin` class place any browser request values for the field into a list (field not present is represented by the empty list). The attribute must not be used for input field types ``radio``, ``submit``, and ``image``. The attribute is ignored for the ``file`` input field type. .. _tag-input-name: ``name="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^ When determining the generated ``name`` attribute the tag looks for a number of attributes. Any supplied ``name`` attribute will be ignored if any of the following attributes are present; ``prevpage``, ``nextpage`` (:ref:`tag-input-page`), ``treefold``, ``treeselect``, ``treeellipsis`` (:ref:`tag-input-tree`), ``alias`` (:ref:`tag-input-alias`), or ``nameexpr`` (:ref:`tag-input-nameexpr`). All of the attributes that automatically generate names and are looked up in the above sequence. The first of those attributes found will be used to determine the name used in the tag. .. _tag-input-nameexpr: ``nameexpr="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This attribute is ignored if any of the following attributes are present; ``prevpage``, ``nextpage`` (:ref:`tag-input-page`), ``treefold``, ``treeselect``, ``treeellipsis`` (:ref:`tag-input-tree`), or ``alias`` (:ref :`tag-input-alias`). The expression in the value of the ``nameexpr`` attribute is evaluated to determine the generated ``name`` (:ref:`tag-input-name`) attribute. One shortcoming of the ``alias`` attribute is that you can only perform input on object attributes. The ``nameexpr`` enables you to perform input on list elements. .. literalinclude:: doctest/tags-input-nameexpr :language: pycon When the browser request is merged into the execution context the names elements of the ``names`` list will be replaced. .. _tag-input-node: ``node="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^ The ``node`` attribute is used in conjunction with the ``treeselect``, ``treefold`` and ``treeellipsis`` (:ref:`tag-input-tree`) attributes. It is ignored otherwise. When this attribute is present the node identified by evaluating the expression in the attribute value will be used when generating the ``name`` attribute. The ``name`` (:ref:`tag-input-name`) attribute is generated as follows: .. literalinclude:: doctest/tags-input-treeselect-node :language: pycon Refer to the documentation for ``treeselect``, ``treefold`` and ``treeellipsis`` (:ref:`tag-input-tree`) attributes for more information. .. _tag-input-page: ``prevpage="..."`` and ``nextpage="..."`` attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``prevpage`` and ``nextpage`` attributes respectively select the previous and next pages of an ```` :class:`ListIterator` (:ref:`tag-for-listiter`). An attribute value must be supplied that specifies the name of the iterator. The ``name`` (:ref:`tag-input-name`) attribute is generated as follows: .. literalinclude:: doctest/tags-input-nextpage :language: pycon When merging the browser request the :meth:`NamespaceMixin.set_value` (:ref :`mixin-namespace`) method looks for field names that contain commas. These names are split into *operation*, *iterator*, and optional *value* then the :meth:`set_backdoor` method of the identified iterator is invoked. During request merging the above example will execute code equivalent to the following. :: ctx.locals.i.set_backdoor('nextpage', 'nextpage,i') .. _tag-input-tree: ``treeselect="..."``, ``treefold="..."`` and ``treeellipsis="..."`` attributes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The ``treeselect``, ``treefold``, and ``treeellipsis`` attributes respectively select, open/close, or expand the ellipsis of an ```` node via a :class:`LazyTreeIterator` (:ref:`tag-tree-lazytreeiter`) or :class:`EllipsisTreeIterator` (:ref:`tag-tree-ellipsistreeiter`) iterator. These attributes are ignored if any of the following attributes are present; ``prevpage``, or ``nextpage`` (:ref:`tag-input-page`). An attribute value must be supplied that specifies the name of the :class:`LazyTreeIterator` iterator. The ``name`` (:ref:`tag-input-name`) attribute is generated as follows: .. literalinclude:: doctest/tags-input-treeselect :language: pycon When merging the browser request the :meth:`NamespaceMixin.set_value` (:ref :`mixin-namespace`) method looks for field names that contain commas. These names are split into *operation*, *iterator*, and optional *value* then the :meth:`set_backdoor` method of the identified iterator is invoked. During request merging the above example will execute code equivalent to the following. :: ctx.locals.n.set_backdoor('treeselect', 'ino81489', 'treeselect,n,ino81489') The "ino81489" string is generated by calling the :meth:`albatross_alias` method for the tree node. If the ``node``\ (:ref:`tag-input-node`) attribute is not specified, the node referenced by the current value of the iterator will be used to determine the alias. .. _tag-input-value: ``value="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^^ When determining the generated ``value`` attribute the tag looks for a number of attributes. Any supplied ``value`` attribute will be ignored if either ``expr`` (:ref:`tag-input-expr`) or ``valueexpr`` (:ref:`tag-input-valueexpr`) attributes (depending upon input type) present. If no ``expr``, ``valueexpr``, or ``value`` attribute is present then the value identified by the generated ``name`` (:ref:`tag-input-name`) attribute will be taken from the local namespace. If this value is ``None`` then no ``value`` attribute will be written. The name used to look into the local namespace is the result of evaluating all ``name`` related attributes. For input field types ``radio`` and ``checkbox`` the ``valueexpr`` attribute if present takes priority over and specified ``value`` attribute. Refer to the documentation for individual input types for more details. .. _tag-input-valueexpr: ``valueexpr="..."`` attribute ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This attribute is used to generate a ``value`` (:ref:`tag-input-value`) attribute for ``radio`` and ``checkbox`` input field types. It is ignored for in all other cases. Refer to the documentation for individual input types for more details. .. _tag-input-generic: ``type="..."`` attribute (text, password, submit, reset, hidden, button) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The tag determines the generated ``name`` (:ref:`tag-input-name`) from the ``name`` related attributes. To determine the generated ``value`` (:ref:`tag-input-value`) attribute the tag first looks for an ``expr`` (:ref:`tag-input-expr`) attribute, then a ``value`` attribute, and then if that fails it looks up the generated ``name`` in the execution context. If the generated ``name`` contains a non-empty value it will be written as the ``name`` attribute. If the generated ``value`` is not ``None`` it will be escaped and written as the ``value`` attribute. Escaping values makes all ``&``, ``<``, ``>``, and ``"`` characters safe. For example: .. literalinclude:: doctest/tags-input-generic :language: pycon After writing all tag attributes the execution context :meth:`input_add` method is called with the following arguments; input field type (``'text'``, ``'password``, ``'submit``, ``'reset``, ``'hidden``, or ``'button'``), the generated ``name``, the generated ``value``, and a flag indicating whether or not the ``list`` (:ref:`tag-input-list`) attribute was present. Application code handling browser requests typically determines the ``submit`` input pressed by the user via the execution context :meth:`req_equals` method. The :meth:`req_equals` method simply tests that the named input is present in the browser request and contains a non-empty value. For example:: def page_process(ctx): if ctx.req_equals('login'): user = process_login(ctx.locals.username, ctx.locals.passwd) if user: ctx.locals._user = user ctx.add_session_vars('_user') ctx.set_page('home') .. _tag-input-radio: ``type="..."`` attribute (radio) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The tag determines the generated ``name`` (:ref:`tag-input-name`) from the ``name`` related attributes. Then an internal comparison value is determined by evaluating the ``expr`` (:ref:`tag-input-expr`) attribute if it is present, or by looking up the generated ``name`` in the execution context. If the comparison value equals the generated ``value`` (:ref:`tag-input-value`) attribute then the ``checked`` (:ref:`tag-input-checked`) attribute is written. Both values are converted to string before being compared. To determine the generated ``value`` attribute the tag first looks for a ``valueexpr`` (:ref:`tag-input-valueexpr`) attribute, then a ``value`` attribute. For example: .. literalinclude:: doctest/tags-input-radio1 :language: pycon The ``expr`` attribute can be used to generate the internal comparison value. This is then compared with the ``value`` attribute to control the state of the ``checked`` attribute. For example: .. literalinclude:: doctest/tags-input-radio2 :language: pycon The ``valueexpr`` attribute can be used to dynamically generate the ``value`` attribute. For example: .. literalinclude:: doctest/tags-input-radio3 :language: pycon After writing all tag attributes the execution context :meth:`input_add` method is called with the arguments; input field type (``'radio'``), the generated ``name``, the generated ``value``, and a flag indicating whether or not the ``list`` (:ref:`tag-input-list`) attribute was present. .. _tag-input-checkbox: ``type="..."`` attribute (checkbox) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The tag determines the generated ``name`` (:ref:`tag-input-name`) from the ``name`` related attributes. Then an internal comparison value is determined by evaluating the ``expr`` (:ref:`tag-input-expr`) attribute if it is present, or by looking up the generated ``name`` in the execution context. If the comparison value equals the generated ``value`` (:ref:`tag-input-value`) attribute then the ``checked`` (:ref:`tag-input-checked`) attribute is written. Both values are converted to string before being compared. If the internal comparison value is either a list or tuple the ``checked`` attribute is written if the generated ``value`` attribute is present in the list/tuple. To determine the generated ``value`` attribute the tag first looks for a ``valueexpr`` (:ref:`tag-input-valueexpr`) attribute, then a ``value`` attribute, and then if that fails it defaults to the value ``'on'``. For example: .. literalinclude:: doctest/tags-input-checkbox :language: pycon After writing all tag attributes the execution context :meth:`input_add` method is called with the arguments; input field type (``'checkbox'``), the generated ``name``, the generated ``value``, and a flag indicating whether or not the ``list`` (:ref:`tag-input-list`) attribute was present. .. _tag-input-image: ``type="..."`` attribute (image) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The tag determines the generated ``name`` (:ref:`tag-input-name`) from the ``name`` related attributes. For example: .. literalinclude:: doctest/tags-input-image :language: pycon After writing all tag attributes the execution context :meth:`input_add` method is called with the arguments; input field type (``'image'``), the generated ``name``, ``None``, and a flag indicating whether or not the ``list`` (:ref :`tag-input-list`) attribute was present. When a browser submits input to an ``image`` input it sends an ``x`` and ``y`` value for the field. These are saved as attributes of the field. For example, if an image input named ``map`` was clicked by the user, then the code to detect and process the input would look something like this:: def page_process(ctx): if ctx.req_equals('map'): map_clicked_at(ctx.locals.map.x, ctx.locals.map.y) .. _tag-input-file: ``type="..."`` attribute (file) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The tag determines the generated ``name`` (:ref:`tag-input-name`) from the ``name`` related attributes. If you are using an execution context that inherits from the :class:`NameRecorderMixin` (:ref:`mixin-recorder`) then using this input field type will automatically cause the enclosing ```` (:ref:`tag-form`) tag to include an ``enctype="multipart/form-data"`` (:ref:`tag-form-enctype`) attribute. For example: .. literalinclude:: doctest/tags-input-file :language: pycon After writing all tag attributes the execution context :meth:`input_add` method is called with the arguments; input field type (``'file'``), the generated ``name``, ``None``, and a flag indicating whether or not the ``list`` (:ref :`tag-input-list`) attribute was present. The request merging allows the user to submit more than one file in a ``file`` input field. To simplify application code the :class:`Request` always returns a list of :class:`FileField` objects for ``file`` inputs. Application code to process ``file`` inputs typically looks like the following:: def page_process(ctx): if ctx.req_equals('resume'): for r in ctx.locals.resume: if r.filename: save_uploaded_resume(r.filename, r.file.read()) .. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% .. % .. _tag-select: ```` --------------- Albatross browser request merging depends upon the functionality provided by the ```` tag. If you do no use this tag in applications then the standard request merging will not work. To determine the ``name`` (:ref:`tag-select-name`) attribute in the generated tag a number of attributes are used. The generated name is evaluated in the execution context to determine an internal compare value. The compare value is used to control which option tags are generated with the ``selected`` (:ref:`tag-option-selected`) attribute. ``