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).

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 pyams_content package ⊞.

Create Portlet

The Portlet component is a utility, which implements the pyams_portal.interfaces.IPortlet interface and is registered by the pyams_portal.portlet.portlet_config() decorator;

@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 pyams_portal.interfaces.IPortletSettings or extend the interface by adding additional properties for example:

1) create portlet settings

from zope.schema import Text

NEW_PORTLET_NAME = 'new.portlet'

class INewPortletSettings(IPortletSettings):

    comment = Text(title=_("Comment"),
                   required=True)

A pyams_portal.portlet.PortletSettings persistent subclass then implements what IPortletSettings describes:

@implementer(INewPortletSettings)
class NewPortletSettings(PortletSettings):
    """Portlet settings"""

    comment = FieldProperty(INewPortletSettings['comment'])

2. declare the settings portlet

Add the settings portlet class inside the portlet

@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

3. portlet settings edit form

Portlet settings have to be updated through management interface (ZMI) via a pyams_portal.zmi.portlet.PortletSettingsEditor subform:

@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"""

Previewing portlet content

A previewer is used into ZMI to display portlet content into portal template definition page:

@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:

<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.

Rendering portlet content

A renderer is used to display portlet content into rendered page content:

@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:

<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:

@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 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

../_images/select_portlet.png