# HG changeset patch # User Damien Correia # Date 1545063575 -3600 # Node ID 7e69ecc9fd4304574efda139d5fe73a77533caba # Parent 98a84761634ff08746c9d0f80d3d8b352637ebf7 Updated doc diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/adv-features.rst --- a/src/source/dev_guide/adv-features.rst Fri Dec 14 12:16:12 2018 +0100 +++ b/src/source/dev_guide/adv-features.rst Mon Dec 17 17:19:35 2018 +0100 @@ -11,6 +11,68 @@ Renaming persistent classes --------------------------- +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. + +To rename classes of existing 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. + Including SQLAlchemy -------------------- diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/dev-form.rst --- a/src/source/dev_guide/dev-form.rst Fri Dec 14 12:16:12 2018 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -.. _devform: - -Understanding PyAMS form -======================== diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/howto-form.rst --- a/src/source/dev_guide/howto-form.rst Fri Dec 14 12:16:12 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 - diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/howto-paragraph.rst --- a/src/source/dev_guide/howto-paragraph.rst Fri Dec 14 12:16:12 2018 +0100 +++ b/src/source/dev_guide/howto-paragraph.rst Mon Dec 17 17:19:35 2018 +0100 @@ -100,31 +100,16 @@ Paragraph in the ZMI """""""""""""""""""" - -To display and manage the new paragraph in the ZMI, you should create this associated forms +1) Container Paragraph +---------------------- -1) Paragraph factory --------------------- +For example :py:class:`IParagraphContainerTarget`, it's a marker interface for paragraph containers. 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 +`IParagraphContainerTarget`, it's the default marker interface container for all paragraphs. +We could use this interface as context to declare a new pagelet. - @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 +Definition of a Contact Phone form to create a new ContactPhone object .. code-block:: python @@ -152,6 +137,8 @@ IParagraphContainer(self.context).append(object) +To display and manage the new paragraph in the ZMI, you should create this associated forms + 2) Create the Paragraph addform button in the menu -------------------------------------------------- @@ -209,7 +196,7 @@ 4) Create an Edit modal form ------------------------------ +---------------------------- This form is used inside modals popup @@ -241,6 +228,27 @@ .. image:: ../_static/select_paragraph.png +5) Paragraph Factory +-------------------- + +If you want to create automatically a paragraph object for a shared content you must define a 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 + +When the contributors will create new shared content with predefined paragraphs, +it's the container factory class that will be used to create the paragraph object. + + How to associate links or Illustrations to a Paragraph ? ======================================================== diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/howto-portlet.rst --- a/src/source/dev_guide/howto-portlet.rst Fri Dec 14 12:16:12 2018 +0100 +++ b/src/source/dev_guide/howto-portlet.rst Mon Dec 17 17:19:35 2018 +0100 @@ -17,12 +17,45 @@ :ref:`pyams_content`. -1. Define portlet settings -'''''''''''''''''''''''''' +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' + + +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 + +NB: the argument **settings_class** could be set to add a portlet settings (see below). + + +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: +1) create portlet settings +^^^^^^^^^^^^^^^^^^^^^^^^^^ + .. code-block:: python from zope.schema import Text @@ -46,11 +79,10 @@ comment = FieldProperty(INewPortletSettings['comment']) -2. Create Portlet -''''''''''''''''' +2. declare the settings 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; +Add the settings portlet class inside the portlet .. code-block:: python @@ -63,24 +95,14 @@ 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. portlet settings edit form +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -3. Create portlet settings edit form -'''''''''''''''''''''''''''''''''''' - -Portlet settings have to be updated through management interface via a :py:class:`pyams_portal.zmi.portlet.PortletSettingsEditor` +Portlet settings have to be updated through management interface (ZMI) via a :py:class:`pyams_portal.zmi.portlet.PortletSettingsEditor` subform: .. code-block:: python @@ -98,8 +120,8 @@ """New portlet settings editor, JSON renderer""" -4. Previewing portlet content -''''''''''''''''''''''''''''' +Previewing portlet content +-------------------------- A *previewer* is used into ZMI to display portlet content into portal template definition page: @@ -132,8 +154,8 @@ 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 -'''''''''''''''''''''''''''' +Rendering portlet content +------------------------- A *renderer* is used to display portlet content into rendered page content: diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/howto-rename.rst --- a/src/source/dev_guide/howto-rename.rst Fri Dec 14 12:16:12 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. diff -r 98a84761634f -r 7e69ecc9fd43 src/source/dev_guide/new-form.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/source/dev_guide/new-form.rst Mon Dec 17 17:19:35 2018 +0100 @@ -0,0 +1,129 @@ +.. _newform: + +Understanding PyAMS form +======================== + + +The forms are based on ZC3.form framework. It provide HTML and Json + + +.. seealso:: + + https://z3cform.readthedocs.io/en/latest/ + + + + + +.. _formhowto: + + +How to create a form to a component +----------------------------------- + + +The standard form creation with Z3C is described in the following link. + + https://z3cform.readthedocs.io/en/latest/mustread/form.html + + +PyAMS Form Integration +---------------------- + + +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) Add Form +^^^^^^^^^^^ + +.. code-block:: python + + + @pagelet_config(name='add-contact-phone-paragraph.html', context=IParagraphContainerTarget, layer=IPyAMSLayer, + permission=MANAGE_CONTENT_PERMISSION) + 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 + + +2) Ajax Form +^^^^^^^^^^^^ + +The decorator :py:function:`@ajax_config()` allows the form is working with ajax requests by providing `json` content. + +.. 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" + ... + + + +3) 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 + + + + diff -r 98a84761634f -r 7e69ecc9fd43 src/source/index.rst --- a/src/source/index.rst Fri Dec 14 12:16:12 2018 +0100 +++ b/src/source/index.rst Mon Dec 17 17:19:35 2018 +0100 @@ -57,9 +57,9 @@ Creating new PyAMS Packages Customizing site appearance Understanding PyAMS forms - Creating new blocks - Creating new portlet - Creating new content type + Creating new Paragraph (or blocks) + Creating new Portlet + Creating new Content type Creating Custom workflows Advanced features