|
1 .. _portlethowto: |
|
2 |
|
3 |
|
4 How to create a new Portlet? |
|
5 ============================ |
|
6 |
|
7 **Portlets** are pluggable user interface software components that are managed and displayed in a web portal, |
|
8 for example an enterprise portal or a web CMS. A portlet can aggregate (integrate) and personalize content from |
|
9 different sources within a web page. A portlet responds to requests from a web client and generates dynamic content |
|
10 (*reference:* `Wiki portlet`_). |
|
11 |
|
12 .. _`wiki portlet`: https://en.wikipedia.org/wiki/Portlet |
|
13 |
|
14 |
|
15 **PyAMS Portal** provides the portal engine but only a very small set of predefined portlets that can be used to compose |
|
16 and organize the structure of a web page; additional portlets are provided by other packages, like |
|
17 :ref:`pyams_content`. |
|
18 |
|
19 |
|
20 1. Define portlet settings |
|
21 '''''''''''''''''''''''''' |
|
22 |
|
23 Portlet settings interface are defined in ``interfaces.py``, you can use :py:class:`pyams_portal.interfaces.IPortletSettings` |
|
24 or extend the interface by adding additional properties for example: |
|
25 |
|
26 .. code-block:: python |
|
27 |
|
28 from zope.schema import Text |
|
29 |
|
30 NEW_PORTLET_NAME = 'new.portlet' |
|
31 |
|
32 class INewPortletSettings(IPortletSettings): |
|
33 |
|
34 comment = Text(title=_("Comment"), |
|
35 required=True) |
|
36 |
|
37 |
|
38 A :py:class:`pyams_portal.portlet.PortletSettings` persistent subclass then implements what IPortletSettings describes: |
|
39 |
|
40 .. code-block:: python |
|
41 |
|
42 @implementer(INewPortletSettings) |
|
43 class NewPortletSettings(PortletSettings): |
|
44 """Portlet settings""" |
|
45 |
|
46 comment = FieldProperty(INewPortletSettings['comment']) |
|
47 |
|
48 |
|
49 2. Create Portlet |
|
50 ''''''''''''''''' |
|
51 |
|
52 The Portlet component is a utility, which implements the :py:class:`pyams_portal.interfaces.IPortlet` interface and is |
|
53 registered by the :py:func:`pyams_portal.portlet.portlet_config` decorator; |
|
54 |
|
55 .. code-block:: python |
|
56 |
|
57 @portlet_config(permission=VIEW_PERMISSION) |
|
58 class ImagePortlet(Portlet): |
|
59 """Image portlet""" |
|
60 |
|
61 name = NEW_PORTLET_NAME |
|
62 label = _("New portlet") |
|
63 |
|
64 toolbar_image = None |
|
65 toolbar_css_class = 'fa fa-fw fa-2x fa-picture-o' |
|
66 |
|
67 settings_class = NewPortletSettings |
|
68 |
|
69 |
|
70 Where: |
|
71 - **permission**: permission required to display this portlet content |
|
72 - **name**: internal name given to this portlet. This name must be unique between all portlets, so using your own |
|
73 namespace into this name is generally a good option! |
|
74 - **label**: user label given to this portlet |
|
75 - **toolbar_image**: URL of an image used to display portlet button into ZMI toolbar, if any |
|
76 - **toolbar_css_class**: CSS class used to display portlet button into ZMI toolbar, if any |
|
77 - **settings_class**: class used to store portlet settings. |
|
78 |
|
79 |
|
80 3. Create portlet settings edit form |
|
81 '''''''''''''''''''''''''''''''''''' |
|
82 |
|
83 Portlet settings have to be updated through management interface via a :py:class:`pyams_portal.zmi.portlet.PortletSettingsEditor` |
|
84 subform: |
|
85 |
|
86 .. code-block:: python |
|
87 |
|
88 @pagelet_config(name='properties.html', context=INewPortletSettings, layer=IPyAMSLayer, |
|
89 permission=VIEW_SYSTEM_PERMISSION) |
|
90 class NewPortletSettingsEditor(PortletSettingsEditor): |
|
91 """New portlet settings editor""" |
|
92 |
|
93 settings = INewPortletSettings |
|
94 |
|
95 |
|
96 @adapter_config(name='properties.json', context=(INewPortletSettings, IPyAMSLayer), provides=IPagelet) |
|
97 class NewPortletSettingsAJAXEditor(AJAXEditForm, NewPortletSettingsEditor): |
|
98 """New portlet settings editor, JSON renderer""" |
|
99 |
|
100 |
|
101 4. Previewing portlet content |
|
102 ''''''''''''''''''''''''''''' |
|
103 |
|
104 A *previewer* is used into ZMI to display portlet content into portal template definition page: |
|
105 |
|
106 .. code-block:: python |
|
107 |
|
108 @adapter_config(context=(Interface, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletPreviewer) |
|
109 @template_config(template='my-portlet-preview.pt', layer=IPyAMSLayer) |
|
110 class NewPortletPreviewer(PortletPreviewer): |
|
111 """New portlet previewer""" |
|
112 |
|
113 |
|
114 The previewer template is a Chameleon template: |
|
115 |
|
116 .. code-block:: genshi |
|
117 |
|
118 <tal:var define="settings view.settings"> |
|
119 <tal:if condition="settings.visible"> |
|
120 <div tal:content="settings.comment">Comment</div> |
|
121 </tal:if> |
|
122 <tal:if condition="not settings.visible"> |
|
123 <div class="text-center padding-y-5"> |
|
124 <span class="fa-stack fa-lg"> |
|
125 <i class="fa fa-eye fa-stack-1x"></i> |
|
126 <i class="fa fa-ban fa-stack-2x text-danger"></i> |
|
127 </span> |
|
128 </div> |
|
129 </tal:if> |
|
130 </tal:var> |
|
131 |
|
132 Here we check if portlet is visible or not to display a small icon when hidden; otherwise we display entered comment. |
|
133 |
|
134 |
|
135 5. Rendering portlet content |
|
136 '''''''''''''''''''''''''''' |
|
137 |
|
138 A *renderer* is used to display portlet content into rendered page content: |
|
139 |
|
140 .. code-block:: python |
|
141 |
|
142 @adapter_config(context=(IPortalContext, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletRenderer) |
|
143 @template_config(template='my-portlet-render.pt', layer=IPyAMSLayer) |
|
144 class NewPortletRenderer(PortletRenderer): |
|
145 """New portlet renderer""" |
|
146 |
|
147 label = _("Default comment renderer") |
|
148 |
|
149 |
|
150 Each renderer template is also a Chameleon template: |
|
151 |
|
152 .. code-block:: genshi |
|
153 |
|
154 <div class="comment" tal:content="view.settings.comment">Comment</div> |
|
155 |
|
156 |
|
157 This is the configuration of a *default* renderer defined for this portlet; you can provide several renderers for a |
|
158 given portlet by given distinct names to the adapters: |
|
159 |
|
160 .. code-block:: python |
|
161 |
|
162 @adapter_config(name='another-renderer', |
|
163 context=(IPortalContext, IPyAMSLayer, Interface, INewPortletSettings), provides=IPortletRenderer) |
|
164 @template_config(template='my-portlet-render-2.pt', layer=IPyAMSLayer) |
|
165 class AnotherNewPortletRenderer(PortletRenderer): |
|
166 """Another new portlet renderer""" |
|
167 |
|
168 label = _("Another comment renderer") |