Skip to content

Commit 00508c5

Browse files
committed
imrpove classic-ui/views and add layers
1 parent 67c9a1e commit 00508c5

File tree

3 files changed

+400
-133
lines changed

3 files changed

+400
-133
lines changed

docs/classic-ui/layers.md

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
---
2+
html_meta:
3+
"description": "Layers allow you to enable and disable views and other site functionality based on installed add-ons and themes."
4+
"property=og:description": "Layers allow you to enable and disable views and other site functionality based on installed add-ons and themes."
5+
"property=og:title": "Layers"
6+
"keywords": "layer, layers,browser layer, views, viewlets, portlets"
7+
---
8+
9+
(classic-ui-layers-label)=
10+
11+
# Layers
12+
13+
Layers allow you to activate different code paths and modules depending on
14+
the external configuration.
15+
16+
Examples:
17+
18+
- Code belonging to a theme is only active when that theme has been selected.
19+
- Mobile browsing code is only active when the site is being browsed on a
20+
mobile phone.
21+
22+
Layers are marker interfaces applied to the {term}`HTTPRequest` object.
23+
They are usually used in conjunction with {term}`ZCML` directives to
24+
dynamically activate various parts
25+
of the configuration (theme files, add-on product functionality).
26+
27+
Layers ensure that only one add-on product can override the specific Plone
28+
instance functionality in your site at a time, while still allowing you
29+
to have possibly conflicting add-on products in your buildout and
30+
ZCML. Remember that multiple Plone site instances can share
31+
the same ZCML and code files.
32+
33+
Many ZCML directives take the optional `layer` parameter. See example,
34+
[resourceDirectory](http://apidoc.zope.org/++apidoc++/ZCML/http_co__sl__sl_namespaces.zope.org_sl_browser/resourceDirectory/index.html)
35+
36+
Layers are activated when an add-on product is installed or a certain
37+
theme is picked.
38+
39+
## Using layers
40+
41+
Some ZCML directives for example: `browser:page` take a `layer` attribute.
42+
43+
Given the following:
44+
45+
- A layer interface defined in Python code: `plonetheme.yourthemename.interfaces.IThemeSpecific`
46+
- Your add-on or theme package installed through add-on product installer on your site instance
47+
48+
then views and viewlets from your product can be enabled on the site
49+
instance using the following ZCML:
50+
51+
```
52+
<!-- Site actions override in YourTheme -->
53+
<browser:viewlet
54+
name="plone.site_actions"
55+
manager="plone.app.layout.viewlets.interfaces.IPortalHeader"
56+
class=".siteactions.SiteActionsViewlet"
57+
layer="plonetheme.yourthemename.interfaces.IThemeSpecific"
58+
permission="zope2.View"
59+
/>
60+
```
61+
62+
### Unconditional overrides
63+
64+
If you want to override a view or a viewlet unconditionally for all sites
65+
without the add-on product installer
66+
support you need to use `overrides.zcml`.
67+
68+
## Creating a layer
69+
70+
### Theme layer
71+
72+
Theme layers can be created via the following steps:
73+
74+
1. Subclass an interface from `IDefaultPloneLayer`:
75+
76+
```
77+
from plone.theme.interfaces import IDefaultPloneLayer
78+
79+
class IThemeSpecific(IDefaultPloneLayer):
80+
"""Marker interface that defines a Zope 3 skin layer bound to a Skin
81+
Selection in portal_skins.
82+
If you need to register a viewlet only for the "YourSkin"
83+
skin, this is the interface that must be used for the layer attribute
84+
in YourSkin/browser/configure.zcml.
85+
"""
86+
```
87+
88+
2. Register it in ZCML. The name must match the theme name.
89+
90+
```xml
91+
<interface
92+
interface=".interfaces.IThemeSpecific"
93+
type="zope.publisher.interfaces.browser.IBrowserSkinType"
94+
name="SitsSkin"
95+
/>
96+
```
97+
98+
3. Register and set your theme as the default theme in `profiles/default/skins.xml`. Theme layers require that they are set as the default theme and not just activated on your Plone site. Example:
99+
100+
```xml
101+
<object name="portal_skins" allow_any="False" cookie_persistence="False"
102+
default_skin="SitsSkin">
103+
104+
<!-- define skins-based folder objects here if any -->
105+
106+
<skin-path name="SitsSkin" based-on="Plone Default">
107+
<layer name="plone_skins_style_folder_name"
108+
insert-before="*"/>
109+
</skin-path>
110+
111+
</object>
112+
```
113+
114+
### Add-on layer for clean extensions
115+
116+
An add-on product layer is enabled when an add-on product is installed.
117+
Since one Zope application server may contain several Plone sites,
118+
you need to keep enabled code paths separate by using add-on layers -
119+
otherwise all views and viewlets apply to all sites in one Zope application server.
120+
121+
- You can enable views and viewlets specific to functional add-ons.
122+
- Unlike theme layers, add-on layers depend on the activated add-on
123+
products, not on the selected theme.
124+
125+
An add-on layer is a marker interface which is applied on the
126+
{term}`HTTPRequest` object by Plone core logic.
127+
128+
First create an {term}`interface` for your layer in `your.product.interfaces.py`:
129+
130+
```
131+
""" Define interfaces for your add-on.
132+
"""
133+
134+
import zope.interface
135+
136+
class IAddOnInstalled(zope.interface.Interface):
137+
""" A layer specific for this add-on product.
138+
139+
This interface is referred in browserlayer.xml.
140+
141+
All views and viewlets register against this layer will appear on
142+
your Plone site only when the add-on installer has been run.
143+
"""
144+
```
145+
146+
You then need to refer to this in the `profile/default/browserlayer.xml`
147+
file of your add-on installer to use it:
148+
149+
```xml
150+
<layers>
151+
<layer
152+
name="your.product"
153+
interface="your.product.interfaces.IAddOnInstalled"
154+
/>
155+
</layers>
156+
```
157+
158+
```{note}
159+
The add-on layer registry is persistent and stored in the database.
160+
The changes to add-on layers are applied only when add-ons are installed or uninstalled.
161+
```
162+
163+
More information
164+
165+
- <https://pypi.python.org/pypi/plone.browserlayer>
166+
167+
168+
### Add-on layer for changing existing behavior
169+
170+
You can also use layers to modify the behavior of plone or another Add-on.
171+
172+
To make sure that your own view is used, your Layer must be more specific than the layer where original view is registered.
173+
174+
For example, some z3cform things register their views on the `IPloneFormLayer` from plone.app.z3cform.interfaces.
175+
176+
If you want to override the ploneform-macros view that is registered on the `IPloneFormLayer`, your own Layer must be a subclass of IPloneFormLayer.
177+
178+
If a view does not declare a specific Layer, it becomes registered on the `IDefaultBrowserLayer` from zope.publisher.interfaces.browser.IDefaultBrowserLayer.
179+
180+
181+
### Manual layers
182+
183+
Apply your layer to the {term}`HTTPRequest` in the `before_traverse` hook or
184+
before you call the code which looks up the interfaces.
185+
186+
In the example below we turn on a layer for the request which is later
187+
checked by the rendering code.
188+
This way some pages can ask for special View/Viewlet rendering.
189+
190+
Example:
191+
192+
```
193+
# Defining layer
194+
195+
from zope.publisher.interfaces.browser import IBrowserRequest
196+
197+
class INoHeaderLayer(IBrowserRequest):
198+
""" When applied to HTTP request object, header animations or images are not rendered on this.
199+
200+
If this layer is on request do not render header images.
201+
This allows uncluttered editing of header animations and images.
202+
"""
203+
204+
# Applying layer for some requests (manually done in view)
205+
# The browser page which renders the form
206+
class EditHeaderAnimationsView(FormWrapper):
207+
208+
form = HeaderCRUDForm
209+
210+
def __call__(self):
211+
""" """
212+
213+
# Signal viewlet layer that we are rendering
214+
# edit view for header animations and it is not meaningful
215+
# to try to render the big animation on this page
216+
zope.interface.alsoProvides(self.request, INoHeaderLayer)
217+
218+
# Render the edit form
219+
return FormWrapper.__call__(self)
220+
```
221+
222+
## Troubleshooting instructions for layers
223+
224+
- Check that your view or whatever is working without a layer assigned
225+
(globally);
226+
- Check that `configure.zcml` has a layer entry. Put some garbage to
227+
trigger a syntax error in `configure.zcml` to make sure that it is being
228+
loaded;
229+
- Add-on layer: check that `profiles/default/browserlayer.xml` has a
230+
matching entry with a matching name;
231+
- Theme layer: if it's a theme layer, check that there is a matching
232+
`skins.xml` entry
233+
- Check that layer name is correctly spelt in the view declaration.
234+
235+
## Checking active layers
236+
237+
### Layers are activated on the current request object
238+
239+
Example:
240+
241+
```
242+
if INoHeaderLayer.providedBy(self.request):
243+
# The page has asked to suspend rendering of the header animations
244+
return ""
245+
```
246+
247+
### Active themes and add-on products
248+
249+
The `registered_layers()` method returns a list of all layers active on
250+
the site.
251+
Note that this is different to the list of layers which are applied on the
252+
current HTTP request object:
253+
the request object may contain manually activated layers.
254+
255+
Example:
256+
257+
```
258+
from interfaces import IThemeSpecific
259+
from plone.browserlayer.utils import registered_layers
260+
261+
if IThemeSpecific in registered_layers():
262+
# Your theme specific code
263+
pass
264+
else:
265+
# General code
266+
pass
267+
```
268+
269+
### Getting active theme layer
270+
271+
Only one theme layer can be active at once.
272+
273+
The active theme name is defined in `portal_skins` properties.
274+
This name can be resolved to a theme layer.
275+
276+
### Debugging active layers
277+
278+
You can check the activated layers from HTTP request object by looking at
279+
`self.request.__provides__.__iro__`.
280+
Layers are evaluated from zero index (highest priority) the last index
281+
(lowest priority).
282+
283+
## Testing Layers
284+
285+
Plone testing tool kits won't register layers for you, you have to do it
286+
yourself somewhere in the boilerplate code:
287+
288+
```
289+
from zope.interface import directlyProvides
290+
291+
directlyProvides(self.portal.REQUEST, IThemeLayer)
292+
```

0 commit comments

Comments
 (0)