Use Case Two: Skin By Type of User
Suppose that you want your site to use different skins depending on
whether an anonymous user, member, manager, etc. is viewing the page.
The skin only needs to be determined at one point. So, a longer-lasting switch would work. And in fact, Access Rules are loaded early on while rendering a page, before the page is aware of who is viewing, so an Access Rule will also think that the current user is the Anonymous User. So, running a one-time script makes the most sense for this use case.
The following example sets up a user action that shows in
in the personal bar, which allows certain users with a custom
permission to perform that action and switch between the standard Plone
Default skin, and a custom Public View skin. Our example is for a skin
product MySkin, with a permission SWITCH_SKINS_PERMISSION = "MySkin:
Switch Skins" defined in Products/MySkin/config.py.
We define a method in MySkin/Extensions/utils.py
from AccessControl import getSecurityManager, Unauthorized
from Acquisition import aq_base
from Products.CMFCore.utils import getToolByName
from Products.MySkin.config import SWITCH_SKINS_PERMISSION
def switchSkin(self, context):
skins_tool = getToolByName(self, 'portal_skins')
mtool = getToolByName(self, 'portal_membership')
if getSecurityManager().checkPermission(SWITCH_SKINS_PERMISSION, self):
member = mtool.getAuthenticatedMember()
if not hasattr(aq_base(member), 'portal_skin'):
member.setProperties(portal_skin = 'My Public View')
portal_skin = member.portal_skin
if portal_skin == 'My Public View':
member.setProperties(portal_skin = 'Plone Default')
else:
member.setProperties(portal_skin = 'My Public View')
skins_tool.updateSkinCookie()
else:
raise Unauthorized
In this case, the method switchSkin uses code that through-the-web (TTW) Python scripts cannot call. So we add it as an external method ext_switchSkin to the Plone site root, and then add a proxy script switchSkin in a skins folder:
## Script (Python) "switchSkin"
##bind container=container
##bind context=context
##bind namespace=
##bind script=script
##bind subpath=traverse_subpath
##parameters=
##title=Switch skin between my public skin and default Plone skin
##
context.portal_url.getPortalObject().ext_switchSkin(context)
context.REQUEST['RESPONSE'].redirect(context.REQUEST['HTTP_REFERER'])
Finally, we use portal_actions to add an action that shows the public view
- Title: Public View
- Id: publicskinswitch
- URL (Expression): string:$portal_url/switchSkin
- Condition (Expression): python:portal.getCurrentSkinName() != 'My Public View'
- Permission: MySkin: Switch Skins
- Category: User
- Visible: True
and an action that returns to the Plone Default view
- Title: Plone View
- Id: ploneskinswitch
- URL (Expression): string:$portal_url/switchSkin
- Condition (Expression): python:portal.getCurrentSkinName() == 'My Public View'
- Permission: MySkin: Switch Skins
- Category: User
- Visible: True
Voila! A user with the permission "MySkin: Switch Skins" will now see an option in the personal bar to switch skins. (Notice that one should not manually enter a URL to the switchSkin script, because the script will redirect to itself, resulting in an infinite loop.)
It would also be possible to create a script that fired every time somebody logged in, which switched the skin immediately for them -- although we don't supply the code here.
The Trouble With Cookies!
If we use cookies, then after somebody logs out, the cookie may continue to exist. In this case, an anonymous user will see the same skin as the logged-in user.
To avoid this issue, make sure to:
- Avoid enabling the option for cookies to exist after the end of a session (by default this is disabled, but it can be changed in the Zope management screen for portal_skins).
- Design all skins with the knowledge that any user can switch skins with enough technical savvy. Skins are not security!
- Change the skin back to the default skin upon logging out -- for instance, by including the following code in a customized logout.cpy:
from Products.CMFCore.utils import getToolByName
skins_tool = getToolByName(context, 'portal_skins')
mtool = getToolByName(context, 'portal_membership')
member = mtool.getAuthenticatedMember()
try:
member.setProperties(portal_skin = 'My Public View')
skins_tool.updateSkinCookie()
context.acl_users.logout(context.REQUEST)
except:
pass