diff -r 1de74ac0628f -r 31b3d00edb8a src/source/developer_guide/howto-portlet.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/source/developer_guide/howto-portlet.rst Tue Dec 11 16:29:56 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 + + + +
Comment
+
+ +
+ + + + +
+
+
+ +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 + +
Comment
+ + +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