Personal tools
You are here: Home Documentation Tutorials Switching Skins
Support

Get Help

Join our chat rooms or support forums if you have more specific questions.

Plone Training
Learn how to design, build, and deploy a website in Plone through one of the numerous Plone training sessions around the world.
Find Plone training…
 
Document Actions

Switching Skins

Note: Return to tutorial view.

This Tutorial describes how Plone chooses a skin when you open a web page, and then describes the multiple ways that you can intervene to switch the skin. The Tutorial also discusses two common use cases, switching skins based on URL and the type of user (e.g., Anonymous or Authenticated).

Overview

An overview of the tutorial.

This Tutorial describes how Plone chooses a skin when you open a web page, and then describes the multiple ways that you can intervene to switch (a) the skin proper, i.e. the skin layers that are used; (b) the CSS and javascript resources that are used.

More specifically, the Tutorial discusses:

  • Using standard TTW forms to change underlying settings in a portal that determine what skins are displayed;
  • Using one-time Python scripts that change these underlying settings;
  • Using Access Rules that switch the skin every time a web page is loaded.

The Tutorial also discusses two common use cases, switching skins based on URL and the type of user (e.g., Anonymous or Authenticated), giving references for the first use case and sample code for the second.

How Does Plone Choose a Skin?

A description of the variables and processes that Plone uses to choose a skin each time you visit a page.

The current skin is determined every time you open a Zope or Plone web page on an object -- whether a page through the Zope Management Interface (ZMI) or in your Plone web site. Each time you do so, there are several key variables in play:

  • portal_skins, the skins tool.
  • object, the object you are accessing.
  • REQUEST, the HTTP request that is processed as you access the object and which expires when the page is rendered.
  • REQUEST.portal_skin. If this property exists on the REQUEST object, it says which skin to use. It is typically a cookie which is created when a user logs in, or when a user changes her/his preferences.
  • member.portal_skin. The standard way of creating a REQUEST.portal_skin cookie is by calling portal_skins.updateSkinCookie(). Provided that the current member has a 'portal_skin' property, this method creates a cookie portal_skin with a value equal to member.portal_skin.
  • SKINDATA, a variable tucked in the Python code of Products.CMFCore.Skinnable. During the life of a REQUEST, it stores information about what skin to use. The standard way of changing SKINDATA is a call of the form object.changeSkin('mySkin').


In the final stages of determining the skin, Plone takes three steps:

  • Look if a skin name is encoded in SKINDATA. If so, use it.
  • Otherwise, look if a skin name is stored in REQUEST.portal_skin. If so, use it.
  • Otherwise, look up the default skin in the portal_skins tool.

Intervening to Switch a Skin

A summary of the main ways you can intervene in Plone's skin-choosing process, to switch a skin.

With all these variables and steps, you can intervene to switch the skin in many different ways. (In the below list, sibling items represent different alternatives.)

  • (1) Encode a skin in the SKINDATA variable ...
    • (1A) Call object.changeSkin('mySkin').
    • (1B) Manually modify the SKINDATA variable.
  • (2) Add a 'portal_skin' property to the REQUEST object ...
    • (2A) Add a cookie 'portal_skin' ...
      • (2B.i) On the user's personal preferences page, update the 'portal_skin' property (which autoamtically creates the cookie). In order to enable this, you must enable an option on the portal_skins tool using the Properties tab in the ZMI.
      • (2B.ii) Update the user's 'portal_skin' property manually, then manually call portal_skins.updateSkinCookie(). It is not necessary to enable the ZMI option to change the 'portal_skin' property with Python code outside of the personal preferences page.
      • (2B.iii) Create a cookie manually.
    • (2B) Add 'portal_skin' as a simple string attribute.
  • (3) Set the default skin in the portal_skins tool, using the Properties tab in the ZMI.

Making a Skin Switch Last

How to make a skin switch last beyond a single REQUEST and page view.

Because Plone determines the current skin on every page load, we need to make sure that any changes we make last.


Two of the above interventions are standard ways of changing underlying settings, ensuring that the skin switch lasts:

  • (2B.i) Use the ZMI to enable changing users' skin preferences. Then use the Plone personal preferences page to change a user's preferred skin, and thus automatically create a cookie with the skin name.
  • (3) Use the ZMI to change the default skin.


Two of the interventions involve changing underlying settings, but not in a standard way.

  • (2B.ii) Set a user's preferred skin outside of the preferences form, and update the cookie.
  • (2B.iii) Create a portal_skins cookie manually.

To do this, you should create a one-time script.


Finally, three of the interventions only make short-term changes that only last for the short lifetime of a single REQUEST:

  • (1A) Call object.changeSkin('mySkin').
  • (1B) Manually modify the SKINDATA variable.
  • (2B) Add 'portal_skin' to the REQUEST as a simple string attribute.

To make these changes sustainable, you must use a script that repeats on every page load. The typical way of doing this is using an Access Rule. See EnSimpleStaging Tutorial - Switching Skins By URL and Using Zope/Apache to Switch Skins By URL for examples of how to do this.

Use Case One: Skin By URL

References for switching a skin based on URL.

Suppose that you want your site to use different skins depending on whether it is accessed via http://prefix1.---, http://prefix2.---, etc.

A skin must be being determined every time the site is accessed ("What does the URL look like? Well then, let's use this skin"). So, an Access Rule is the best way to switch the skin.

Once again, see EnSimpleStaging Tutorial - Switching Skins By URL and Using Zope/Apache to Switch Skins By URL for examples.

Use Case Two: Skin By Type of User

Example code for allowing certain users to switch skins.

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

Synchronizing Skin Layers and CSS/Javascript Resources

How to ensure that the proper CSS/Javascript resources are used in different skins.

Switching skins changes what skin layers in portal_skins are used, but it does not switch what CSS and javscript resources to load. This could be disastrous if you have CSS that applies only to the page templates in one skin, but not to the page templates in another skin.

The solution is simple: in the portal_css or portal_javascripts tools, just add a condition on certain resources similar to

python:portal.getCurrentSkinName() == 'My Public View'

Additional Documentation on Switching Skins

References for switching skins by URL.

Here are links to additional documentation on switching skins:


For any issues with the web site functionality, please file a ticket.

Please consult the policy on plone.org content if you want your content published on this site.

Servers and hosting by