Updated internals and custom skin doc-dc
authorDamien Correia
Fri, 21 Dec 2018 15:20:06 +0100
branchdoc-dc
changeset 139 d632f8d6140b
parent 138 7205ae7c43dc
child 140 df3106def670
Updated internals and custom skin
src/source/conf.py
src/source/dev_guide/custom-skin.rst
src/source/dev_guide/howto-renderer.rst
src/source/dev_guide/howto-skin.rst
src/source/dev_guide/internals.rst
src/source/dev_guide/tales.rst
--- a/src/source/conf.py	Thu Dec 20 17:59:43 2018 +0100
+++ b/src/source/conf.py	Fri Dec 21 15:20:06 2018 +0100
@@ -137,7 +137,7 @@
 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['source/_static', ]
+html_static_path = ['_static', ]
 
 # Custom sidebar templates, must be a dictionary that maps document names
 # to template names.
@@ -202,7 +202,6 @@
 ]
 
 
-
 # -- Options for Epub output ----------------------------------------------
 
 # Bibliographic Dublin Core info.
--- a/src/source/dev_guide/custom-skin.rst	Thu Dec 20 17:59:43 2018 +0100
+++ b/src/source/dev_guide/custom-skin.rst	Fri Dec 21 15:20:06 2018 +0100
@@ -6,14 +6,204 @@
 Understading layers and skins
 -----------------------------
 
+
+.. _skinhowto:
+
 Creating a new skin
----------------------
+-------------------
+
+A Skin is a tagging interface for associating media, javascript and CSS resources to a **renderer**
+
+
+1) Create a new Layer to your skin
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Build a new interface inherit from `ICustomLayer`
+
+.. code-block:: python
+
+    class ICustomLayer(ICustomLayer):
+        """skin layer"""
+
+Define an utility providing ISkin with the custom label and the layer interface
+
+.. code-block:: python
+
+    @utility_config(name='Custom skin', provides=ISkin)
+    class CustomSkin(object):
+        """custom root skin"""
+
+        label = _("Custom: skin")
+        layer = ICustomLayer
+
+
+2) Declare the layer adapter
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+    @adapter_config(context=(Interface, ICustomLayer, Interface), provides=IResources)
+    class CustomSkinResourcesAdapter(ContextRequestViewAdapter):
+    """Custom skin resources adapter"""
 
-Adding resources
-----------------
+        def get_resources(self):
+            mycms.need()
+
+
+We have defined a Multiadapter with context=(context, request, view).
+
+.. note::
+
+    In the ZMI website you can now change the default graphical theme by you custom skin
+
+    .. image:: ../_static/select_skin.png
+
+
+
+
+Adding resources library
+------------------------
+
+
+.. code-block:: python
+
+    from fanstatic import Library, Resource
+    from pyams_default_theme import pyams_default_theme
+
+    #Library(name, folder_path)
+    library = Library('mycms', 'resources')
+
+    #Resource(library, path_to_static)
+    mycms_css = Resource(library, 'css/mystyle.css',)
+
+
+    mycms_js = Resource(library, 'js/pyams-default.js',
+                        depends=(pyams_default_theme, )
+                        bottom=True
+                        )
+
+
+:py:class:`Resource` can include others resources already defined with *depends* attribute, here `pyams_default_theme`.
+
 
 Overriding templates
----------------------
+--------------------
+
+The simplest is to create a new class that inherits from the existing **Renderer**  and modify this template.
+After that all you have to define a new adapter name and a new label.
+
+
+.. code-block:: python
+    :linenos:
+
+    # New custom contact paragraph renderer
+
+    @adapter_config(name='custom', context=(IContactParagraph, IPyAMSLayer), provides=ISharedContentRenderer)
+    @template_config(template='templates/contact-custom.pt', layer=IPyAMSLayer)
+    class ContactParagraphCustomRenderer(ContactParagraphDefaultRenderer):
+        """Context paragraph custom renderer"""
+
+        label = _("Custom contact renderer")
+        #settings_interface = IContactParagraphDefaultRendererSettings
+
+
+In this example, we have defined an adapter named 'custom' with :py:class:`IContactParagraph`,
+:py:class:`IPyAMSLayer` as context and provides :py:class:`ISharedContentRenderer` interface.
+
+Using ``@template_config()`` decorator, the renderer will be displayed in html container according to the template
+`templates/contact-custom.pt`
+
+The new renderer inherit of :py:class:`ContactParagraphDefaultRenderer`, have a new **label** (line 8)
+and this associated **settings_interface** is not modify(line 9)
+
+.. tip::
+
+    You can override the template of a renderer easily with the function :py:func:`pyams_template.template.override_template`
+    It's takes the context and the new template path as params.
+
+
 
 Creating custom renderer
 ------------------------
+
+We can define a new settings for the renderer, to do that we start by creating an interface:
+
+
+a) Create setting interface for the renderer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+    class IPhotoRendererSettings(Interface):
+        """Custom renderer settings interface"""
+
+
+        display_photo = Bool(title=_("Show photo?"),
+                             description=_("Display contact photo"),
+                             required=True,
+                             default=True)
+
+        can_display_photo = Attribute("Check if photo can be displayed")
+
+
+We have created an interface with two attributes *display_photo* and *can_display_photo*
+Then we create an implemantation of the interface
+
+.. code-block:: python
+
+    @implementer(IPhotoRendererSettings)
+    class PhotoRendererSettings(Persistent, Location):
+        """Custom renderer settings"""
+
+        display_photo = FieldProperty(IPhotoRendererSettings['display_photo'])
+
+        @property
+        def can_display_photo(self):
+            contact = IContactParagraph(self.__parent__)
+            if not contact.photo:
+                return False
+            return self.display_photo
+
+
+
+b) Create an adapter for the render setting interface
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+With :py:func:`@adapter_config()` we declare a new adapter that applies to a context and provide the interface of
+ renderer settings
+
+.. code-block:: python
+
+    PHOTO_RENDERER_SETTINGS_KEY = 'pyams_content.contact.renderer:photo'
+
+    @adapter_config(context=IContactParagraph, provides=IPhotoRendererSettings)
+    def custom_renderer_settings_factory(context):
+        """Contact paragraph default renderer settings factory"""
+        return get_annotation_adapter(context, PHOTO_RENDERER_SETTINGS_KEY,
+                                      CustomRendererSettings)
+
+
+In this example the settings interface adapter is defined with `IContactParagraph` as context
+and provide `IPhotoRendererSettings`.
+
+
+
+c) Create an adapter for the render interface
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+.. code-block:: python
+
+    @adapter_config(context=(IContactParagraph, IPyAMSLayer), provides=ISharedContentRenderer)
+    @template_config(template='templates/contact-custom.pt', layer=IPyAMSLayer)
+    class PhotoRenderer(BaseContentRenderer):
+        """Context paragraph custom renderer"""
+
+        label = _("Custom contact renderer")
+        settings_interface = IPhotoRendererSettings
+
+
+Add settings interface to the renderer `settings_interface = IPhotoRendererSettings`
+
+.. tip::
+    When a setting_interface is associated to a renderer, you can access to `settings` attributes through the template
+
--- a/src/source/dev_guide/howto-renderer.rst	Thu Dec 20 17:59:43 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,135 +0,0 @@
-.. _rendererhowto:
-
-
-How to create a Renderer?
-=========================
-
-
-**Renderer** are the layout of the utility data content. A renderer combine un context, a skin and
-a template to produce the front office html
-
- To create new renderer you can override an already exist renderer or create a new one from scratch. Steps below
- we will create a renderer for a `IContact` paragraph
-
-
-1. Override a Renderer
-----------------------
-
-The simplest is to create a new class that inherits from the existing **Renderer**  and modify this template.
-After that all you have to define a new adapter name and a new label.
-
-
-.. code-block:: python
-    :linenos:
-
-    # New custom contact paragraph renderer
-
-    @adapter_config(name='custom', context=(IContactParagraph, IPyAMSLayer), provides=ISharedContentRenderer)
-    @template_config(template='templates/contact-custom.pt', layer=IPyAMSLayer)
-    class ContactParagraphCustomRenderer(ContactParagraphDefaultRenderer):
-        """Context paragraph custom renderer"""
-
-        label = _("Custom contact renderer")
-        #settings_interface = IContactParagraphDefaultRendererSettings
-
-
-In this example, we have defined an adapter named 'custom' with :py:class:`IContactParagraph`,
-:py:class:`IPyAMSLayer` as context and provides :py:class:`ISharedContentRenderer` interface.
-
-Using ``@template_config()`` decorator, the renderer will be displayed in html container according to the template
-`templates/contact-custom.pt`
-
-The new renderer inherit of :py:class:`ContactParagraphDefaultRenderer`, have a new **label** (line 8)
-and this associated **settings_interface** is not modify(line 9)
-
-.. tip::
-
-    You can override the template of a renderer easily with the function :py:func:`pyams_template.template.override_template`
-    It's takes the context and the new template path as params.
-
-
-
-2. Create a new Renderer from scratch
--------------------------------------
-
-We can define a new settings for the renderer, to do that we start by creating an interface:
-
-
-a) Create setting interface for the renderer
-""""""""""""""""""""""""""""""""""""""""""""
-
-.. code-block:: python
-
-    class IPhotoRendererSettings(Interface):
-        """Custom renderer settings interface"""
-
-
-        display_photo = Bool(title=_("Show photo?"),
-                             description=_("Display contact photo"),
-                             required=True,
-                             default=True)
-
-        can_display_photo = Attribute("Check if photo can be displayed")
-
-
-We have created an interface with two attributes *display_photo* and *can_display_photo*
-Then we create an implemantation of the interface
-
-.. code-block:: python
-
-    @implementer(IPhotoRendererSettings)
-    class PhotoRendererSettings(Persistent, Location):
-        """Custom renderer settings"""
-
-        display_photo = FieldProperty(IPhotoRendererSettings['display_photo'])
-
-        @property
-        def can_display_photo(self):
-            contact = IContactParagraph(self.__parent__)
-            if not contact.photo:
-                return False
-            return self.display_photo
-
-
-
-b) Create an adapter for the render setting interface
-"""""""""""""""""""""""""""""""""""""""""""""""""""""
-
-With :py:func:`@adapter_config()` we declare a new adapter that applies to a context and provide the interface of
- renderer settings
-
-.. code-block:: python
-
-    PHOTO_RENDERER_SETTINGS_KEY = 'pyams_content.contact.renderer:photo'
-
-    @adapter_config(context=IContactParagraph, provides=IPhotoRendererSettings)
-    def custom_renderer_settings_factory(context):
-        """Contact paragraph default renderer settings factory"""
-        return get_annotation_adapter(context, PHOTO_RENDERER_SETTINGS_KEY,
-                                      CustomRendererSettings)
-
-
-In this example the settings interface adapter is defined with `IContactParagraph` as context
-and provide `IPhotoRendererSettings`.
-
-
-
-c) Create an adapter for the render interface
-"""""""""""""""""""""""""""""""""""""""""""""
-
-.. code-block:: python
-
-    @adapter_config(context=(IContactParagraph, IPyAMSLayer), provides=ISharedContentRenderer)
-    @template_config(template='templates/contact-custom.pt', layer=IPyAMSLayer)
-    class PhotoRenderer(BaseContentRenderer):
-        """Context paragraph custom renderer"""
-
-        label = _("Custom contact renderer")
-        settings_interface = IPhotoRendererSettings
-
-
-Add settings interface to the renderer `settings_interface = IPhotoRendererSettings`
-
-.. tip::
-    When a setting_interface is associated to a renderer, you can access to `settings` attributes through the template
-
--- a/src/source/dev_guide/howto-skin.rst	Thu Dec 20 17:59:43 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,75 +0,0 @@
-.. _skinhowto:
-
-
-How to create Skin?
-===================
-
-A Skin is a tagging interface for associating media, javascript and CSS resources to a **renderer**
-
-1) Configuring resource library
--------------------------------
-
-
-.. code-block:: python
-
-    from fanstatic import Library, Resource
-    from pyams_default_theme import pyams_default_theme
-
-    #Library(name, folder_path)
-    library = Library('mycms', 'resources')
-
-    #Resource(library, path_to_static)
-    mycms_css = Resource(library, 'css/mystyle.css',)
-
-
-    mycms_js = Resource(library, 'js/pyams-default.js',
-                        depends=(pyams_default_theme, )
-                        bottom=True
-                        )
-
-
-:py:class:`Resource` can include others resources already defined with *depends* attribute, here `pyams_default_theme`.
-
-
-2) Create a new Layer to your skin
-----------------------------------
-
-Build a new interface inherit from `ICustomLayer`
-
-.. code-block:: python
-
-    class ICustomLayer(ICustomLayer):
-        """skin layer"""
-
-Define an utility providing ISkin with the custom label and the layer interface
-
-.. code-block:: python
-
-    @utility_config(name='Custom skin', provides=ISkin)
-    class CustomSkin(object):
-        """custom root skin"""
-
-        label = _("Custom: skin")
-        layer = ICustomLayer
-
-
-3) Declare the layer adapter
-----------------------------
-
-.. code-block:: python
-
-    @adapter_config(context=(Interface, ICustomLayer, Interface), provides=IResources)
-    class CustomSkinResourcesAdapter(ContextRequestViewAdapter):
-    """Custom skin resources adapter"""
-
-        def get_resources(self):
-            mycms.need()
-
-
-We have defined a Multiadapter with context=(context, request, view).
-
-.. note::
-
-    In the ZMI website you can now change the default graphical theme by you custom skin
-
-    .. image:: ../_static/select_skin.png
--- a/src/source/dev_guide/internals.rst	Thu Dec 20 17:59:43 2018 +0100
+++ b/src/source/dev_guide/internals.rst	Fri Dec 21 15:20:06 2018 +0100
@@ -3,6 +3,7 @@
 Understanding PyAMS internals
 =============================
 
+
 Annotations
 -----------
 
@@ -12,11 +13,334 @@
 Namespaces
 ----------
 
+
+.. _renderer:
+
+Renderers
+---------
+
+**Renderer** are the layout of the utility data content. A renderer combine un context, a skin and
+a template to produce the front office html
+
+ To create new renderer you can override an already exist renderer or create a new one from scratch. Steps below
+ we will create a renderer for a `IContact` paragraph
+
+
+HTML renderer
+-------------
+
+Chameleon
+^^^^^^^^^
+
+To generate html page with  dynamic content PyAMS renderer use Chameleon as a template engine
+
+Once *pyramid_chameleon* been activated **.pt** templates can be loaded either by looking names
+that would be found on the Chameleon search path.
+
+
+.. seealso::
+
+	https://chameleon.readthedocs.io/en/latest/
+	https://zope.readthedocs.io/en/latest/zope2book/ZPT.html
+
+
+
+PyAMS defines a custom expression for TALES called *extension*.
+
+This expression are used in the html template (.pt) to ease to display
+or manipulate content.
+
+
+.. _tales:
+
 TALES Extensions
 ----------------
 
-Renderers
----------
+PyAMS defines a custom expression for TALES called *extension*.
+
+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 extensions can require or accept arguments. For example, the *absolute_url* extension can accept
+a context and a view 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)
+
+
+.. _talesext:
+
+List of PyAMS TALES extensions
+------------------------------
+
+Reformat text
+^^^^^^^^^^^^^
+
+- br
+	*br*\(value, class) TALES expression
+
+This expression can be used to context a given character (‘|’ by default) into HTML breaks with given CSS class.
+
+For example:
+
+	${tales:br(value, 'hidden-xs')}
+
+If value is "Raining| cats and dogs", the output will be "Raining <br class='hidden-xs'> cats and dogs".
+
+----
+
+- html
+	*html*\(value)
+
+Replaces line breaks in plain text with appropriate HTML (<br>).
+
+If value is:
+
+| "Raining
+| cats
+| and
+| dogs"
+
+The output will be "Raining<br>cats<br>and<br>dogs".
+
+
+----
+
+- truncate
+	*truncate*\(value, length, max)
+
+Truncates a string if it is longer than the specified length of characters. Truncated strings will end with ellipsis sequence (“…”).
+
+
+For example:
+
+	${tales:truncate(value,9, 0)}
+
+If value is "Raining cats and dogs", the output will be "Raining...".
+
+	${tales:truncate(value,9, 1)}
+
+If value is "Raining cats and dogs", the output will be "Raining cats...".
+
+
+----
+
+- i18n
+	*i18n*\(value)
+
+Return sentence according to the context user’s language, the value is a dict.
+
+
+Utilities
+^^^^^^^^^
+
+- oid
+	*oid*(value)
+
+Return the **oid** of the value
+
+----
+
+- cache_key
+	*cache_key*\(value)
+
+Return an unique ID based of the component value
+
+----
+
+- timestamp
+
+Return incremental number based on the time past in second
+
+
+
+Media
+^^^^^
+
+
+- picture
+	*picture*\(value, lg_thumb='banner', md_thumb='banner', sm_thumb='banner',xs_thumb='banner', css_class='inner-header__img', alt=alt_title)
+
+Search the illustration associated with the value.
+Return the first illustration found, by order: the component definition, content illustration navigation  and content illustration
+
+[lg, md, sm, xs]*_thumb
+	- banner
+	- pano
+	- portrait
+	- square
+
+[lg, md, sm, xs]*_width
+	[1-12] boostrat column size
+
+css_class
+	add a css class to the container of this illustration (<picture>)
+
+img_class
+	add a css class to the <img> balise
+
+alt
+	alternative title
+
+
+----
+
+- thumbnails
+	*thumbnails*\(value)
+
+----
+
+- thumbnail
+	*thumbnail*\(value, (value, lg_thumb='banner', md_thumb='banner', sm_thumb='banner',xs_thumb='banner', css_class='inner-header__img', alt=alt_title)
+
+Search the image associated with the value.
+
+[lg, md, sm, xs]*_thumb
+	- banner
+	- pano
+	- portrait
+	- square
+
+[lg, md, sm, xs]*_width
+	[1-12] boostrat column size
+
+css_class
+	add a css class to the container of this illustration (<picture>)
+
+img_class
+	add a css class to the <img> balise
+
+alt
+	alternative title)
+
+----
+
+- conversions
+	*conversion*\(value)
+
+Return the list of conversion format supported by the value
+
+----
+
+- audio_type
+	*audio_type*\(value)
+
+Return the type of the audio media
+
+----
+
+- video_type*
+	*video_type*\(value)
+
+Return the type of the video media
+
+
+Find a resource
+^^^^^^^^^^^^^^^
+
+- absolute_url
+	*absolute_url*\(object)
+
+Used to get access to an object URL
+
+----
+
+- canonical_url
+	*canonical_url*\(context,request)
+
+Used to get access to an uniq object URL, based on current request display context
+
+----
+
+- relative_url
+	*relative_url*\(context, request)
+
+ Used to get an object's relative URL based on current request display context
+
+----
+
+- *resource_path*\(value)
+
+Generates an URL matching a given Fanstatic resource.
+Resource is given as a string made of package name as value (in dotted form) followed by a colon and by the resource name.
+
+----
+
+- *need_resource*\(value)
+
+
+This extension generates a call to Fanstatic resource.need() function to include given resource
+into generated HTML code.
+Resource is given as a string made of package name as value (in dotted form) followed by a colon and by the resource name.
+
+For example:
+
+.. code-block::
+
+	<tal:var define="tales:need_resource('pyams_content.zmi:pyams_content')" />
+
+
+----
+
 
 Sequences
 ---------
--- a/src/source/dev_guide/tales.rst	Thu Dec 20 17:59:43 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,74 +0,0 @@
-.. _tales:
-
-Custom TALES extensions
-=======================
-
-PyAMS defines a custom expression for TALES called *extension*.
-
-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 extensions can require or accept arguments. For example, the *absolute_url* extension can accept
-a context and a view 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)