Updated rst doc-dc
authorDamien Correia
Tue, 11 Dec 2018 17:00:42 +0100
branchdoc-dc
changeset 111 097b0c025eec
parent 110 d7dd088ed557
child 112 49e4432c0a1d
Updated rst
src/source/README.txt
src/source/_api-doc/README.txt
src/source/dev_guide/developerguide.rst
src/source/dev_guide/howto-adapter.rst
src/source/dev_guide/howto-content.rst
src/source/dev_guide/howto-form.rst
src/source/dev_guide/howto-i18n.rst
src/source/dev_guide/howto-paragraph.rst
src/source/dev_guide/howto-portlet.rst
src/source/dev_guide/howto-rename.rst
src/source/dev_guide/howto-renderer.rst
src/source/dev_guide/howto-skin.rst
src/source/dev_guide/howto-template.rst
src/source/dev_guide/introduction.rst
src/source/dev_guide/tales.rst
src/source/dev_guide/traverser.rst
src/source/dev_guide/utilities.rst
src/source/dev_guide/zca.rst
src/source/developer_guide/developerguide.rst
src/source/developer_guide/howto-adapter.rst
src/source/developer_guide/howto-content.rst
src/source/developer_guide/howto-form.rst
src/source/developer_guide/howto-i18n.rst
src/source/developer_guide/howto-paragraph.rst
src/source/developer_guide/howto-portlet.rst
src/source/developer_guide/howto-rename.rst
src/source/developer_guide/howto-renderer.rst
src/source/developer_guide/howto-skin.rst
src/source/developer_guide/howto-template.rst
src/source/developer_guide/introduction.rst
src/source/developer_guide/tales.rst
src/source/developer_guide/traverser.rst
src/source/developer_guide/utilities.rst
src/source/developer_guide/zca.rst
src/source/index.rst
src/source/ref_guide/package_layout.rst
src/source/ref_guide/packages.rst
src/source/ref_guide/tests.rst
src/source/reference_guide/package_layout.rst
src/source/reference_guide/packages.rst
src/source/reference_guide/tests.rst
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/README.txt	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,6 @@
+Don't edit _api-doc/ files manually
+Theses RST files are generated automatically using the source code and python docstring of packages.
+
+To generale api-doc run:
+
+./bin/pyams_autodoc
--- a/src/source/_api-doc/README.txt	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-Don't edit these files manually
-Theses RST files are generated automatically using the source code and python docstring of packages.
-
-To generale api-doc run:
-
-./bin/pyams_autodoc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/developerguide.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,54 @@
+.. _developerguide:
+
+
+Developer's Guide
+=================
+
+Introduction
+++++++++++++
+
+Pyams packages are...
+
+
+What is Pyramid?
+++++++++++++++++
+
+Pyramid is an open source Python web application framework. It is designed to make creating web applications easier.
+
+
+What is Zope?
++++++++++++++
+
+Zope 2 is a free and open-source, object-oriented web application server written in the Python programming language. The term ZOPE is an acronym for “Z Object Publishing Environment” (the Z doesn’t really mean anything in particular). However, nowadays ZOPE is simply written as Zope. It has three distinct audiences.
+
+
+This guide is intended to document Zope for the second audience, Developers, as defined above. If you fit more into the “user” audience defined above, you’ll probably want to start by reading The Zope Book .
+
+Throughout this guide, it is assumed that you know how to program in the Python programming language. Most of the examples in this guide will be in Python. There are a number of great resources and books for learning Python; the best online resource is the python.org web site and many books can be found on the shelves of your local bookstore.
+1.2. Organization of the book
+
+This book describes Zope’s services to the developer from a hands on, example-oriented standpoint. This book is not a complete reference to the Zope API, but rather a practical guide to applying Zope’s services to develop and deploy your own web applications. This book covers the following topics:
+
+Components and Interfaces
+    Zope use a component-centric development model. This chapter describes the component model in Zope and how Zope components are described through interfaces.
+Object Publishing
+    Developing applications for Zope involves more than just creating a component, that component must be publishable on the web. This chapter describes publication, and how your components need to be designed to be published.
+Zope Products
+    New Zope components are distributed and installed in packages called “Products”. This chapter explains Products in detail.
+Persistent Components
+    Zope provides a built-in, transparent Python object database called ZODB. This chapter describes how to create persistent components, and how they work in conjunction with the ZODB.
+Acquisition
+    Zope relies heavily on a dynamic technique called acquisition. This chapter explores acquisition thoroughly.
+Security
+    When your component is used by many different people through the web, security becomes a big concern. This chapter describes Zope’s security API and how you can use it to make security assertions about your object.
+Debugging and Testing
+    Zope has built in debugging and testing support. This chapter describes these facilities and how you can debug and test your components.
+
+
+.. toctree::
+   :maxdepth: 2
+
+   Package directory layout <package_layout>
+   appextend
+   traverser
+   tales
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-adapter.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,180 @@
+.. _adapterhowto:
+
+
+How to define an annotations adapter?
+=====================================
+
+Adapters are important concepts of ZCA and PyAMS framework. If you don't know what are adapters, see :ref:`zca`.
+
+
+What are annotations?
++++++++++++++++++++++
+
+When an adapter have to add persistent attributes to a persistent class, it can add these attributes directly into
+persistent instances. But this way can lead to conflicts when several adapters want to use the same attribute name for
+different kinds of information.
+
+Annotations are an elegant way to handle this use case: they are based on a BTree which is stored into a
+specific instance attribute (*__annotations__*). Any adapter can then use this dictionary to store it's own
+informations, using it's own namespace as dictionary key.
+
+ZODB browser allows you to display existing annotations:
+
+.. image:: _static/annotations-1.png
+
+This example displays several annotations, each using it's own namespace:
+
+.. image:: _static/annotations-2.png
+
+
+Designing interfaces
+++++++++++++++++++++
+
+The first step with ZCA is to design your interfaces.
+
+The are going to base our example on PyAMS_content 'paragraphs' component: a content class is marked as a
+*paragraphs container target*, a class that can store paragraphs. But the real storage of paragraphs is done by
+another *container* class:
+
+.. code-block:: python
+    :linenos:
+
+    from zope.annotation.interfaces import IAttributeAnnotatable
+    from zope.containers.constraints import containers, contains
+
+
+    class IBaseParagraph(Interface):
+        """Base paragraph interface"""
+
+        containers('.IParagraphContainer')
+
+
+    class IParagraphContainer(IOrderedContainer):
+        """Paragraphs container"""
+
+        contains(IBaseParagraph)
+
+
+    class IParagraphContainerTarget(IAttributeAnnotatable):
+        """Paragraphs container marker interface"""
+
+
+    PARAGRAPH_CONTAINER_KEY = 'pyams_content.paragraph'
+
+
+- line 5 to 8: :class:`IBaseParagraph` is the base interface for all paragraphs; constraint implies that paragraphs
+  can only be stored in a container implementing :class:`IParagraphContainer` interface.
+- line 11 to 14: :class:`IParagraphContainer` is the base interface for paragraphs containers; constraint implies that
+  such a container can only contain objects implementing :class:`IBaseParagraph` interface.
+- line 17 to 18: :class:`IParagraphContainerTarget` is only a *marker* interface which doesn't provide any method or
+  attribute; it only inherits from :class:`IAttributeAnnotatable`, which implies that classes implementing this
+  interface allows other classes to add informations as annotations through a dedicated *__annotations__* attribute.
+- line 21: this is the key which will be used to store our annotation.
+
+
+Creating persistent classes
++++++++++++++++++++++++++++
+
+The first step is to declare that a given content class can store paragraphs:
+
+.. code-block:: python
+    :linenos:
+
+    from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
+    from zope.interface import implementer
+
+    @implementer(IParagraphContainerTarget)
+    class WfNewsEvent(WfSharedContent):
+        """News event class"""
+
+Here we just say "Well, I'm a shared content, and I'm OK to store paragraphs!".
+
+So we can design the paragraphs container class. It's this class which will *really* store the paragraphs:
+
+.. code-block:: python
+    :linenos:
+
+    @implementer(IParagraphContainer)
+    class ParagraphContainer(BTreeOrderedContainer):
+        """Paragraphs container"""
+
+The paragraphs container class inherits from a :class:`BTreeOrderedContainer` and implements
+:class:`IParagraphContainer`.
+
+The last operation is to create the adapter, which is the *glue* between the *target* class and the paragraphs
+container:
+
+.. code-block:: python
+    :linenos:
+
+    from pyams_utils.adapter import adapter_config, get_annotation_adapter
+
+    @adapter_config(context=IParagraphContainerTarget, provides=IParagraphContainer)
+    def paragraph_container_factory(target):
+        """Paragraphs container factory"""
+        return get_annotation_adapter(target,
+                                      PARAGRAPH_CONTAINER_KEY,
+                                      ParagraphContainer,
+                                      name='++paras++')
+
+PyAMS provides a shortcut to create an annotation adapter in :func:`pyams_utils.adapter.get_annotation_adapter`.
+It's mandatory arguments are:
+
+- **context** (line 6): the context to which the adapter is applied
+- **key** (line 7): the string key used to access and store context's annotations
+- **factory** (line 8): if the requested annotation is missing, a new one is created using this factory (which can be a class or
+  a function)
+
+Optional arguments are:
+
+- **markers** (None by default): if set, should be a list of marker interfaces which will be assigned to object
+  created by the factory
+- **notify**: if *True* (default), an :class:`ObjectCreatedEvent` event is notified on object creation
+- **locate**: if *True* (default), context is set as *parent* of created object
+- **parent**: if *locate* is True and if *parent* is set, this is the object to which the new object should be *parented*
+  instead of initial context
+- **name** (None by default): some objects need to be traversed, especially when you have to be able to access them through an URL; this
+  is the name given to created object.
+
+
+Using your adapter
+++++++++++++++++++
+
+Starting from your *content* object, it's then very simple to access to the paragraphs container:
+
+.. code-block:: python
+    :linenos:
+
+    event = WfNewsEvent()
+    paragraphs_container = IParagraphContainer(event, None)
+
+And that's it! From now I can get access to all paragraphs associated with my initial content!!
+
+
+Managing traversal
+++++++++++++++++++
+
+As said before, sometimes you have to be able to *traverse* from an initial content to a given sub-content
+managed by an adapter.
+
+PyAMS defines a custom :class:`pyams_utils.traversing.NamespaceTraverser`: when a request traversing subpath is
+starting with '++' characters, it is looking for a named traverser providing :class:`ITraversable` interface
+to the last traversed object.
+
+.. code-block:: python
+    :linenos:
+
+    @adapter_config(name='paras', context=IParagraphContainerTarget, provides=ITraversable)
+    class ParagraphContainerNamespace(ContextAdapter):
+        """++paras++ namespace adapter"""
+
+        def traverse(self, name, furtherpath=None):
+            return IParagraphContainer(self.context)
+
+- line 1: the adapter is named "paras"; this is matching the *++paras++* name which was given to our annotation adapter
+- line 2: the adapter is just a simple context adapter, so inheriting from :class:`pyams_utils.adapter.ContextAdapter`
+- lines 5 to 6: the *traverse* method is used to access the adapted content; if a name like "++ns++value" is given
+  to an adapted object, the "value" part is given as *name" argument.
+
+From now, as soon as an URL like "/mycontent/++paras++/" will be used, you will get access to the paragraphs container.
+This is a standard BTree container, so will get access to it's sub-objects by key.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-content.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,5 @@
+.. _contenthowto:
+
+
+How to create a new content type?
+=================================
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-form.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,103 @@
+.. _formhowto:
+
+
+How to create a form to a component
+===================================
+
+
+When creating a new object to the zodb, the construction of the form can't be based on an object passed context.
+To build the form we use a default factory that is attached to the container of the element.
+
+
+1) Container factory
+--------------------
+
+Declaration of the **factory** of `ContactPhoneParagraph`
+
+.. code-block:: python
+
+    @utility_config(name=CONTACT_PHONE_PARAGRAPH_TYPE, provides=IParagraphFactory)
+    class ContactPhoneParagraphFactory(BaseParagraphFactory):
+        """Contact paragraph factory"""
+
+        name = _("Contact Phone card")
+        content_type = ContactPhoneParagraph
+        secondary_menu = True
+
+
+
+For example :py:class:`IParagraphContainerTarget`, it's a marker interface for paragraph containers.
+We could use this interface as context to declare a new pagelet.
+
+
+.. code-block:: python
+
+    from pyams_form.form import ajax_config
+
+
+    @pagelet_config(name='add-contact-phone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                    permission=MANAGE_CONTENT_PERMISSION)
+    @ajax_config(name='add-contact-phone-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                 base=BaseParagraphAJAXAddForm)
+    class ContactPhoneParagraphAddForm(AdminDialogAddForm):
+        """Contact paragraph add form"""
+
+        legend = _("Add new phone contact card")
+        dialog_class = 'modal-large'
+        icon_css_class = 'fa fa-fw fa-phone'
+        edit_permission = MANAGE_CONTENT_PERMISSION
+
+        #Retrieve fields from the interface of the component
+        fields = field.Fields(IContactPhoneParagraph).omit('__parent__', '__name__', 'visible')
+
+
+        def create(self, data):
+            """Create one instance of the component"""
+            return ContactPhoneParagraph()
+
+        def add(self, object):
+            """Add the new component to the container that implement the interface `IParagraphContainer` """
+            IParagraphContainer(self.context).append(object)
+
+The associate form field are generated automatically based on this interface attributes
+
+:py:function:`@ajax_config()` allows the form is working with ajax requests by providing `json` content.
+
+
+
+2) Edit form
+------------
+
+.. code-block:: python
+
+    @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+                permission=MANAGE_CONTENT_PERMISSION)
+    @ajax_config(name='inner-properties.json', context=IContactPhoneParagraph, layer=IPyAMSLayer,
+             base=BaseParagraphAJAXEditForm)
+    @implementer(IInnerForm)
+    class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm):
+        """Contact paragraph inner edit form"""
+
+        legend = None
+
+        @property
+        def buttons(self):
+            if self.mode == INPUT_MODE:
+                return button.Buttons(IParagraphEditFormButtons)
+            else:
+                return button.Buttons()
+
+        def get_ajax_output(self, changes):
+            output = super(ContactParagraphInnerAJAXEditForm, self).get_ajax_output(changes)
+            updated = changes.get(IBaseParagraph, ())
+            if 'title' in updated:
+                output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
+            updated = changes.get(IContactParagraph, ())
+            if ('photo' in updated) or ('renderer' in updated):
+                # we have to commit transaction to be able to handle blobs...
+                if 'photo' in updated:
+                    ITransactionManager(self.context).get().commit()
+                output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
+                                                                                   ContactParagraphInnerEditForm))
+            return output
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-i18n.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,10 @@
+.. _howto-i18n:
+
+
+pot-create -o ref.pot scr/
+msgmerge -U def.po ref.pot
+msgfmt def.po
+
+#bin/pot-create -o src/onf_website/locales/onf_website.pot src/
+#msgmerge -U --previous --sort-by-file  src/onf_website/locales/fr/LC_MESSAGES/onf_website.po src/onf_website/locales/onf_website.pot
+#msgfmt  src/onf_website/locales/fr/LC_MESSAGES/onf_website.po -o src/onf_website/locales/fr/LC_MESSAGES/onf_website.mo
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-paragraph.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,345 @@
+.. _paragraphhowto:
+
+
+How to create a Paragraph type?
+===============================
+
+Paragraphs or Blocks are components that contain elements/fields and provide one or many renderer methods to compose
+the front office website
+
+
+Create a Paragraph
+""""""""""""""""""
+
+In this example we will create a contact paragraph to display at the user, who to contact.
+
+1) Interface
+------------
+
+At first we create a new paragraph interface.
+
+.. code-block:: python
+
+    CONTACT_PHONE_PARAGRAPH_TYPE = 'PhoneContact'
+    CONTACT_PHONE_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.contact.phone.renderers'
+
+
+    class IContactPhoneParagraph(IBaseParagraph):
+    """Contact with phone number paragraph interface"""
+
+    name = TextLine(title=_("Contact identity"),
+                    description=_("Name of the contact"),
+                    required=True)
+
+    photo = ImageField(title=_("Photo"),
+                       description=_("Use 'browse' button to select contact picture"),
+                       required=False)
+
+    phone = TextLine(title=_("Phone Number"),
+                     description=_("Name of the contact", required=False))
+
+    renderer = Choice(title=_("Contact template"),
+                      description=_("Presentation template used for this contact"),
+                      vocabulary=CONTACT_PHONE_PARAGRAPH_RENDERERS,
+                      default='default')
+
+
+
+
+2) Implement the interface
+--------------------------
+
+An implementation of the interface
+
+.. code-block:: python
+
+    @implementer(IContactPhoneParagraph)
+    @factory_config(provided=IContactPhoneParagraph)
+    class ContactPhoneParagraph(BaseParagraph):
+        """Contact paragraph"""
+
+
+        icon_class = 'fa-phone'
+        icon_hint = _("Phone number card")
+
+        name = FieldProperty(IContactPhoneParagraph['name'])
+        _photo = FileProperty(IContactPhoneParagraph['photo'])
+
+        renderer = FieldProperty(IContactParagraph['renderer'])
+
+        @property
+        def photo(self):
+            return self._photo
+
+        @photo.setter
+        def photo(self, value):
+            self._photo = value
+            if IImage.providedBy(self._photo):
+                alsoProvides(self._photo, IResponsiveImage)
+
+
+3) Renderers Vocabulary
+-----------------------
+
+The list of rendered available for a paragraph is build automatically and is based on adapters that provide this interface
+
+.. code-block:: python
+
+    @vocabulary_config(name=CONTACT_PARAGRAPH_RENDERERS)
+    class ContactParagraphRendererVocabulary(RenderersVocabulary):
+        """Contact Phone paragraph renderers vocabulary"""
+
+        content_interface = IContactPhoneParagraph
+
+
+.. seealso::
+
+    :ref:`rendererhowto`
+
+
+Paragraph in the ZMI
+""""""""""""""""""""
+
+
+To display and manage the new paragraph in the ZMI, you should create this associated forms
+
+1) Paragraph factory
+--------------------
+
+To create a new element instance inside the zodb, we need a container to this object. PyAMS provide
+`IParagraphContainerTarget`, it's the default container for all paragraphs. We could use this container interface
+as context to create a new paragraph.
+
+
+Declaration of the **factory** of `ContactPhoneParagraph`
+
+.. code-block:: python
+
+    @utility_config(name=CONTACT_PHONE_PARAGRAPH_TYPE, provides=IParagraphFactory)
+    class ContactPhoneParagraphFactory(BaseParagraphFactory):
+        """Contact paragraph factory"""
+
+        name = _("Contact Phone card")
+        content_type = ContactPhoneParagraph
+        secondary_menu = True
+
+
+Definition of a form to create a new ContactPhone instance
+
+.. code-block:: python
+
+    from pyams_form.form import ajax_config
+
+
+    @pagelet_config(name='add-contact-phone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                    permission=MANAGE_CONTENT_PERMISSION)
+    @ajax_config(name='add-contact-phone-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
+                 base=BaseParagraphAJAXAddForm)
+    class ContactPhoneParagraphAddForm(AdminDialogAddForm):
+        """Contact phone paragraph add form"""
+
+        legend = _("Add new contact phone card")
+        dialog_class = 'modal-large'
+        icon_css_class = 'fa fa-fw fa-phone'
+
+        fields = field.Fields(IContactPhoneParagraph).omit('__parent__', '__name__', 'visible')
+        edit_permission = MANAGE_CONTENT_PERMISSION
+
+         def create(self, data):
+            return ContactPhoneParagraph()
+
+        def add(self, object):
+            IParagraphContainer(self.context).append(object)
+
+
+2) Create the Paragraph addform button in the menu
+--------------------------------------------------
+
+We have created a new form and we want add a quick access button to create a new paragraph
+
+.. code-block:: python
+
+    @viewlet_config(name='add-contact-phone-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
+                    layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
+    class ContactPhoneParagraphAddMenu(BaseParagraphAddMenu):
+        """Contact paragraph add menu"""
+
+        label = _("Contact card...")
+        label_css_class = 'fa fa-fw fa-id-card-o'
+        url = 'add-contact-paragraph.html'
+        paragraph_type = CONTACT_PARAGRAPH_TYPE
+
+
+
+3) Create Edit inner form
+-------------------------
+
+.. code-block:: python
+
+    @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+                permission=MANAGE_CONTENT_PERMISSION)
+    @ajax_config(name='inner-properties.json', context=IContactPhoneParagraph, layer=IPyAMSLayer,
+             base=BaseParagraphAJAXEditForm)
+    @implementer(IInnerForm)
+    class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm):
+        """Contact paragraph inner edit form"""
+
+        legend = None
+
+        @property
+        def buttons(self):
+            if self.mode == INPUT_MODE:
+                return button.Buttons(IParagraphEditFormButtons)
+            else:
+                return button.Buttons()
+
+        def get_ajax_output(self, changes):
+            output = super(ContactParagraphInnerAJAXEditForm, self).get_ajax_output(changes)
+            updated = changes.get(IBaseParagraph, ())
+            if 'title' in updated:
+                output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
+            updated = changes.get(IContactParagraph, ())
+            if ('photo' in updated) or ('renderer' in updated):
+                # we have to commit transaction to be able to handle blobs...
+                if 'photo' in updated:
+                    ITransactionManager(self.context).get().commit()
+                output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
+                                                                                   ContactParagraphInnerEditForm))
+            return output
+
+
+4) Create an Edit modal form
+-----------------------------
+
+This form is used inside modals popup
+
+
+.. code-block:: python
+
+    @ajax_config(name='properties.json', context=IContactPhoneParagraph, request_type=IPyAMSLayer,
+                     permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
+    @pagelet_config(name='properties.html', context=IContactParagraph, layer=IPyAMSLayer,
+                    permission=MANAGE_CONTENT_PERMISSION)
+    class ContactPhoneParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
+        """Contact phone paragraph properties edit form"""
+
+        prefix = 'contact_properties.'
+
+        legend = _("Edit contact card properties")
+        icon_css_class = 'fa fa-fw fa-id-card-o'
+
+        fields = field.Fields(IContactParagraph).omit('__parent__', '__name__', 'visible')
+        fields['renderer'].widgetFactory = RendererFieldWidget
+
+        edit_permission = MANAGE_CONTENT_PERMISSION
+
+
+.. note::
+
+    Select the new content block types in ZMI to make it available in tools
+
+    .. image:: _static/select_paragraph.png
+
+
+How to associate links or Illustrations to a Paragraph ?
+========================================================
+
+Adding the following marker interface, we add new behavior to the Paragraph `ContactPhoneParagraph` .
+
+
+Paragraph advanced
+""""""""""""""""""
+
+You can attach Associated files, links or illustration and manage them directly through the rubric `Links and Attachments`.
+
+.. image:: _static/select_links_n_attachment.png
+
+
+1) Paragraph with Links and Attachements
+----------------------------------------
+
+To "activate" this features the paragrath object must to implement specific interface
+
+
+.. code-block:: python
+
+    @implementer(IContactPhoneParagraph, IIllustrationTarget,ILinkContainerTarget,IExtFileContainerTarget))
+    @factory_config(provided=IContactPhoneParagraph)
+    class ContactPhoneParagraph(BaseParagraph):
+        """Contact paragraph"""
+            ...
+
+
+These interfaces will allow to link other data to the paragraph.
+
+**Marker interfaces:**
+
+    +--------------------------------+
+    |:py:class:`IIllustrationTarget` |
+    +===================+============+
+    |                   |            |
+    +-------------------+------------+
+
+    +---------------------------------+
+    |:py:class:`ILinkContainerTarget` |
+    +==============+==================+
+    |              | Add internal link|
+    |              +------------------+
+    |              | Add external link|
+    |              +------------------+
+    |              | Add mailto link  |
+    +--------------+------------------+
+
+    +------------------------------------+
+    |:py:class:`IExtFileContainerTarget` |
+    +================+===================+
+    |                | Add external file |
+    |                +-------------------+
+    |                | Add image         |
+    |                +-------------------+
+    |                | Add video         |
+    |                +-------------------+
+    |                | Add audio file    |
+    +----------------+-------------------+
+
+
+**ZMI overview:**
+
+.. image:: _static/select_add_links.png
+
+
+
+2) Add link and association form
+--------------------------------
+
+You can add form to manage links and attachments directly in paragraph form to do that your form must implement
+``IPropertiesEditForm`` and/or  ``IAssociationParentForm``
+
+
+.. code-block:: python
+
+    @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
+    @implementer(IInnerForm, IPropertiesEditForm, IAssociationParentForm)
+    class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm):
+        """Contact paragraph inner edit form"""
+
+        legend = None
+        ajax_handler = 'inner-properties.json'
+
+
+**Marker interfaces:**
+
++-----------------------------------+
+|:py:class:`IPropertiesEditForm`    |
++=========+=========================+
+|         | Add Illustration form   |
++---------+-------------------------+
+
++-----------------------------------+
+|:py:class:`IAssociationParentForm` |
++===========+=======================+
+|           | Add Association form  |
++-----------+-----------------------+
+
+.. image:: _static/associations_form.png
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-portlet.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,180 @@
+.. _portlethowto:
+
+
+How to create a Portlet?
+========================
+
+**Portlets** are pluggable user interface software components that are managed and displayed in a web portal,
+for example an enterprise portal or a web CMS. A portlet can aggregate (integrate) and personalize content from
+different sources within a web page. A portlet responds to requests from a web client and generates dynamic content
+(*reference:* `Wiki portlet`_).
+
+.. _`wiki portlet`: https://en.wikipedia.org/wiki/Portlet
+
+
+**PyAMS Portal** provides the portal engine but only a very small set of predefined portlets that can be used to compose
+and organize the structure of a web page; additional portlets are provided by other packages, like
+:ref:`pyams_content`.
+
+
+1. Define portlet settings
+''''''''''''''''''''''''''
+
+Portlet settings interface are defined in ``interfaces.py``, you can use :py:class:`pyams_portal.interfaces.IPortletSettings`
+or extend the interface by adding additional properties for example:
+
+.. code-block:: python
+
+    from zope.schema import Text
+
+    NEW_PORTLET_NAME = 'new.portlet'
+
+    class INewPortletSettings(IPortletSettings):
+
+        comment = Text(title=_("Comment"),
+                       required=True)
+
+
+A :py:class:`pyams_portal.portlet.PortletSettings` persistent subclass then implements what IPortletSettings describes:
+
+.. code-block:: python
+
+    @implementer(INewPortletSettings)
+    class NewPortletSettings(PortletSettings):
+        """Portlet settings"""
+
+        comment = FieldProperty(INewPortletSettings['comment'])
+
+
+2. Create Portlet
+'''''''''''''''''
+
+The Portlet component is a utility, which implements the :py:class:`pyams_portal.interfaces.IPortlet` interface and is
+registered by the :py:func:`pyams_portal.portlet.portlet_config` decorator;
+
+.. code-block:: python
+
+    @portlet_config(permission=VIEW_PERMISSION)
+    class ImagePortlet(Portlet):
+        """Image portlet"""
+
+        name = NEW_PORTLET_NAME
+        label = _("New portlet")
+
+        toolbar_image = None
+        toolbar_css_class = 'fa fa-fw fa-2x fa-picture-o'
+
+        settings_class = NewPortletSettings
+
+
+Where:
+ - **permission**: permission required to display this portlet content
+ - **name**: internal name given to this portlet. This name must be unique between all portlets, so using your own
+   namespace into this name is generally a good option!
+ - **label**: user label given to this portlet
+ - **toolbar_image**: URL of an image used to display portlet button into ZMI toolbar, if any
+ - **toolbar_css_class**: CSS class used to display portlet button into ZMI toolbar, if any
+ - **settings_class**: class used to store portlet settings.
+
+
+3. Create portlet settings edit form
+''''''''''''''''''''''''''''''''''''
+
+Portlet settings have to be updated through management interface via a :py:class:`pyams_portal.zmi.portlet.PortletSettingsEditor`
+subform:
+
+.. code-block:: python
+
+    @pagelet_config(name='properties.html', context=INewPortletSettings, layer=IPyAMSLayer,
+                    permission=VIEW_SYSTEM_PERMISSION)
+    class NewPortletSettingsEditor(PortletSettingsEditor):
+        """New portlet settings editor"""
+
+        settings = INewPortletSettings
+
+
+    @adapter_config(name='properties.json', context=(INewPortletSettings, IPyAMSLayer), provides=IPagelet)
+    class NewPortletSettingsAJAXEditor(AJAXEditForm, NewPortletSettingsEditor):
+        """New portlet settings editor, JSON renderer"""
+
+
+4. Previewing portlet content
+'''''''''''''''''''''''''''''
+
+A *previewer* is used into ZMI to display portlet content into portal template definition page:
+
+.. code-block:: python
+
+    @adapter_config(context=(Interface, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletPreviewer)
+    @template_config(template='my-portlet-preview.pt', layer=IPyAMSLayer)
+    class NewPortletPreviewer(PortletPreviewer):
+        """New portlet previewer"""
+
+
+The previewer template is a Chameleon template:
+
+.. code-block:: genshi
+
+    <tal:var define="settings view.settings">
+        <tal:if condition="settings.visible">
+            <div tal:content="settings.comment">Comment</div>
+        </tal:if>
+        <tal:if condition="not settings.visible">
+            <div class="text-center padding-y-5">
+                <span class="fa-stack fa-lg">
+                    <i class="fa fa-eye fa-stack-1x"></i>
+                    <i class="fa fa-ban fa-stack-2x text-danger"></i>
+                </span>
+            </div>
+        </tal:if>
+    </tal:var>
+
+Here we check if portlet is visible or not to display a small icon when hidden; otherwise we display entered comment.
+
+
+5. Rendering portlet content
+''''''''''''''''''''''''''''
+
+A *renderer* is used to display portlet content into rendered page content:
+
+.. code-block:: python
+
+    @adapter_config(context=(IPortalContext, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletRenderer)
+    @template_config(template='my-portlet-render.pt', layer=IPyAMSLayer)
+    class NewPortletRenderer(PortletRenderer):
+        """New portlet renderer"""
+
+        label = _("Default comment renderer")
+
+
+Each renderer template is also a Chameleon template:
+
+.. code-block:: genshi
+
+    <div class="comment" tal:content="view.settings.comment">Comment</div>
+
+
+This is the configuration of a *default* renderer defined for this portlet; you can provide several renderers for a
+given portlet by given distinct names to the adapters:
+
+.. code-block:: python
+
+    @adapter_config(name='another-renderer',
+                    context=(IPortalContext, IPyAMSLayer, Interface, INewPortletSettings),
+                    provides=IPortletRenderer)
+    @template_config(template='my-portlet-render-2.pt', layer=IPyAMSLayer)
+    class AnotherNewPortletRenderer(PortletRenderer):
+        """Another new portlet renderer"""
+
+        label = _("Another comment renderer")
+
+.. tip::
+    You can use an other template without create a new renderer component,
+    with :py:func:`pyams_utils` to override the default template with you own.
+
+
+.. note::
+
+    Select the new portlet in ZMI to make it available in the website template editor
+
+    .. image:: _static/select_portlet.png
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-rename.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,67 @@
+.. _renamehowto:
+
+
+How to rename classes of existing objects ?
+===========================================
+
+
+PyAMS provides `zodbupdate` script to update easily update the change in ZODB. Because, when you develop new features
+to PyAMS we sometimes have to rename or modify classes of already stored objects and if you do that you will never access
+yours objects
+
+
+**1) Include you package in the require resource for you webapp**
+
+
+.. code-block:: python
+
+    requires = [
+        'mypackage',
+        ...
+    ]
+
+**2) Add the Entry points**
+
+
+In the `setup.py` file add 'zodbupdate' params into entry_points where is store zodbupdate directives
+
+.. code-block:: python
+
+      entry_points={
+          'zodbupdate': [
+              'renames = mypackage.generations:RENAMED_CLASSES'
+          ]
+      }
+
+
+
+**3) Create a module named** ``generation`` **with the migration directives**
+
+
+.. code-block:: python
+
+    RENAMED_CLASSES = {
+        'mypackage.mymodule MyOldClass': 'mypackage.mymodule MyNewClass'
+    }
+
+
+**4) Run the commands** ``buildout`` .
+
+.. code-block:: console
+
+    $ mypackage/bin/buildout
+    $ webapp/bin/buildout
+
+
+The ``buildout`` command going to install correctly the package and add the new entry_points references in entry_points.txt
+
+**5)  Run the commands** ``zodbupdate``
+
+To finish run `zodbupdate` to apply the changes describe in `RENAMED_CLASSES`
+
+.. code-block:: console
+
+    $   bin/zodbupdate --config etc/zodbupdate-zeo.conf
+
+
+Where `zodbupdate-zeo.conf` is the configuration file to access to the ZODB.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-renderer.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,135 @@
+.. _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
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-skin.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,75 @@
+.. _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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/howto-template.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,63 @@
+.. _templatehowto:
+
+
+How to define or change a template for a specific skin?
+=======================================================
+
+Override the default template for a renderer
+--------------------------------------------
+
+If you want to modify the template for a particular rendering mode, you can use the function :py:func:`override_template`
+
+.. code-block:: python
+
+	from pyams_template.template import override_template
+
+	from my_website.skin.public.layer import ICustomLayer
+	from pyams_default_theme.component.keynumber.portlet import KeyNumberPortletHorizontalRenderer
+
+
+	override_template(context=KeyNumberPortletHorizontalRenderer,
+					template="templates/keynumber-horizontal.pt",
+					layer=ICustomLayer
+					)
+
+
+This new template can be applied to a particular :ref:`Skin <skinhowto>` by specifying on which layer to use this renderer
+*(ex: layer=IMyWebsiteLayer)*
+
+
+
+Redefine the default template for a renderer
+--------------------------------------------
+
+You must redefine an adapter to add new variables or static resources for your new template,
+
+.. code-block:: python
+
+	# import interfaces
+	from my_website.skin.public.layer import ICustomLayer
+
+	from pyams_content.component.keynumber.portlet.interfaces import IKeyNumberPortletSettings
+	from pyams_portal.interfaces import IPortletRenderer, IPortalContext
+
+	# import packages
+	from my_website.skin.public import my_carousel_init ,my_carousel_animation
+
+	from pyams_default_theme.component.keynumber.portlet import KeyNumberPortletHorizontalRenderer
+	from pyams_template.template import template_config
+	from pyams_utils.adapter import adapter_config
+	from zope.interface import Interface
+
+
+	@adapter_config(context=(IPortalContext, IBaseLayer, Interface, IKeyNumberPortletSettings),
+	                provides=IPortletRenderer)
+	@template_config(template='templates/keynumber-horizontal.pt', layer=ICustomLayer)
+	class MyCustomKeyNumberPortletHorizontalRenderer(KeyNumberPortletHorizontalRenderer):
+		"""Key numbers portlet horizontal renderer"""
+
+		resources = (my_carousel_init, my_carousel_animation)
+
+
+The attribute :py:attr:`resources` is used to load in the template static resources. The application will automatically
+integrate resource content when the template is calling.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/introduction.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,36 @@
+.. _introduction:
+
+Why should i use PyAMS?
+-----------------------
+
+Based on the python langage, **PyAMS** supply powerfull tools like *PyAMS_content*, this package
+providing content management features, which can be easily extended to manage your own content types.
+But PyAMS can also be used to manage any kind of application.
+
+In addition **PyAMS** is built on top of **MyAMS** (**My** **\A**\pplication **\M**\anagement **\S**\kin), a small web client framework built on top of JQuery
+and Bootstrap, which was developed by the french national forestry office (ONF -- Office national des forêts --
+http://www.onf.fr) to build web applications in several languages (actually java, Python and PHP). The new ONF website
+is now completely handled with PyAMS framework.
+
+PyAMS is a multipurpose set of packages, providing tools including:
+
+    * custom interfaces
+    * custom registry annotations
+    * custom security policy
+    * local registry support
+    * network protocols utilities (for HTTP and XML-RPC)
+    * command line scripts
+    * custom utilities.
+
+
+.. tip::
+    Some screenshots of the admin interface are available at :ref:`screenshots`!
+
+
+How to install PyAMS?
+---------------------
+
+If you want to create a PyAMS application instance easily, just follow the :ref:`quickstart` guide!
+If you want learn more about a custom installation follow :ref:`install` procedure.
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/tales.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,74 @@
+.. _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)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/traverser.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,47 @@
+.. _traverser:
+
+PyAMS namespace traverser
+=========================
+
+PyAMS_utils provide a custom URL traverser, defined in package :py:mod:`pyams_utils.traversing`.
+
+The :py:class:`NamespaceTraverser <pyams_utils.traversing.NamespaceTraverser>` is a custom traverser based on default
+Pyramid's *ResourceTreeAdapter*, but it adds the ability to use *namespaces*. Inherited from *Zope3* concept, a
+namespace is a resource path element starting with the « *++* » characters, like this:
+
+.. code-block:: none
+
+    http://localhost:5432/folder/content/++ns++argument/@@view.html
+
+In this sample, *ns* is the namespace name. When the traverser detects a namespace, it looks for several named
+adapters (or multi-adapters) to the :py:class:`ITraversable <zope.traversing.interfaces.ITraversable>` interface
+defined in *zope.traversing* package. Adapters lookup with name *ns* is done for the current *context* and *request*,
+then only for the context and finally for the request, in this order. If a traversing adapter is found, it's
+:py:func:`traverse` method is called, with the *attr* value as first argument, and the rest of the traversal stack
+as second one.
+
+This is for example how a custom *etc* namespace traverser is defined:
+
+.. code-block:: python
+
+    from pyams_utils.interfaces.site import ISiteRoot
+    from zope.traversing.interfaces import ITraversable
+
+    from pyams_utils.adapter import adapter_config, ContextAdapter
+
+    @adapter_config(name='etc', context=ISiteRoot, provides=ITraversable)
+    class SiteRootEtcTraverser(ContextAdapter):
+        """Site root ++etc++ namespace traverser"""
+
+        def traverse(self, name, furtherpath=None):
+            if name == 'site':
+                return self.context.getSiteManager()
+            raise NotFound
+
+By using an URL like '++etc++site' on your site root, you can then get access to your local site manager.
+
+*argument* is not mandatory for the namespace traverser. If it is not provided, the *traverse* method is called with
+an empty string (with is a default adapter name) as first argument.
+
+Several PyAMS components use custom traversal adapters. For example, getting thumbnails from an image is done
+through a traversing adapter, which results in nicer URLs than when using classic URLs with arguments...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/utilities.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,57 @@
+.. _utilities:
+
+Custom PyAMS utilities
+======================
+
+PyAMS_utils provides a small set of utilities. You can create some of them as global utilities registered in
+the global components registry; other ones can be created manually by a site administrator and
+are then registered automatically.
+
+
+Server timezone
+---------------
+
+To manage timezones correctly, and display datetimes based on current server timezone, all datetimes should
+be defined and stored in UTC.
+
+PyAMS_utils provides a :py:class:`ServerTimezoneUtility <pyams_utils.timezone.utility.ServerTimezoneUtility>` which
+allows you to assign a default timezone to your server.
+
+To display a datetime with correct timezone, you can use the :py:func:`tztime <pyams_utils.timezone.tztime>` function,
+which assign server timezone to the given parameter:
+
+.. code-block:: python
+
+    from datetime import datetime
+    from pyams_utils.timezone import tztime
+
+    now = datetime.utcnow()
+    my_date = tztime(now)  # converts *now* to server timezone
+
+We could imagine that datetimes could be displayed with current user timezone. But it's quite impossible to know
+the user timazone from a server request. The only options are:
+
+- you ask an authenticated user to update a timezone setting in his profile
+
+- you can include Javascript libraries which will try to detect browser timezone from their computer configuration, and
+  do an AJAX request to update data in their session.
+
+That should require an update of :py:func:`tzinfo` adapter to get timezone info from session, request or user profile.
+
+
+ZEO connection
+--------------
+
+Several PyAMS utilities (like the tasks scheduler or the medias converter) are working with dedicated processes,
+are connected to main PyAMS process through ØMQ, and use ZEO connections for their PyAMS database access.
+
+Clients of these processes have to send settings of the ZEO connections that they should use.
+
+The ZEOConnection utility can be created by the site manager through the web management interface (ZMI) from the
+*Control panel*:
+
+.. image:: _static/zeo-add-menu.png
+
+ZEO connection creation form allows you to define all settings of a ZEO connection:
+
+.. image:: _static/zeo-add-form.png
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/dev_guide/zca.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,292 @@
+.. _zca:
+
+Zope Component Architecture with PyAMS
+++++++++++++++++++++++++++++++++++++++
+
+PyAMS packages are developed based on the **Zope Component Architecture** (aka **ZCA**). ZCA is used by the Pyramid framework
+"under the hood" to handle interfaces, adapters and utilities. You don't **have to** use it in your own applications.
+But you can.
+
+The ZCA is mainly adding elements like **interfaces**, **adapters** and **utilities** to the Python language. It
+allows you to write a framework or an application by using **components** which can be extended easily.
+
+Interfaces
+    Interfaces are objects that specify (document) the external behavior
+    of objects that "provide" them.  An interface specifies behavior through, a documentation in a doc string,
+    attribute definitions and conditions of attribute values.
+
+Components
+    Components are objects that are associated with interfaces.
+
+Utilities
+    Utilities are just components that provide an interface and that are looked up by an interface and a name
+
+Adapters
+    Adapters are components that are computed from other components to adapt them to some interface.
+    Because they are computed from other objects, they are provided as factories, usually classes.
+
+
+You will find several useful resources about ZCA concepts on the internet.
+
+.. seealso::
+    Zope Documentations:
+        - `Components and Interfaces <http://zope.readthedocs.io/en/latest/zdgbook/ComponentsAndInterfaces.html>`_
+        - `Zope component <http://zopecomponent.readthedocs.io/en/latest/narr.html>`_
+        - `Zope interface <https://docs.zope.org/zope.interface/README.html>`_
+
+
+Utilities
+---------
+
+Local utilities
+'''''''''''''''
+
+In ZCA, a **utility** is a **registered** component which provides an **interface**. This interface is the
+**contract** which defines features (list of attributes and methods) provided by the component which implements it.
+
+When a Pyramid application starts, a **global registry** is created to register a whole set of utilities and
+adapters; this registration can be done via ZCML directives or via native Python code.
+In addition, PyAMS allows you to define **local utilities**, which are stored and registered in the ZODB via a
+**site manager**.
+
+
+Registering local utilities
+'''''''''''''''''''''''''''
+
+
+.. tip::
+
+    :ref:`site` can be used to store **local utilities** whose configuration, which is easily
+    available to site administrators through management interface, is stored in the ZODB.
+
+
+A local utility is a persistent object, registered in a *local site manager*, and providing a specific interface (if
+a component provides several interfaces, it can be registered several times).
+
+Some components can be required by a given package, and created automatically via the *pyams_upgrade* command line
+script; this process relies on the *ISiteGenerations* interface, for example for the timezone utility, a component
+provided by PyAMS_utils package to handle server timezone and display times correctly:
+
+.. code-block:: python
+
+    from pyams_utils.interfaces.site import ISiteGenerations
+    from pyams_utils.interfaces.timezone import IServerTimezone
+
+    from persistent import Persistent
+    from pyams_utils.registry import utility_config
+    from pyams_utils.site import check_required_utilities
+    from pyramid.events import subscriber
+    from zope.container.contained import Contained
+    from zope.interface import implementer
+    from zope.schema.fieldproperty import FieldProperty
+
+    @implementer(IServerTimezone)
+    class ServerTimezoneUtility(Persistent, Contained):
+
+        timezone = FieldProperty(IServerTimezone['timezone'])
+
+    REQUIRED_UTILITIES = ((IServerTimezone, '', ServerTimezoneUtility, 'Server timezone'),)
+
+    @subscriber(INewLocalSite)
+    def handle_new_local_site(event):
+        """Create a new ServerTimezoneUtility when a site is created"""
+        site = event.manager.__parent__
+        check_required_utilities(site, REQUIRED_UTILITIES)
+
+    @utility_config(name='PyAMS timezone', provides=ISiteGenerations)
+    class TimezoneGenerationsChecker(object):
+        """Timezone generations checker"""
+
+        generation = 1
+
+        def evolve(self, site, current=None):
+            """Check for required utilities"""
+            check_required_utilities(site, REQUIRED_UTILITIES)
+
+Some utilities can also be created manually by an administrator through the management interface, and registered
+automatically after their creation. For example, this is how a ZEO connection utility (which is managing settings to
+define a ZEO connection) is registered:
+
+.. code-block:: python
+
+    from pyams_utils.interfaces.site import IOptionalUtility
+    from pyams_utils.interfaces.zeo import IZEOConnection
+    from zope.annotation.interfaces import IAttributeAnnotatable
+    from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
+
+    from persistent import Persistent
+    from pyramid.events import subscriber
+    from zope.container.contained import Contained
+
+    @implementer(IZEOConnection)
+    class ZEOConnection(object):
+        """ZEO connection object. See source code to get full implementation..."""
+
+    @implementer(IOptionalUtility, IAttributeAnnotatable)
+    class ZEOConnectionUtility(ZEOConnection, Persistent, Contained):
+        """Persistent ZEO connection utility"""
+
+    @subscriber(IObjectAddedEvent, context_selector=IZEOConnection)
+    def handle_added_connection(event):
+        """Register new ZEO connection when added"""
+        manager = event.newParent
+        manager.registerUtility(event.object, IZEOConnection, name=event.object.name)
+
+    @subscriber(IObjectRemovedEvent, context_selector=IZEOConnection)
+    def handle_removed_connection(event):
+        """Un-register ZEO connection when deleted"""
+        manager = event.oldParent
+        manager.unregisterUtility(event.object, IZEOConnection, name=event.object.name)
+
+*context_selector* is a custom subscriber predicate, so that subscriber event is activated only if object concerned
+by an event is providing given interface.
+
+
+Registering global utilities
+''''''''''''''''''''''''''''
+
+**Global utilities** are components providing an interface which are registered in the global registry.
+PyAMS_utils package provides custom annotations to register global utilities without using ZCML. For example, a skin
+is nothing more than a simple utility providing the *ISkin* interface:
+
+.. code-block:: python
+
+    from pyams_default_theme.layer import IPyAMSDefaultLayer
+    from pyams_skin.interfaces import ISkin
+    from pyams_utils.registry import utility_config
+
+    @utility_config(name='PyAMS default skin', provides=ISkin)
+    class PyAMSDefaultSkin(object):
+        """PyAMS default skin"""
+
+        label = _("PyAMS default skin")
+        layer = IPyAMSDefaultLayer
+
+This annotation registers a utility, named *PyAMS default skin*, providing the *ISkin* interface. It's the developer
+responsibility to provide all attributes and methods required by the provided interface.
+
+
+Looking for utilities
+'''''''''''''''''''''
+
+ZCA provides the *getUtility* and *queryUtility* functions to look for a utility. But these methods only applies to
+global registry.
+
+PyAMS package provides equivalent functions, which are looking for components into local registry before looking into
+the global one. For example:
+
+.. code-block:: python
+
+    from pyams_security.interfaces import ISecurityManager
+    from pyams_utils.registry import query_utility
+
+    manager = query_utility(ISecurityManager)
+    if manager is not None:
+        print("Manager is there!")
+
+All ZCA utility functions have been ported to use local registry: *registered_utilities*, *query_utility*,
+*get_utility*, *get_utilities_for*, *get_all_utilities_registered_for* functions all follow the equivalent ZCA
+functions API, but are looking for utilities in the local registry before looking in the global registry.
+
+
+Adapters
+--------
+
+Registering adapters
+''''''''''''''''''''
+
+An adapter is also a kind of utility. But instead of *just* providing an interface, it adapts an input object,
+providing a given interface, to provide another interface. An adapter can also be named, so that you can choose which
+adapter to use at a given time.
+
+PyAMS_utils provide another annotation, to help registering adapters without using ZCML files. An adapter can be a
+function which directly returns an object providing the requested interface, or an object which provides the interface.
+
+The first example is an adapter which adapts any persistent object to get it's associated transaction manager:
+
+.. code-block:: python
+
+    from persistent.interfaces import IPersistent
+    from transaction.interfaces import ITransactionManager
+    from ZODB.interfaces import IConnection
+
+    from pyams_utils.adapter import adapter_config
+
+    @adapter_config(context=IPersistent, provides=ITransactionManager)
+    def get_transaction_manager(obj):
+        conn = IConnection(obj)
+        try:
+            return conn.transaction_manager
+        except AttributeError:
+            return conn._txn_mgr
+
+This is another adapter which adapts any contained object to the *IPathElements* interface; this interface can be
+used to build index that you can use to find objects based on a parent object:
+
+.. code-block:: python
+
+    from pyams_utils.interfaces.traversing import IPathElements
+    from zope.intid.interfaces import IIntIds
+    from zope.location.interfaces import IContained
+
+    from pyams_utils.adapter import ContextAdapter
+    from pyams_utils.registry import query_utility
+    from pyramid.location import lineage
+
+    @adapter_config(context=IContained, provides=IPathElements)
+    class PathElementsAdapter(ContextAdapter):
+        """Contained object path elements adapter"""
+
+        @property
+        def parents(self):
+            intids = query_utility(IIntIds)
+            if intids is None:
+                return []
+            return [intids.register(parent) for parent in lineage(self.context)]
+
+An adapter can also be a multi-adapter, when several input objects are requested to provide a given interface. For
+example, many adapters require a context and a request, eventually a view, to provide another feature. This is how,
+for example, we define a custom *name* column in a security manager table displaying a list of plug-ins:
+
+.. code-block:: python
+
+    from pyams_zmi.layer import IAdminLayer
+    from z3c.table.interfaces import IColumn
+
+    from pyams_skin.table import I18nColumn
+    from z3c.table.column import GetAttrColumn
+
+    @adapter_config(name='name', context=(Interface, IAdminLayer, SecurityManagerPluginsTable), provides=IColumn)
+    class SecurityManagerPluginsNameColumn(I18nColumn, GetAttrColumn):
+        """Security manager plugins name column"""
+
+        _header = _("Name")
+        attrName = 'title'
+        weight = 10
+
+As you can see, adapted objects can be given as functions or as classes.
+
+
+Vocabularies
+------------
+
+Registering vocabularies
+''''''''''''''''''''''''
+
+A **vocabulary** is a custom factory which can be used as source for several field types, like *choices* or *lists*.
+Vocabularies have to be registered in a custom registry, so PyAMS_utils provide another annotation to register them.
+This example is based on the *Timezone* component which allows you to select a timezone between a list of references:
+
+.. code-block:: python
+
+    import pytz
+    from pyams_utils.vocabulary import vocabulary_config
+    from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
+
+    @vocabulary_config(name='PyAMS timezones')
+    class TimezonesVocabulary(SimpleVocabulary):
+        """Timezones vocabulary"""
+
+        def __init__(self, *args, **kw):
+            terms = [SimpleTerm(t, t, t) for t in pytz.all_timezones]
+            super(TimezonesVocabulary, self).__init__(terms)
--- a/src/source/developer_guide/developerguide.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-.. _developerguide:
-
-
-Developer's Guide
-=================
-
-Introduction
-++++++++++++
-
-Pyams packages are...
-
-
-What is Pyramid?
-++++++++++++++++
-
-Pyramid is an open source Python web application framework. It is designed to make creating web applications easier.
-
-
-What is Zope?
-+++++++++++++
-
-Zope 2 is a free and open-source, object-oriented web application server written in the Python programming language. The term ZOPE is an acronym for “Z Object Publishing Environment” (the Z doesn’t really mean anything in particular). However, nowadays ZOPE is simply written as Zope. It has three distinct audiences.
-
-
-This guide is intended to document Zope for the second audience, Developers, as defined above. If you fit more into the “user” audience defined above, you’ll probably want to start by reading The Zope Book .
-
-Throughout this guide, it is assumed that you know how to program in the Python programming language. Most of the examples in this guide will be in Python. There are a number of great resources and books for learning Python; the best online resource is the python.org web site and many books can be found on the shelves of your local bookstore.
-1.2. Organization of the book
-
-This book describes Zope’s services to the developer from a hands on, example-oriented standpoint. This book is not a complete reference to the Zope API, but rather a practical guide to applying Zope’s services to develop and deploy your own web applications. This book covers the following topics:
-
-Components and Interfaces
-    Zope use a component-centric development model. This chapter describes the component model in Zope and how Zope components are described through interfaces.
-Object Publishing
-    Developing applications for Zope involves more than just creating a component, that component must be publishable on the web. This chapter describes publication, and how your components need to be designed to be published.
-Zope Products
-    New Zope components are distributed and installed in packages called “Products”. This chapter explains Products in detail.
-Persistent Components
-    Zope provides a built-in, transparent Python object database called ZODB. This chapter describes how to create persistent components, and how they work in conjunction with the ZODB.
-Acquisition
-    Zope relies heavily on a dynamic technique called acquisition. This chapter explores acquisition thoroughly.
-Security
-    When your component is used by many different people through the web, security becomes a big concern. This chapter describes Zope’s security API and how you can use it to make security assertions about your object.
-Debugging and Testing
-    Zope has built in debugging and testing support. This chapter describes these facilities and how you can debug and test your components.
-
-
-.. toctree::
-   :maxdepth: 2
-
-   Package directory layout <package_layout>
-   appextend
-   traverser
-   tales
--- a/src/source/developer_guide/howto-adapter.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-.. _adapterhowto:
-
-
-How to define an annotations adapter?
-=====================================
-
-Adapters are important concepts of ZCA and PyAMS framework. If you don't know what are adapters, see :ref:`zca`.
-
-
-What are annotations?
-+++++++++++++++++++++
-
-When an adapter have to add persistent attributes to a persistent class, it can add these attributes directly into
-persistent instances. But this way can lead to conflicts when several adapters want to use the same attribute name for
-different kinds of information.
-
-Annotations are an elegant way to handle this use case: they are based on a BTree which is stored into a
-specific instance attribute (*__annotations__*). Any adapter can then use this dictionary to store it's own
-informations, using it's own namespace as dictionary key.
-
-ZODB browser allows you to display existing annotations:
-
-.. image:: _static/annotations-1.png
-
-This example displays several annotations, each using it's own namespace:
-
-.. image:: _static/annotations-2.png
-
-
-Designing interfaces
-++++++++++++++++++++
-
-The first step with ZCA is to design your interfaces.
-
-The are going to base our example on PyAMS_content 'paragraphs' component: a content class is marked as a
-*paragraphs container target*, a class that can store paragraphs. But the real storage of paragraphs is done by
-another *container* class:
-
-.. code-block:: python
-    :linenos:
-
-    from zope.annotation.interfaces import IAttributeAnnotatable
-    from zope.containers.constraints import containers, contains
-
-
-    class IBaseParagraph(Interface):
-        """Base paragraph interface"""
-
-        containers('.IParagraphContainer')
-
-
-    class IParagraphContainer(IOrderedContainer):
-        """Paragraphs container"""
-
-        contains(IBaseParagraph)
-
-
-    class IParagraphContainerTarget(IAttributeAnnotatable):
-        """Paragraphs container marker interface"""
-
-
-    PARAGRAPH_CONTAINER_KEY = 'pyams_content.paragraph'
-
-
-- line 5 to 8: :class:`IBaseParagraph` is the base interface for all paragraphs; constraint implies that paragraphs
-  can only be stored in a container implementing :class:`IParagraphContainer` interface.
-- line 11 to 14: :class:`IParagraphContainer` is the base interface for paragraphs containers; constraint implies that
-  such a container can only contain objects implementing :class:`IBaseParagraph` interface.
-- line 17 to 18: :class:`IParagraphContainerTarget` is only a *marker* interface which doesn't provide any method or
-  attribute; it only inherits from :class:`IAttributeAnnotatable`, which implies that classes implementing this
-  interface allows other classes to add informations as annotations through a dedicated *__annotations__* attribute.
-- line 21: this is the key which will be used to store our annotation.
-
-
-Creating persistent classes
-+++++++++++++++++++++++++++
-
-The first step is to declare that a given content class can store paragraphs:
-
-.. code-block:: python
-    :linenos:
-
-    from pyams_content.component.paragraph.interfaces import IParagraphContainerTarget
-    from zope.interface import implementer
-
-    @implementer(IParagraphContainerTarget)
-    class WfNewsEvent(WfSharedContent):
-        """News event class"""
-
-Here we just say "Well, I'm a shared content, and I'm OK to store paragraphs!".
-
-So we can design the paragraphs container class. It's this class which will *really* store the paragraphs:
-
-.. code-block:: python
-    :linenos:
-
-    @implementer(IParagraphContainer)
-    class ParagraphContainer(BTreeOrderedContainer):
-        """Paragraphs container"""
-
-The paragraphs container class inherits from a :class:`BTreeOrderedContainer` and implements
-:class:`IParagraphContainer`.
-
-The last operation is to create the adapter, which is the *glue* between the *target* class and the paragraphs
-container:
-
-.. code-block:: python
-    :linenos:
-
-    from pyams_utils.adapter import adapter_config, get_annotation_adapter
-
-    @adapter_config(context=IParagraphContainerTarget, provides=IParagraphContainer)
-    def paragraph_container_factory(target):
-        """Paragraphs container factory"""
-        return get_annotation_adapter(target,
-                                      PARAGRAPH_CONTAINER_KEY,
-                                      ParagraphContainer,
-                                      name='++paras++')
-
-PyAMS provides a shortcut to create an annotation adapter in :func:`pyams_utils.adapter.get_annotation_adapter`.
-It's mandatory arguments are:
-
-- **context** (line 6): the context to which the adapter is applied
-- **key** (line 7): the string key used to access and store context's annotations
-- **factory** (line 8): if the requested annotation is missing, a new one is created using this factory (which can be a class or
-  a function)
-
-Optional arguments are:
-
-- **markers** (None by default): if set, should be a list of marker interfaces which will be assigned to object
-  created by the factory
-- **notify**: if *True* (default), an :class:`ObjectCreatedEvent` event is notified on object creation
-- **locate**: if *True* (default), context is set as *parent* of created object
-- **parent**: if *locate* is True and if *parent* is set, this is the object to which the new object should be *parented*
-  instead of initial context
-- **name** (None by default): some objects need to be traversed, especially when you have to be able to access them through an URL; this
-  is the name given to created object.
-
-
-Using your adapter
-++++++++++++++++++
-
-Starting from your *content* object, it's then very simple to access to the paragraphs container:
-
-.. code-block:: python
-    :linenos:
-
-    event = WfNewsEvent()
-    paragraphs_container = IParagraphContainer(event, None)
-
-And that's it! From now I can get access to all paragraphs associated with my initial content!!
-
-
-Managing traversal
-++++++++++++++++++
-
-As said before, sometimes you have to be able to *traverse* from an initial content to a given sub-content
-managed by an adapter.
-
-PyAMS defines a custom :class:`pyams_utils.traversing.NamespaceTraverser`: when a request traversing subpath is
-starting with '++' characters, it is looking for a named traverser providing :class:`ITraversable` interface
-to the last traversed object.
-
-.. code-block:: python
-    :linenos:
-
-    @adapter_config(name='paras', context=IParagraphContainerTarget, provides=ITraversable)
-    class ParagraphContainerNamespace(ContextAdapter):
-        """++paras++ namespace adapter"""
-
-        def traverse(self, name, furtherpath=None):
-            return IParagraphContainer(self.context)
-
-- line 1: the adapter is named "paras"; this is matching the *++paras++* name which was given to our annotation adapter
-- line 2: the adapter is just a simple context adapter, so inheriting from :class:`pyams_utils.adapter.ContextAdapter`
-- lines 5 to 6: the *traverse* method is used to access the adapted content; if a name like "++ns++value" is given
-  to an adapted object, the "value" part is given as *name" argument.
-
-From now, as soon as an URL like "/mycontent/++paras++/" will be used, you will get access to the paragraphs container.
-This is a standard BTree container, so will get access to it's sub-objects by key.
--- a/src/source/developer_guide/howto-content.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-.. _contenthowto:
-
-
-How to create a new content type?
-=================================
--- a/src/source/developer_guide/howto-form.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-.. _formhowto:
-
-
-How to create a form to a component
-===================================
-
-
-When creating a new object to the zodb, the construction of the form can't be based on an object passed context.
-To build the form we use a default factory that is attached to the container of the element.
-
-
-1) Container factory
---------------------
-
-Declaration of the **factory** of `ContactPhoneParagraph`
-
-.. code-block:: python
-
-    @utility_config(name=CONTACT_PHONE_PARAGRAPH_TYPE, provides=IParagraphFactory)
-    class ContactPhoneParagraphFactory(BaseParagraphFactory):
-        """Contact paragraph factory"""
-
-        name = _("Contact Phone card")
-        content_type = ContactPhoneParagraph
-        secondary_menu = True
-
-
-
-For example :py:class:`IParagraphContainerTarget`, it's a marker interface for paragraph containers.
-We could use this interface as context to declare a new pagelet.
-
-
-.. code-block:: python
-
-    from pyams_form.form import ajax_config
-
-
-    @pagelet_config(name='add-contact-phone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-                    permission=MANAGE_CONTENT_PERMISSION)
-    @ajax_config(name='add-contact-phone-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-                 base=BaseParagraphAJAXAddForm)
-    class ContactPhoneParagraphAddForm(AdminDialogAddForm):
-        """Contact paragraph add form"""
-
-        legend = _("Add new phone contact card")
-        dialog_class = 'modal-large'
-        icon_css_class = 'fa fa-fw fa-phone'
-        edit_permission = MANAGE_CONTENT_PERMISSION
-
-        #Retrieve fields from the interface of the component
-        fields = field.Fields(IContactPhoneParagraph).omit('__parent__', '__name__', 'visible')
-
-
-        def create(self, data):
-            """Create one instance of the component"""
-            return ContactPhoneParagraph()
-
-        def add(self, object):
-            """Add the new component to the container that implement the interface `IParagraphContainer` """
-            IParagraphContainer(self.context).append(object)
-
-The associate form field are generated automatically based on this interface attributes
-
-:py:function:`@ajax_config()` allows the form is working with ajax requests by providing `json` content.
-
-
-
-2) Edit form
-------------
-
-.. code-block:: python
-
-    @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
-                permission=MANAGE_CONTENT_PERMISSION)
-    @ajax_config(name='inner-properties.json', context=IContactPhoneParagraph, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXEditForm)
-    @implementer(IInnerForm)
-    class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm):
-        """Contact paragraph inner edit form"""
-
-        legend = None
-
-        @property
-        def buttons(self):
-            if self.mode == INPUT_MODE:
-                return button.Buttons(IParagraphEditFormButtons)
-            else:
-                return button.Buttons()
-
-        def get_ajax_output(self, changes):
-            output = super(ContactParagraphInnerAJAXEditForm, self).get_ajax_output(changes)
-            updated = changes.get(IBaseParagraph, ())
-            if 'title' in updated:
-                output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
-            updated = changes.get(IContactParagraph, ())
-            if ('photo' in updated) or ('renderer' in updated):
-                # we have to commit transaction to be able to handle blobs...
-                if 'photo' in updated:
-                    ITransactionManager(self.context).get().commit()
-                output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
-                                                                                   ContactParagraphInnerEditForm))
-            return output
-
--- a/src/source/developer_guide/howto-i18n.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-.. _howto-i18n:
-
-
-pot-create -o ref.pot scr/
-msgmerge -U def.po ref.pot
-msgfmt def.po
-
-#bin/pot-create -o src/onf_website/locales/onf_website.pot src/
-#msgmerge -U --previous --sort-by-file  src/onf_website/locales/fr/LC_MESSAGES/onf_website.po src/onf_website/locales/onf_website.pot
-#msgfmt  src/onf_website/locales/fr/LC_MESSAGES/onf_website.po -o src/onf_website/locales/fr/LC_MESSAGES/onf_website.mo
--- a/src/source/developer_guide/howto-paragraph.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,345 +0,0 @@
-.. _paragraphhowto:
-
-
-How to create a Paragraph type?
-===============================
-
-Paragraphs or Blocks are components that contain elements/fields and provide one or many renderer methods to compose
-the front office website
-
-
-Create a Paragraph
-""""""""""""""""""
-
-In this example we will create a contact paragraph to display at the user, who to contact.
-
-1) Interface
-------------
-
-At first we create a new paragraph interface.
-
-.. code-block:: python
-
-    CONTACT_PHONE_PARAGRAPH_TYPE = 'PhoneContact'
-    CONTACT_PHONE_PARAGRAPH_RENDERERS = 'PyAMS.paragraph.contact.phone.renderers'
-
-
-    class IContactPhoneParagraph(IBaseParagraph):
-    """Contact with phone number paragraph interface"""
-
-    name = TextLine(title=_("Contact identity"),
-                    description=_("Name of the contact"),
-                    required=True)
-
-    photo = ImageField(title=_("Photo"),
-                       description=_("Use 'browse' button to select contact picture"),
-                       required=False)
-
-    phone = TextLine(title=_("Phone Number"),
-                     description=_("Name of the contact", required=False))
-
-    renderer = Choice(title=_("Contact template"),
-                      description=_("Presentation template used for this contact"),
-                      vocabulary=CONTACT_PHONE_PARAGRAPH_RENDERERS,
-                      default='default')
-
-
-
-
-2) Implement the interface
---------------------------
-
-An implementation of the interface
-
-.. code-block:: python
-
-    @implementer(IContactPhoneParagraph)
-    @factory_config(provided=IContactPhoneParagraph)
-    class ContactPhoneParagraph(BaseParagraph):
-        """Contact paragraph"""
-
-
-        icon_class = 'fa-phone'
-        icon_hint = _("Phone number card")
-
-        name = FieldProperty(IContactPhoneParagraph['name'])
-        _photo = FileProperty(IContactPhoneParagraph['photo'])
-
-        renderer = FieldProperty(IContactParagraph['renderer'])
-
-        @property
-        def photo(self):
-            return self._photo
-
-        @photo.setter
-        def photo(self, value):
-            self._photo = value
-            if IImage.providedBy(self._photo):
-                alsoProvides(self._photo, IResponsiveImage)
-
-
-3) Renderers Vocabulary
------------------------
-
-The list of rendered available for a paragraph is build automatically and is based on adapters that provide this interface
-
-.. code-block:: python
-
-    @vocabulary_config(name=CONTACT_PARAGRAPH_RENDERERS)
-    class ContactParagraphRendererVocabulary(RenderersVocabulary):
-        """Contact Phone paragraph renderers vocabulary"""
-
-        content_interface = IContactPhoneParagraph
-
-
-.. seealso::
-
-    :ref:`rendererhowto`
-
-
-Paragraph in the ZMI
-""""""""""""""""""""
-
-
-To display and manage the new paragraph in the ZMI, you should create this associated forms
-
-1) Paragraph factory
---------------------
-
-To create a new element instance inside the zodb, we need a container to this object. PyAMS provide
-`IParagraphContainerTarget`, it's the default container for all paragraphs. We could use this container interface
-as context to create a new paragraph.
-
-
-Declaration of the **factory** of `ContactPhoneParagraph`
-
-.. code-block:: python
-
-    @utility_config(name=CONTACT_PHONE_PARAGRAPH_TYPE, provides=IParagraphFactory)
-    class ContactPhoneParagraphFactory(BaseParagraphFactory):
-        """Contact paragraph factory"""
-
-        name = _("Contact Phone card")
-        content_type = ContactPhoneParagraph
-        secondary_menu = True
-
-
-Definition of a form to create a new ContactPhone instance
-
-.. code-block:: python
-
-    from pyams_form.form import ajax_config
-
-
-    @pagelet_config(name='add-contact-phone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-                    permission=MANAGE_CONTENT_PERMISSION)
-    @ajax_config(name='add-contact-phone-paragraph.json', context=IParagraphContainerTarget, layer=IPyAMSLayer,
-                 base=BaseParagraphAJAXAddForm)
-    class ContactPhoneParagraphAddForm(AdminDialogAddForm):
-        """Contact phone paragraph add form"""
-
-        legend = _("Add new contact phone card")
-        dialog_class = 'modal-large'
-        icon_css_class = 'fa fa-fw fa-phone'
-
-        fields = field.Fields(IContactPhoneParagraph).omit('__parent__', '__name__', 'visible')
-        edit_permission = MANAGE_CONTENT_PERMISSION
-
-         def create(self, data):
-            return ContactPhoneParagraph()
-
-        def add(self, object):
-            IParagraphContainer(self.context).append(object)
-
-
-2) Create the Paragraph addform button in the menu
---------------------------------------------------
-
-We have created a new form and we want add a quick access button to create a new paragraph
-
-.. code-block:: python
-
-    @viewlet_config(name='add-contact-phone-paragraph.menu', context=IParagraphContainerTarget, view=IParagraphContainerView,
-                    layer=IPyAMSLayer, manager=IToolbarAddingMenu, weight=600)
-    class ContactPhoneParagraphAddMenu(BaseParagraphAddMenu):
-        """Contact paragraph add menu"""
-
-        label = _("Contact card...")
-        label_css_class = 'fa fa-fw fa-id-card-o'
-        url = 'add-contact-paragraph.html'
-        paragraph_type = CONTACT_PARAGRAPH_TYPE
-
-
-
-3) Create Edit inner form
--------------------------
-
-.. code-block:: python
-
-    @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
-                permission=MANAGE_CONTENT_PERMISSION)
-    @ajax_config(name='inner-properties.json', context=IContactPhoneParagraph, layer=IPyAMSLayer,
-             base=BaseParagraphAJAXEditForm)
-    @implementer(IInnerForm)
-    class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm):
-        """Contact paragraph inner edit form"""
-
-        legend = None
-
-        @property
-        def buttons(self):
-            if self.mode == INPUT_MODE:
-                return button.Buttons(IParagraphEditFormButtons)
-            else:
-                return button.Buttons()
-
-        def get_ajax_output(self, changes):
-            output = super(ContactParagraphInnerAJAXEditForm, self).get_ajax_output(changes)
-            updated = changes.get(IBaseParagraph, ())
-            if 'title' in updated:
-                output.setdefault('events', []).append(get_json_paragraph_refresh_event(self.context, self.request))
-            updated = changes.get(IContactParagraph, ())
-            if ('photo' in updated) or ('renderer' in updated):
-                # we have to commit transaction to be able to handle blobs...
-                if 'photo' in updated:
-                    ITransactionManager(self.context).get().commit()
-                output.setdefault('events', []).append(get_json_form_refresh_event(self.context, self.request,
-                                                                                   ContactParagraphInnerEditForm))
-            return output
-
-
-4) Create an Edit modal form
------------------------------
-
-This form is used inside modals popup
-
-
-.. code-block:: python
-
-    @ajax_config(name='properties.json', context=IContactPhoneParagraph, request_type=IPyAMSLayer,
-                     permission=MANAGE_CONTENT_PERMISSION, renderer='json', xhr=True)
-    @pagelet_config(name='properties.html', context=IContactParagraph, layer=IPyAMSLayer,
-                    permission=MANAGE_CONTENT_PERMISSION)
-    class ContactPhoneParagraphPropertiesEditForm(BaseParagraphPropertiesEditForm):
-        """Contact phone paragraph properties edit form"""
-
-        prefix = 'contact_properties.'
-
-        legend = _("Edit contact card properties")
-        icon_css_class = 'fa fa-fw fa-id-card-o'
-
-        fields = field.Fields(IContactParagraph).omit('__parent__', '__name__', 'visible')
-        fields['renderer'].widgetFactory = RendererFieldWidget
-
-        edit_permission = MANAGE_CONTENT_PERMISSION
-
-
-.. note::
-
-    Select the new content block types in ZMI to make it available in tools
-
-    .. image:: _static/select_paragraph.png
-
-
-How to associate links or Illustrations to a Paragraph ?
-========================================================
-
-Adding the following marker interface, we add new behavior to the Paragraph `ContactPhoneParagraph` .
-
-
-Paragraph advanced
-""""""""""""""""""
-
-You can attach Associated files, links or illustration and manage them directly through the rubric `Links and Attachments`.
-
-.. image:: _static/select_links_n_attachment.png
-
-
-1) Paragraph with Links and Attachements
-----------------------------------------
-
-To "activate" this features the paragrath object must to implement specific interface
-
-
-.. code-block:: python
-
-    @implementer(IContactPhoneParagraph, IIllustrationTarget,ILinkContainerTarget,IExtFileContainerTarget))
-    @factory_config(provided=IContactPhoneParagraph)
-    class ContactPhoneParagraph(BaseParagraph):
-        """Contact paragraph"""
-            ...
-
-
-These interfaces will allow to link other data to the paragraph.
-
-**Marker interfaces:**
-
-    +--------------------------------+
-    |:py:class:`IIllustrationTarget` |
-    +===================+============+
-    |                   |            |
-    +-------------------+------------+
-
-    +---------------------------------+
-    |:py:class:`ILinkContainerTarget` |
-    +==============+==================+
-    |              | Add internal link|
-    |              +------------------+
-    |              | Add external link|
-    |              +------------------+
-    |              | Add mailto link  |
-    +--------------+------------------+
-
-    +------------------------------------+
-    |:py:class:`IExtFileContainerTarget` |
-    +================+===================+
-    |                | Add external file |
-    |                +-------------------+
-    |                | Add image         |
-    |                +-------------------+
-    |                | Add video         |
-    |                +-------------------+
-    |                | Add audio file    |
-    +----------------+-------------------+
-
-
-**ZMI overview:**
-
-.. image:: _static/select_add_links.png
-
-
-
-2) Add link and association form
---------------------------------
-
-You can add form to manage links and attachments directly in paragraph form to do that your form must implement
-``IPropertiesEditForm`` and/or  ``IAssociationParentForm``
-
-
-.. code-block:: python
-
-    @adapter_config(context=(IContactPhoneParagraph, IPyAMSLayer), provides=IParagraphInnerEditor)
-    @implementer(IInnerForm, IPropertiesEditForm, IAssociationParentForm)
-    class ContactPhoneParagraphInnerEditForm(ContactPhoneParagraphPropertiesEditForm):
-        """Contact paragraph inner edit form"""
-
-        legend = None
-        ajax_handler = 'inner-properties.json'
-
-
-**Marker interfaces:**
-
-+-----------------------------------+
-|:py:class:`IPropertiesEditForm`    |
-+=========+=========================+
-|         | Add Illustration form   |
-+---------+-------------------------+
-
-+-----------------------------------+
-|:py:class:`IAssociationParentForm` |
-+===========+=======================+
-|           | Add Association form  |
-+-----------+-----------------------+
-
-.. image:: _static/associations_form.png
-
--- a/src/source/developer_guide/howto-portlet.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,180 +0,0 @@
-.. _portlethowto:
-
-
-How to create a Portlet?
-========================
-
-**Portlets** are pluggable user interface software components that are managed and displayed in a web portal,
-for example an enterprise portal or a web CMS. A portlet can aggregate (integrate) and personalize content from
-different sources within a web page. A portlet responds to requests from a web client and generates dynamic content
-(*reference:* `Wiki portlet`_).
-
-.. _`wiki portlet`: https://en.wikipedia.org/wiki/Portlet
-
-
-**PyAMS Portal** provides the portal engine but only a very small set of predefined portlets that can be used to compose
-and organize the structure of a web page; additional portlets are provided by other packages, like
-:ref:`pyams_content`.
-
-
-1. Define portlet settings
-''''''''''''''''''''''''''
-
-Portlet settings interface are defined in ``interfaces.py``, you can use :py:class:`pyams_portal.interfaces.IPortletSettings`
-or extend the interface by adding additional properties for example:
-
-.. code-block:: python
-
-    from zope.schema import Text
-
-    NEW_PORTLET_NAME = 'new.portlet'
-
-    class INewPortletSettings(IPortletSettings):
-
-        comment = Text(title=_("Comment"),
-                       required=True)
-
-
-A :py:class:`pyams_portal.portlet.PortletSettings` persistent subclass then implements what IPortletSettings describes:
-
-.. code-block:: python
-
-    @implementer(INewPortletSettings)
-    class NewPortletSettings(PortletSettings):
-        """Portlet settings"""
-
-        comment = FieldProperty(INewPortletSettings['comment'])
-
-
-2. Create Portlet
-'''''''''''''''''
-
-The Portlet component is a utility, which implements the :py:class:`pyams_portal.interfaces.IPortlet` interface and is
-registered by the :py:func:`pyams_portal.portlet.portlet_config` decorator;
-
-.. code-block:: python
-
-    @portlet_config(permission=VIEW_PERMISSION)
-    class ImagePortlet(Portlet):
-        """Image portlet"""
-
-        name = NEW_PORTLET_NAME
-        label = _("New portlet")
-
-        toolbar_image = None
-        toolbar_css_class = 'fa fa-fw fa-2x fa-picture-o'
-
-        settings_class = NewPortletSettings
-
-
-Where:
- - **permission**: permission required to display this portlet content
- - **name**: internal name given to this portlet. This name must be unique between all portlets, so using your own
-   namespace into this name is generally a good option!
- - **label**: user label given to this portlet
- - **toolbar_image**: URL of an image used to display portlet button into ZMI toolbar, if any
- - **toolbar_css_class**: CSS class used to display portlet button into ZMI toolbar, if any
- - **settings_class**: class used to store portlet settings.
-
-
-3. Create portlet settings edit form
-''''''''''''''''''''''''''''''''''''
-
-Portlet settings have to be updated through management interface via a :py:class:`pyams_portal.zmi.portlet.PortletSettingsEditor`
-subform:
-
-.. code-block:: python
-
-    @pagelet_config(name='properties.html', context=INewPortletSettings, layer=IPyAMSLayer,
-                    permission=VIEW_SYSTEM_PERMISSION)
-    class NewPortletSettingsEditor(PortletSettingsEditor):
-        """New portlet settings editor"""
-
-        settings = INewPortletSettings
-
-
-    @adapter_config(name='properties.json', context=(INewPortletSettings, IPyAMSLayer), provides=IPagelet)
-    class NewPortletSettingsAJAXEditor(AJAXEditForm, NewPortletSettingsEditor):
-        """New portlet settings editor, JSON renderer"""
-
-
-4. Previewing portlet content
-'''''''''''''''''''''''''''''
-
-A *previewer* is used into ZMI to display portlet content into portal template definition page:
-
-.. code-block:: python
-
-    @adapter_config(context=(Interface, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletPreviewer)
-    @template_config(template='my-portlet-preview.pt', layer=IPyAMSLayer)
-    class NewPortletPreviewer(PortletPreviewer):
-        """New portlet previewer"""
-
-
-The previewer template is a Chameleon template:
-
-.. code-block:: genshi
-
-    <tal:var define="settings view.settings">
-        <tal:if condition="settings.visible">
-            <div tal:content="settings.comment">Comment</div>
-        </tal:if>
-        <tal:if condition="not settings.visible">
-            <div class="text-center padding-y-5">
-                <span class="fa-stack fa-lg">
-                    <i class="fa fa-eye fa-stack-1x"></i>
-                    <i class="fa fa-ban fa-stack-2x text-danger"></i>
-                </span>
-            </div>
-        </tal:if>
-    </tal:var>
-
-Here we check if portlet is visible or not to display a small icon when hidden; otherwise we display entered comment.
-
-
-5. Rendering portlet content
-''''''''''''''''''''''''''''
-
-A *renderer* is used to display portlet content into rendered page content:
-
-.. code-block:: python
-
-    @adapter_config(context=(IPortalContext, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletRenderer)
-    @template_config(template='my-portlet-render.pt', layer=IPyAMSLayer)
-    class NewPortletRenderer(PortletRenderer):
-        """New portlet renderer"""
-
-        label = _("Default comment renderer")
-
-
-Each renderer template is also a Chameleon template:
-
-.. code-block:: genshi
-
-    <div class="comment" tal:content="view.settings.comment">Comment</div>
-
-
-This is the configuration of a *default* renderer defined for this portlet; you can provide several renderers for a
-given portlet by given distinct names to the adapters:
-
-.. code-block:: python
-
-    @adapter_config(name='another-renderer',
-                    context=(IPortalContext, IPyAMSLayer, Interface, INewPortletSettings),
-                    provides=IPortletRenderer)
-    @template_config(template='my-portlet-render-2.pt', layer=IPyAMSLayer)
-    class AnotherNewPortletRenderer(PortletRenderer):
-        """Another new portlet renderer"""
-
-        label = _("Another comment renderer")
-
-.. tip::
-    You can use an other template without create a new renderer component,
-    with :py:func:`pyams_utils` to override the default template with you own.
-
-
-.. note::
-
-    Select the new portlet in ZMI to make it available in the website template editor
-
-    .. image:: _static/select_portlet.png
--- a/src/source/developer_guide/howto-rename.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-.. _renamehowto:
-
-
-How to rename classes of existing objects ?
-===========================================
-
-
-PyAMS provides `zodbupdate` script to update easily update the change in ZODB. Because, when you develop new features
-to PyAMS we sometimes have to rename or modify classes of already stored objects and if you do that you will never access
-yours objects
-
-
-**1) Include you package in the require resource for you webapp**
-
-
-.. code-block:: python
-
-    requires = [
-        'mypackage',
-        ...
-    ]
-
-**2) Add the Entry points**
-
-
-In the `setup.py` file add 'zodbupdate' params into entry_points where is store zodbupdate directives
-
-.. code-block:: python
-
-      entry_points={
-          'zodbupdate': [
-              'renames = mypackage.generations:RENAMED_CLASSES'
-          ]
-      }
-
-
-
-**3) Create a module named** ``generation`` **with the migration directives**
-
-
-.. code-block:: python
-
-    RENAMED_CLASSES = {
-        'mypackage.mymodule MyOldClass': 'mypackage.mymodule MyNewClass'
-    }
-
-
-**4) Run the commands** ``buildout`` .
-
-.. code-block:: console
-
-    $ mypackage/bin/buildout
-    $ webapp/bin/buildout
-
-
-The ``buildout`` command going to install correctly the package and add the new entry_points references in entry_points.txt
-
-**5)  Run the commands** ``zodbupdate``
-
-To finish run `zodbupdate` to apply the changes describe in `RENAMED_CLASSES`
-
-.. code-block:: console
-
-    $   bin/zodbupdate --config etc/zodbupdate-zeo.conf
-
-
-Where `zodbupdate-zeo.conf` is the configuration file to access to the ZODB.
--- a/src/source/developer_guide/howto-renderer.rst	Tue Dec 11 16:43:45 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/developer_guide/howto-skin.rst	Tue Dec 11 16:43:45 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/developer_guide/howto-template.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-.. _templatehowto:
-
-
-How to define or change a template for a specific skin?
-=======================================================
-
-Override the default template for a renderer
---------------------------------------------
-
-If you want to modify the template for a particular rendering mode, you can use the function :py:func:`override_template`
-
-.. code-block:: python
-
-	from pyams_template.template import override_template
-
-	from my_website.skin.public.layer import ICustomLayer
-	from pyams_default_theme.component.keynumber.portlet import KeyNumberPortletHorizontalRenderer
-
-
-	override_template(context=KeyNumberPortletHorizontalRenderer,
-					template="templates/keynumber-horizontal.pt",
-					layer=ICustomLayer
-					)
-
-
-This new template can be applied to a particular :ref:`Skin <skinhowto>` by specifying on which layer to use this renderer
-*(ex: layer=IMyWebsiteLayer)*
-
-
-
-Redefine the default template for a renderer
---------------------------------------------
-
-You must redefine an adapter to add new variables or static resources for your new template,
-
-.. code-block:: python
-
-	# import interfaces
-	from my_website.skin.public.layer import ICustomLayer
-
-	from pyams_content.component.keynumber.portlet.interfaces import IKeyNumberPortletSettings
-	from pyams_portal.interfaces import IPortletRenderer, IPortalContext
-
-	# import packages
-	from my_website.skin.public import my_carousel_init ,my_carousel_animation
-
-	from pyams_default_theme.component.keynumber.portlet import KeyNumberPortletHorizontalRenderer
-	from pyams_template.template import template_config
-	from pyams_utils.adapter import adapter_config
-	from zope.interface import Interface
-
-
-	@adapter_config(context=(IPortalContext, IBaseLayer, Interface, IKeyNumberPortletSettings),
-	                provides=IPortletRenderer)
-	@template_config(template='templates/keynumber-horizontal.pt', layer=ICustomLayer)
-	class MyCustomKeyNumberPortletHorizontalRenderer(KeyNumberPortletHorizontalRenderer):
-		"""Key numbers portlet horizontal renderer"""
-
-		resources = (my_carousel_init, my_carousel_animation)
-
-
-The attribute :py:attr:`resources` is used to load in the template static resources. The application will automatically
-integrate resource content when the template is calling.
--- a/src/source/developer_guide/introduction.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-.. _introduction:
-
-Why should i use PyAMS?
------------------------
-
-Based on the python langage, **PyAMS** supply powerfull tools like *PyAMS_content*, this package
-providing content management features, which can be easily extended to manage your own content types.
-But PyAMS can also be used to manage any kind of application.
-
-In addition **PyAMS** is built on top of **MyAMS** (**My** **\A**\pplication **\M**\anagement **\S**\kin), a small web client framework built on top of JQuery
-and Bootstrap, which was developed by the french national forestry office (ONF -- Office national des forêts --
-http://www.onf.fr) to build web applications in several languages (actually java, Python and PHP). The new ONF website
-is now completely handled with PyAMS framework.
-
-PyAMS is a multipurpose set of packages, providing tools including:
-
-    * custom interfaces
-    * custom registry annotations
-    * custom security policy
-    * local registry support
-    * network protocols utilities (for HTTP and XML-RPC)
-    * command line scripts
-    * custom utilities.
-
-
-.. tip::
-    Some screenshots of the admin interface are available at :ref:`screenshots`!
-
-
-How to install PyAMS?
----------------------
-
-If you want to create a PyAMS application instance easily, just follow the :ref:`quickstart` guide!
-If you want learn more about a custom installation follow :ref:`install` procedure.
-
-
--- a/src/source/developer_guide/tales.rst	Tue Dec 11 16:43:45 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)
--- a/src/source/developer_guide/traverser.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-.. _traverser:
-
-PyAMS namespace traverser
-=========================
-
-PyAMS_utils provide a custom URL traverser, defined in package :py:mod:`pyams_utils.traversing`.
-
-The :py:class:`NamespaceTraverser <pyams_utils.traversing.NamespaceTraverser>` is a custom traverser based on default
-Pyramid's *ResourceTreeAdapter*, but it adds the ability to use *namespaces*. Inherited from *Zope3* concept, a
-namespace is a resource path element starting with the « *++* » characters, like this:
-
-.. code-block:: none
-
-    http://localhost:5432/folder/content/++ns++argument/@@view.html
-
-In this sample, *ns* is the namespace name. When the traverser detects a namespace, it looks for several named
-adapters (or multi-adapters) to the :py:class:`ITraversable <zope.traversing.interfaces.ITraversable>` interface
-defined in *zope.traversing* package. Adapters lookup with name *ns* is done for the current *context* and *request*,
-then only for the context and finally for the request, in this order. If a traversing adapter is found, it's
-:py:func:`traverse` method is called, with the *attr* value as first argument, and the rest of the traversal stack
-as second one.
-
-This is for example how a custom *etc* namespace traverser is defined:
-
-.. code-block:: python
-
-    from pyams_utils.interfaces.site import ISiteRoot
-    from zope.traversing.interfaces import ITraversable
-
-    from pyams_utils.adapter import adapter_config, ContextAdapter
-
-    @adapter_config(name='etc', context=ISiteRoot, provides=ITraversable)
-    class SiteRootEtcTraverser(ContextAdapter):
-        """Site root ++etc++ namespace traverser"""
-
-        def traverse(self, name, furtherpath=None):
-            if name == 'site':
-                return self.context.getSiteManager()
-            raise NotFound
-
-By using an URL like '++etc++site' on your site root, you can then get access to your local site manager.
-
-*argument* is not mandatory for the namespace traverser. If it is not provided, the *traverse* method is called with
-an empty string (with is a default adapter name) as first argument.
-
-Several PyAMS components use custom traversal adapters. For example, getting thumbnails from an image is done
-through a traversing adapter, which results in nicer URLs than when using classic URLs with arguments...
--- a/src/source/developer_guide/utilities.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-.. _utilities:
-
-Custom PyAMS utilities
-======================
-
-PyAMS_utils provides a small set of utilities. You can create some of them as global utilities registered in
-the global components registry; other ones can be created manually by a site administrator and
-are then registered automatically.
-
-
-Server timezone
----------------
-
-To manage timezones correctly, and display datetimes based on current server timezone, all datetimes should
-be defined and stored in UTC.
-
-PyAMS_utils provides a :py:class:`ServerTimezoneUtility <pyams_utils.timezone.utility.ServerTimezoneUtility>` which
-allows you to assign a default timezone to your server.
-
-To display a datetime with correct timezone, you can use the :py:func:`tztime <pyams_utils.timezone.tztime>` function,
-which assign server timezone to the given parameter:
-
-.. code-block:: python
-
-    from datetime import datetime
-    from pyams_utils.timezone import tztime
-
-    now = datetime.utcnow()
-    my_date = tztime(now)  # converts *now* to server timezone
-
-We could imagine that datetimes could be displayed with current user timezone. But it's quite impossible to know
-the user timazone from a server request. The only options are:
-
-- you ask an authenticated user to update a timezone setting in his profile
-
-- you can include Javascript libraries which will try to detect browser timezone from their computer configuration, and
-  do an AJAX request to update data in their session.
-
-That should require an update of :py:func:`tzinfo` adapter to get timezone info from session, request or user profile.
-
-
-ZEO connection
---------------
-
-Several PyAMS utilities (like the tasks scheduler or the medias converter) are working with dedicated processes,
-are connected to main PyAMS process through ØMQ, and use ZEO connections for their PyAMS database access.
-
-Clients of these processes have to send settings of the ZEO connections that they should use.
-
-The ZEOConnection utility can be created by the site manager through the web management interface (ZMI) from the
-*Control panel*:
-
-.. image:: _static/zeo-add-menu.png
-
-ZEO connection creation form allows you to define all settings of a ZEO connection:
-
-.. image:: _static/zeo-add-form.png
--- a/src/source/developer_guide/zca.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,292 +0,0 @@
-.. _zca:
-
-Zope Component Architecture with PyAMS
-++++++++++++++++++++++++++++++++++++++
-
-PyAMS packages are developed based on the **Zope Component Architecture** (aka **ZCA**). ZCA is used by the Pyramid framework
-"under the hood" to handle interfaces, adapters and utilities. You don't **have to** use it in your own applications.
-But you can.
-
-The ZCA is mainly adding elements like **interfaces**, **adapters** and **utilities** to the Python language. It
-allows you to write a framework or an application by using **components** which can be extended easily.
-
-Interfaces
-    Interfaces are objects that specify (document) the external behavior
-    of objects that "provide" them.  An interface specifies behavior through, a documentation in a doc string,
-    attribute definitions and conditions of attribute values.
-
-Components
-    Components are objects that are associated with interfaces.
-
-Utilities
-    Utilities are just components that provide an interface and that are looked up by an interface and a name
-
-Adapters
-    Adapters are components that are computed from other components to adapt them to some interface.
-    Because they are computed from other objects, they are provided as factories, usually classes.
-
-
-You will find several useful resources about ZCA concepts on the internet.
-
-.. seealso::
-    Zope Documentations:
-        - `Components and Interfaces <http://zope.readthedocs.io/en/latest/zdgbook/ComponentsAndInterfaces.html>`_
-        - `Zope component <http://zopecomponent.readthedocs.io/en/latest/narr.html>`_
-        - `Zope interface <https://docs.zope.org/zope.interface/README.html>`_
-
-
-Utilities
----------
-
-Local utilities
-'''''''''''''''
-
-In ZCA, a **utility** is a **registered** component which provides an **interface**. This interface is the
-**contract** which defines features (list of attributes and methods) provided by the component which implements it.
-
-When a Pyramid application starts, a **global registry** is created to register a whole set of utilities and
-adapters; this registration can be done via ZCML directives or via native Python code.
-In addition, PyAMS allows you to define **local utilities**, which are stored and registered in the ZODB via a
-**site manager**.
-
-
-Registering local utilities
-'''''''''''''''''''''''''''
-
-
-.. tip::
-
-    :ref:`site` can be used to store **local utilities** whose configuration, which is easily
-    available to site administrators through management interface, is stored in the ZODB.
-
-
-A local utility is a persistent object, registered in a *local site manager*, and providing a specific interface (if
-a component provides several interfaces, it can be registered several times).
-
-Some components can be required by a given package, and created automatically via the *pyams_upgrade* command line
-script; this process relies on the *ISiteGenerations* interface, for example for the timezone utility, a component
-provided by PyAMS_utils package to handle server timezone and display times correctly:
-
-.. code-block:: python
-
-    from pyams_utils.interfaces.site import ISiteGenerations
-    from pyams_utils.interfaces.timezone import IServerTimezone
-
-    from persistent import Persistent
-    from pyams_utils.registry import utility_config
-    from pyams_utils.site import check_required_utilities
-    from pyramid.events import subscriber
-    from zope.container.contained import Contained
-    from zope.interface import implementer
-    from zope.schema.fieldproperty import FieldProperty
-
-    @implementer(IServerTimezone)
-    class ServerTimezoneUtility(Persistent, Contained):
-
-        timezone = FieldProperty(IServerTimezone['timezone'])
-
-    REQUIRED_UTILITIES = ((IServerTimezone, '', ServerTimezoneUtility, 'Server timezone'),)
-
-    @subscriber(INewLocalSite)
-    def handle_new_local_site(event):
-        """Create a new ServerTimezoneUtility when a site is created"""
-        site = event.manager.__parent__
-        check_required_utilities(site, REQUIRED_UTILITIES)
-
-    @utility_config(name='PyAMS timezone', provides=ISiteGenerations)
-    class TimezoneGenerationsChecker(object):
-        """Timezone generations checker"""
-
-        generation = 1
-
-        def evolve(self, site, current=None):
-            """Check for required utilities"""
-            check_required_utilities(site, REQUIRED_UTILITIES)
-
-Some utilities can also be created manually by an administrator through the management interface, and registered
-automatically after their creation. For example, this is how a ZEO connection utility (which is managing settings to
-define a ZEO connection) is registered:
-
-.. code-block:: python
-
-    from pyams_utils.interfaces.site import IOptionalUtility
-    from pyams_utils.interfaces.zeo import IZEOConnection
-    from zope.annotation.interfaces import IAttributeAnnotatable
-    from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectRemovedEvent
-
-    from persistent import Persistent
-    from pyramid.events import subscriber
-    from zope.container.contained import Contained
-
-    @implementer(IZEOConnection)
-    class ZEOConnection(object):
-        """ZEO connection object. See source code to get full implementation..."""
-
-    @implementer(IOptionalUtility, IAttributeAnnotatable)
-    class ZEOConnectionUtility(ZEOConnection, Persistent, Contained):
-        """Persistent ZEO connection utility"""
-
-    @subscriber(IObjectAddedEvent, context_selector=IZEOConnection)
-    def handle_added_connection(event):
-        """Register new ZEO connection when added"""
-        manager = event.newParent
-        manager.registerUtility(event.object, IZEOConnection, name=event.object.name)
-
-    @subscriber(IObjectRemovedEvent, context_selector=IZEOConnection)
-    def handle_removed_connection(event):
-        """Un-register ZEO connection when deleted"""
-        manager = event.oldParent
-        manager.unregisterUtility(event.object, IZEOConnection, name=event.object.name)
-
-*context_selector* is a custom subscriber predicate, so that subscriber event is activated only if object concerned
-by an event is providing given interface.
-
-
-Registering global utilities
-''''''''''''''''''''''''''''
-
-**Global utilities** are components providing an interface which are registered in the global registry.
-PyAMS_utils package provides custom annotations to register global utilities without using ZCML. For example, a skin
-is nothing more than a simple utility providing the *ISkin* interface:
-
-.. code-block:: python
-
-    from pyams_default_theme.layer import IPyAMSDefaultLayer
-    from pyams_skin.interfaces import ISkin
-    from pyams_utils.registry import utility_config
-
-    @utility_config(name='PyAMS default skin', provides=ISkin)
-    class PyAMSDefaultSkin(object):
-        """PyAMS default skin"""
-
-        label = _("PyAMS default skin")
-        layer = IPyAMSDefaultLayer
-
-This annotation registers a utility, named *PyAMS default skin*, providing the *ISkin* interface. It's the developer
-responsibility to provide all attributes and methods required by the provided interface.
-
-
-Looking for utilities
-'''''''''''''''''''''
-
-ZCA provides the *getUtility* and *queryUtility* functions to look for a utility. But these methods only applies to
-global registry.
-
-PyAMS package provides equivalent functions, which are looking for components into local registry before looking into
-the global one. For example:
-
-.. code-block:: python
-
-    from pyams_security.interfaces import ISecurityManager
-    from pyams_utils.registry import query_utility
-
-    manager = query_utility(ISecurityManager)
-    if manager is not None:
-        print("Manager is there!")
-
-All ZCA utility functions have been ported to use local registry: *registered_utilities*, *query_utility*,
-*get_utility*, *get_utilities_for*, *get_all_utilities_registered_for* functions all follow the equivalent ZCA
-functions API, but are looking for utilities in the local registry before looking in the global registry.
-
-
-Adapters
---------
-
-Registering adapters
-''''''''''''''''''''
-
-An adapter is also a kind of utility. But instead of *just* providing an interface, it adapts an input object,
-providing a given interface, to provide another interface. An adapter can also be named, so that you can choose which
-adapter to use at a given time.
-
-PyAMS_utils provide another annotation, to help registering adapters without using ZCML files. An adapter can be a
-function which directly returns an object providing the requested interface, or an object which provides the interface.
-
-The first example is an adapter which adapts any persistent object to get it's associated transaction manager:
-
-.. code-block:: python
-
-    from persistent.interfaces import IPersistent
-    from transaction.interfaces import ITransactionManager
-    from ZODB.interfaces import IConnection
-
-    from pyams_utils.adapter import adapter_config
-
-    @adapter_config(context=IPersistent, provides=ITransactionManager)
-    def get_transaction_manager(obj):
-        conn = IConnection(obj)
-        try:
-            return conn.transaction_manager
-        except AttributeError:
-            return conn._txn_mgr
-
-This is another adapter which adapts any contained object to the *IPathElements* interface; this interface can be
-used to build index that you can use to find objects based on a parent object:
-
-.. code-block:: python
-
-    from pyams_utils.interfaces.traversing import IPathElements
-    from zope.intid.interfaces import IIntIds
-    from zope.location.interfaces import IContained
-
-    from pyams_utils.adapter import ContextAdapter
-    from pyams_utils.registry import query_utility
-    from pyramid.location import lineage
-
-    @adapter_config(context=IContained, provides=IPathElements)
-    class PathElementsAdapter(ContextAdapter):
-        """Contained object path elements adapter"""
-
-        @property
-        def parents(self):
-            intids = query_utility(IIntIds)
-            if intids is None:
-                return []
-            return [intids.register(parent) for parent in lineage(self.context)]
-
-An adapter can also be a multi-adapter, when several input objects are requested to provide a given interface. For
-example, many adapters require a context and a request, eventually a view, to provide another feature. This is how,
-for example, we define a custom *name* column in a security manager table displaying a list of plug-ins:
-
-.. code-block:: python
-
-    from pyams_zmi.layer import IAdminLayer
-    from z3c.table.interfaces import IColumn
-
-    from pyams_skin.table import I18nColumn
-    from z3c.table.column import GetAttrColumn
-
-    @adapter_config(name='name', context=(Interface, IAdminLayer, SecurityManagerPluginsTable), provides=IColumn)
-    class SecurityManagerPluginsNameColumn(I18nColumn, GetAttrColumn):
-        """Security manager plugins name column"""
-
-        _header = _("Name")
-        attrName = 'title'
-        weight = 10
-
-As you can see, adapted objects can be given as functions or as classes.
-
-
-Vocabularies
-------------
-
-Registering vocabularies
-''''''''''''''''''''''''
-
-A **vocabulary** is a custom factory which can be used as source for several field types, like *choices* or *lists*.
-Vocabularies have to be registered in a custom registry, so PyAMS_utils provide another annotation to register them.
-This example is based on the *Timezone* component which allows you to select a timezone between a list of references:
-
-.. code-block:: python
-
-    import pytz
-    from pyams_utils.vocabulary import vocabulary_config
-    from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
-
-    @vocabulary_config(name='PyAMS timezones')
-    class TimezonesVocabulary(SimpleVocabulary):
-        """Timezones vocabulary"""
-
-        def __init__(self, *args, **kw):
-            terms = [SimpleTerm(t, t, t) for t in pytz.all_timezones]
-            super(TimezonesVocabulary, self).__init__(terms)
--- a/src/source/index.rst	Tue Dec 11 16:43:45 2018 +0100
+++ b/src/source/index.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -25,10 +25,10 @@
    :maxdepth: 2
    :caption: Administrator Guide
 
-   PyAMS Architectures <administrator_guide/architecture>
-   PyAMS Security model <administrator_guide/security>
-   Installing PyAMS <administrator_guide/appinstall>
-   Database initialization
+   PyAMS Architectures <admin_guide/architecture>
+   PyAMS Security model <admin_guide/security>
+   Installing PyAMS <admin_guide/appinstall>
+   Database initialization <admin_guide/appinstall>
    Database maintenance tasks
    Scheduling tasks
    Command line scripts
@@ -57,7 +57,7 @@
    :maxdepth: 2
    :caption:  Developer Guide
 
-   Understanding the Zope Component Architecture <developer_guide/zca>
+   Understanding the Zope Component Architecture <dev_guide/zca>
    Understanding PyAMS internals
    Creating new PyAMS Packages
    Overriding PyAMS features
@@ -75,9 +75,9 @@
    :maxdepth: 2
    :caption:  Reference Guide
 
-   PyAMS packages layout <reference_guide/package_layout>
-   PyAMS packages references <reference_guide/packages>
-   PyAMs Test <reference_guide/test>
+   PyAMS packages layout <ref_guide/package_layout>
+   PyAMS packages references <ref_guide/packages>
+   PyAMs Test <ref_guide/test>
 
 
 .. toctree::
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/ref_guide/package_layout.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,23 @@
+.. _package_layout:
+
+PyAMS Package directory layout
+------------------------------
+
+.. code-block:: bash
+
+
+    ├── pyams_<package>/
+    │   ├── doctests/       <- Documentation for the package
+    |   ├── generation/     <- ZODB Migration directives
+    │   ├── interfaces/     <- Interfaces definition
+    │   ├── locales/        <- Store source code translation files (.mo .pot)
+    │   ├── tests/          <- Contains python scripts for running tests including test runners, unit test
+    │   ├── zmi/            <- ZMI subpackage to register and define elements in admin interface
+    │   ├── __init__.py
+    │   ├── include.py      <- Register Pyramid directives
+    │   ├── configure.zcml  <- Overload default Zope config directive (optional)
+    :   :.. <module>.py     <- source code
+    :   :
+    │   └── site.py
+
+    ...
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/ref_guide/packages.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,60 @@
+.. _packages:
+
+PyAMS packages reference
+========================
+
+
+Core packages
+-------------
+
+.. toctree::
+   :maxdepth: 1
+
+   PyAMS Template <_api-doc/pyams_template/pyams_template>
+   PyAMS Viewlet <_api-doc/pyams_viewlet/pyams_viewlet>
+   PyAMS Pagelet <_api-doc/pyams_pagelet/pyams_pagelet>
+   PyAMS Utils <_api-doc/pyams_utils/pyams_utils>
+   PyAMS Skin <_api-doc/pyams_skin/pyams_skin>
+   PyAMS Form <_api-doc/pyams_form/pyams_form>
+   PyAMS File <_api-doc/pyams_file/pyams_file>
+   PyAMS I18N <_api-doc/pyams_i18n/pyams_i18n>
+   PyAMS Security <_api-doc/pyams_security/pyams_security>
+   PyAMS Catalog <_api-doc/pyams_catalog/pyams_catalog>
+   PyAMS Mail <_api-doc/pyams_mail/pyams_mail>
+   PyAMS Cache <_api-doc/pyams_cache/pyams_cache>
+   PyAMS ZMQ <_api-doc/pyams_zmq/pyams_zmq>
+   PyAMS Scheduler <_api-doc/pyams_scheduler/pyams_scheduler>
+   PyAMS ZMI <_api-doc/pyams_zmi/pyams_zmi>
+
+
+CMS Packages
+------------
+
+.. toctree::
+   :maxdepth: 1
+
+   PyAMS Sequence <_api-doc/pyams_sequence/pyams_sequence>
+   PyAMS Workflow <_api-doc/pyams_workflow/pyams_workflow>
+   PyAMS Thesaurus <_api-doc/pyams_thesaurus/pyams_thesaurus>
+   PyAMS Portal <_api-doc/pyams_portal/pyams_portal>
+   PyAMS Content <_api-doc/pyams_content/pyams_content>
+   PyAMS Default theme <_api-doc/pyams_default_theme/pyams_default_theme>
+
+
+Optional packages
+-----------------
+
+PyAMS is built on many additional packages, and provides a whole set of extensions. Here is a list of them:
+
+.. toctree::
+   :maxdepth: 1
+
+   PyAMS Content with ElasticSearch <_api-doc/pyams_content_es/pyams_content_es>
+   PyAMS APM  <_api-doc/pyams_alchemy/pyams_apm>
+   PyAMS ZODBbrowser <_api-doc/pyams_zodbbrowser/pyams_zodbbrowser>
+   PyAMS LDAP <_api-doc/pyams_ldap/pyams_ldap>
+   PyAMS GIS <_api-doc/pyams_gis/pyams_gis>
+   PyAMS Alchemy  <_api-doc/pyams_alchemy/pyams_alchemy>
+   PyAMS Media <_api-doc/pyams_media/pyams_media>
+   PyAMS Notify <_api-doc/pyams_notify/pyams_notify>
+   PyAMS Notify with WebSocket<_api-doc/pyams_notify_ws/pyams_notify_ws>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/source/ref_guide/tests.rst	Tue Dec 11 17:00:42 2018 +0100
@@ -0,0 +1,34 @@
+.. _tests:
+
+PyAMS tests
+===========
+
+.. toctree::
+   :maxdepth: 1
+
+   PyAMS Alchemy tests <_api-doc/pyams_alchemy/pyams_alchemy.tests>
+   PyAMS Cache tests <_api-doc/pyams_cache/pyams_cache.tests>
+   PyAMS Catalog tests <_api-doc/pyams_catalog/pyams_catalog.tests>
+   PyAMS Content tests <_api-doc/pyams_content/pyams_content.tests>
+   PyAMS Content_es tests <_api-doc/pyams_content_es/pyams_content_es.tests>
+   PyAMS File tests <_api-doc/pyams_file/pyams_file.tests>
+   PyAMS Form tests <_api-doc/pyams_form/pyams_form.tests>
+   PyAMS GIS tests <_api-doc/pyams_gis/pyams_gis.tests>
+   PyAMS LDAP tests <_api-doc/pyams_ldap/pyams_ldap.tests>
+   PyAMS Mail tests <_api-doc/pyams_mail/pyams_mail.tests>
+   PyAMS Media tests <_api-doc/pyams_media/pyams_media.tests>
+   PyAMS Notify tests <_api-doc/pyams_notify/pyams_notify.tests>
+   PyAMS Pagelet tests <_api-doc/pyams_pagelet/pyams_pagelet.tests>
+   PyAMS Portal tests <_api-doc/pyams_portal/pyams_portal.tests>
+   PyAMS Scheduler tests <_api-doc/pyams_scheduler/pyams_scheduler.tests>
+   PyAMS Security tests <_api-doc/pyams_security/pyams_security.tests>
+   PyAMS Sequence tests <_api-doc/pyams_sequence/pyams_sequence.tests>
+   PyAMS Skin tests <_api-doc/pyams_skin/pyams_skin.tests>
+   PyAMS Template tests <_api-doc/pyams_template/pyams_template.tests>
+   PyAMS Thesaurus tests <_api-doc/pyams_thesaurus/pyams_thesaurus.tests>
+   PyAMS Utils tests <_api-doc/pyams_utils/pyams_utils.tests>
+   PyAMS Viewlet tests <_api-doc/pyams_viewlet/pyams_viewlet.tests>
+   PyAMS Workflow tests <_api-doc/pyams_workflow/pyams_workflow.tests>
+   PyAMS ZMI tests <_api-doc/pyams_zmi/pyams_zmi.tests>
+   PyAMS ZMQ tests <_api-doc/pyams_zmq/pyams_zmq.tests>
+   PyAMS ZODBbrowser tests <_api-doc/pyams_zodbbrowser/pyams_zodbbrowser.tests>
--- a/src/source/reference_guide/package_layout.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-.. _package_layout:
-
-PyAMS Package directory layout
-------------------------------
-
-.. code-block:: bash
-
-
-    ├── pyams_<package>/
-    │   ├── doctests/       <- Documentation for the package
-    |   ├── generation/     <- ZODB Migration directives
-    │   ├── interfaces/     <- Interfaces definition
-    │   ├── locales/        <- Store source code translation files (.mo .pot)
-    │   ├── tests/          <- Contains python scripts for running tests including test runners, unit test
-    │   ├── zmi/            <- ZMI subpackage to register and define elements in admin interface
-    │   ├── __init__.py
-    │   ├── include.py      <- Register Pyramid directives
-    │   ├── configure.zcml  <- Overload default Zope config directive (optional)
-    :   :.. <module>.py     <- source code
-    :   :
-    │   └── site.py
-
-    ...
--- a/src/source/reference_guide/packages.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-.. _packages:
-
-PyAMS packages reference
-========================
-
-
-Core packages
--------------
-
-.. toctree::
-   :maxdepth: 1
-
-   PyAMS Template <_api-doc/pyams_template/pyams_template>
-   PyAMS Viewlet <_api-doc/pyams_viewlet/pyams_viewlet>
-   PyAMS Pagelet <_api-doc/pyams_pagelet/pyams_pagelet>
-   PyAMS Utils <_api-doc/pyams_utils/pyams_utils>
-   PyAMS Skin <_api-doc/pyams_skin/pyams_skin>
-   PyAMS Form <_api-doc/pyams_form/pyams_form>
-   PyAMS File <_api-doc/pyams_file/pyams_file>
-   PyAMS I18N <_api-doc/pyams_i18n/pyams_i18n>
-   PyAMS Security <_api-doc/pyams_security/pyams_security>
-   PyAMS Catalog <_api-doc/pyams_catalog/pyams_catalog>
-   PyAMS Mail <_api-doc/pyams_mail/pyams_mail>
-   PyAMS Cache <_api-doc/pyams_cache/pyams_cache>
-   PyAMS ZMQ <_api-doc/pyams_zmq/pyams_zmq>
-   PyAMS Scheduler <_api-doc/pyams_scheduler/pyams_scheduler>
-   PyAMS ZMI <_api-doc/pyams_zmi/pyams_zmi>
-
-
-CMS Packages
-------------
-
-.. toctree::
-   :maxdepth: 1
-
-   PyAMS Sequence <_api-doc/pyams_sequence/pyams_sequence>
-   PyAMS Workflow <_api-doc/pyams_workflow/pyams_workflow>
-   PyAMS Thesaurus <_api-doc/pyams_thesaurus/pyams_thesaurus>
-   PyAMS Portal <_api-doc/pyams_portal/pyams_portal>
-   PyAMS Content <_api-doc/pyams_content/pyams_content>
-   PyAMS Default theme <_api-doc/pyams_default_theme/pyams_default_theme>
-
-
-Optional packages
------------------
-
-PyAMS is built on many additional packages, and provides a whole set of extensions. Here is a list of them:
-
-.. toctree::
-   :maxdepth: 1
-
-   PyAMS Content with ElasticSearch <_api-doc/pyams_content_es/pyams_content_es>
-   PyAMS APM  <_api-doc/pyams_alchemy/pyams_apm>
-   PyAMS ZODBbrowser <_api-doc/pyams_zodbbrowser/pyams_zodbbrowser>
-   PyAMS LDAP <_api-doc/pyams_ldap/pyams_ldap>
-   PyAMS GIS <_api-doc/pyams_gis/pyams_gis>
-   PyAMS Alchemy  <_api-doc/pyams_alchemy/pyams_alchemy>
-   PyAMS Media <_api-doc/pyams_media/pyams_media>
-   PyAMS Notify <_api-doc/pyams_notify/pyams_notify>
-   PyAMS Notify with WebSocket<_api-doc/pyams_notify_ws/pyams_notify_ws>
--- a/src/source/reference_guide/tests.rst	Tue Dec 11 16:43:45 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,34 +0,0 @@
-.. _tests:
-
-PyAMS tests
-===========
-
-.. toctree::
-   :maxdepth: 1
-
-   PyAMS Alchemy tests <_api-doc/pyams_alchemy/pyams_alchemy.tests>
-   PyAMS Cache tests <_api-doc/pyams_cache/pyams_cache.tests>
-   PyAMS Catalog tests <_api-doc/pyams_catalog/pyams_catalog.tests>
-   PyAMS Content tests <_api-doc/pyams_content/pyams_content.tests>
-   PyAMS Content_es tests <_api-doc/pyams_content_es/pyams_content_es.tests>
-   PyAMS File tests <_api-doc/pyams_file/pyams_file.tests>
-   PyAMS Form tests <_api-doc/pyams_form/pyams_form.tests>
-   PyAMS GIS tests <_api-doc/pyams_gis/pyams_gis.tests>
-   PyAMS LDAP tests <_api-doc/pyams_ldap/pyams_ldap.tests>
-   PyAMS Mail tests <_api-doc/pyams_mail/pyams_mail.tests>
-   PyAMS Media tests <_api-doc/pyams_media/pyams_media.tests>
-   PyAMS Notify tests <_api-doc/pyams_notify/pyams_notify.tests>
-   PyAMS Pagelet tests <_api-doc/pyams_pagelet/pyams_pagelet.tests>
-   PyAMS Portal tests <_api-doc/pyams_portal/pyams_portal.tests>
-   PyAMS Scheduler tests <_api-doc/pyams_scheduler/pyams_scheduler.tests>
-   PyAMS Security tests <_api-doc/pyams_security/pyams_security.tests>
-   PyAMS Sequence tests <_api-doc/pyams_sequence/pyams_sequence.tests>
-   PyAMS Skin tests <_api-doc/pyams_skin/pyams_skin.tests>
-   PyAMS Template tests <_api-doc/pyams_template/pyams_template.tests>
-   PyAMS Thesaurus tests <_api-doc/pyams_thesaurus/pyams_thesaurus.tests>
-   PyAMS Utils tests <_api-doc/pyams_utils/pyams_utils.tests>
-   PyAMS Viewlet tests <_api-doc/pyams_viewlet/pyams_viewlet.tests>
-   PyAMS Workflow tests <_api-doc/pyams_workflow/pyams_workflow.tests>
-   PyAMS ZMI tests <_api-doc/pyams_zmi/pyams_zmi.tests>
-   PyAMS ZMQ tests <_api-doc/pyams_zmq/pyams_zmq.tests>
-   PyAMS ZODBbrowser tests <_api-doc/pyams_zodbbrowser/pyams_zodbbrowser.tests>