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