src/pyams_utils/tales.py
changeset 72 9049384a2bd4
parent 66 1e9c6d17203e
child 74 d5fa9579f35d
--- a/src/pyams_utils/tales.py	Tue Nov 15 10:43:55 2016 +0100
+++ b/src/pyams_utils/tales.py	Fri Nov 18 15:28:54 2016 +0100
@@ -27,7 +27,7 @@
 
 
 class ContextExprMixin(object):
-    """Mixin-class for expression compilers."""
+    """Mixin-class for expression compilers"""
 
     transform = None
 
@@ -44,22 +44,92 @@
 ARGUMENTS_EXPRESSION = re.compile('[^(,)]+')
 
 
-def get_value(econtext, arg):
-    """Extract argument value from context"""
-    arg = arg.strip()
-    if arg.startswith('"') or arg.startswith("'"):
-        # may be a quoted string...
-        return arg[1:-1]
-    args = arg.split('.')
-    result = econtext.get(args.pop(0))
-    for arg in args:
-        result = getattr(result, arg)
-    return result
+def render_extension(econtext, name):
+    """TALES extension renderer
+
+    This renderer can be used to render an *extension:* TALES expression.
+    When this expression is encountered, the renderer is looking for an
+    :py:class:`ITALESExtension <pyams_utils.interfaces.tales.ITALESExtension>`
+    multi-adapter for the current *context*, *request* and *view*, for the current
+    *context* and *request*, or only for the current *context*, in this order.
+    If an adapter is found, the renderer call it's :py:func:`render` method with
+    the expression parameters as input parameters.
+
+    For example, the *metas* extension is an *ITALESExtension* adapter defined into
+    :py:mod:`pyams_skin.metas` module which can be used to include all required headers in
+    a page template. Extension is used like this in the page layout template:
+
+    .. code-block:: html
+
+        <tal:var replace="structure extension:metas" />
+
+    This extension is defined like this:
+
+    .. code-block:: python
+
+        from pyams_skin.interfaces.metas import IHTMLContentMetas
+        from pyams_utils.interfaces.tales import ITALESExtension
+        from pyramid.interfaces import IRequest
+
+        from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+
+        @adapter_config(name='metas', context=(Interface, IRequest, Interface), provides=ITALESExtension)
+        class MetasTalesExtension(ContextRequestViewAdapter):
+            '''extension:metas TALES extension'''
+
+            def render(self, context=None):
+                if context is None:
+                    context = self.context
+                result = []
+                for name, adapter in sorted(self.request.registry.getAdapters((context, self.request, self.view),
+                                                                              IHTMLContentMetas),
+                                            key=lambda x: getattr(x[1], 'order', 9999)):
+                    result.extend([meta.render() for meta in adapter.get_metas()])
+                return '\n\t'.join(result)
 
+    Some TALES extension can require or accept arguments. For example, the *absolute_url* extension can accept
+    a context and a view name:
 
-def render_extension(econtext, name):
+    .. code-block:: html
+
+        <tal:var define="logo config.logo">
+            <img tal:attributes="src extension:absolute_url(logo, '++thumb++200x36.png');" />
+        </tal:var>
+
+    The extension is defined like this:
+
+    .. code-block:: python
+
+        from persistent.interfaces import IPersistent
+        from pyams_utils.interfaces.tales import ITALESExtension
+
+        from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter
+        from pyramid.url import resource_url
+        from zope.interface import Interface
+
+        @adapter_config(name='absolute_url', context=(IPersistent, Interface, Interface), provides=ITALESExtension)
+        class AbsoluteUrlTalesExtension(ContextRequestViewAdapter):
+            '''extension:absolute_url(context, view_name) TALES extension'''
+
+            def render(self, context=None, view_name=None):
+                if context is None:
+                    context = self.context
+                return resource_url(context, self.request, view_name)
+    """
+
+    def get_value(econtext, arg):
+        """Extract argument value from context"""
+        arg = arg.strip()
+        if arg.startswith('"') or arg.startswith("'"):
+            # may be a quoted string...
+            return arg[1:-1]
+        args = arg.split('.')
+        result = econtext.get(args.pop(0))
+        for arg in args:
+            result = getattr(result, arg)
+        return result
+
     name = name.strip()
-
     context = econtext.get('context')
     request = econtext.get('request')
     view = econtext.get('view')