Skin layers

Description

Skin layers are a legacy Plone 2 technology, still is use, for adding overridable templates and media resources to Plone packages.

Introduction

Skin layers, portal_skins and CMFCore.SkinsTool are the old-fashioned way to manage Plone templates.

  • Each Plone theme has set of folders it will pick from portal_skins. These sets are defined in portal_skins -> properties.
  • Skins layers are searched for a template by template name, higher layers first.
  • Skin layers can be reordered through-the-web in portal_skins -> properties

Defining a skin layer

Skin files are placed in the skins folder of your add-on product.

The structure looks like this:

  • yourproduct/namespace/configure.zcml
  • yourproduct/namespace/profiles/default/skins.xml
  • yourproduct/namespace/skins
  • yourproduct/namespace/skins/layer1folder
  • yourproduct/namespace/skins/layer2folder/document_view.pt
  • yourproduct/namespace/skins/layer2folder
  • ...

GenericSetup skins.xml:

<?xml version="1.0"?>
<object name="portal_skins" meta_type="Plone Skins Tool">
 <object name="headeranimation" meta_type="Filesystem Directory View"
         directory="plone.app.headeranimation:skins/headeranimation"/>
  <skin-path name="*">
    <layer name="headeranimation" insert-after="custom"/>
  </skin-path>
</object>

ZCML to register the layer:

<configure
    ...
    xmlns:cmf="http://namespaces.zope.org/cmf">

    <cmf:registerDirectory name="skins" directory="skins" recursive="True" />

</configure>

See also

Unit testing and portal_skins

If you test templates in your unit testing code you might need to call PloneTestCase._refreshSkinData():

def afterSetUp(self):
    # Must be called to load our add-on skins folders
    # for unit testing
    self._refreshSkinData()

Activating the current skin layer from a debug/ipzope shell

The skin needs to be initialised before its files can be accessed e.g. via restrictedTraverse:

portal.setupCurrentSkin()

Rendering a skin layer template

Templates must be bound to a context object before rendering. Plone acquisition magic maps templates as acquired attributes of all contentish objects.

Example:

# Any page object
doc = portal.doc

# portal_skins/plone_content/document_view.pt template bound to document
doc.document_view

# Resulting HTML is rendered when template object is called
doc.document_view()

Testing templates

Below is some example code how templates behave.

Example:

(Pdb) doc
<ATDocument at /plone/doc>
(Pdb) template = doc.document_view
(Pdb) template
<FSPageTemplate at /plone/document_view used for /plone/doc>
(Pdb) template._filepath
'/home/moo/workspace2/plone.app.headeranimation/plone/app/headeranimation/skins/headeranimation/document_view.pt'

Nested folder overrides (z3c.jbot)

z3c.jbot allows to override any portal_skins based file based on its file-system path + filename.

Example jbot ZCML slug (no layers, unconditional overrides)

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:five="http://namespaces.zope.org/five"
    xmlns:i18n="http://namespaces.zope.org/i18n"
    xmlns:browser="http://namespaces.zope.org/browser"
    >

    <browser:jbot directory="jbot" />

Then your add-on has folder structure (example):

yourcompany.app/yourcompany/app/jbot
yourcompany.app/yourcompany/app/jbot/Products.TinyMCE.skins.tinymce.plugins.table.js.table.js
yourcompany.app/yourcompany/app/jbot/Products.TinyMCE.skins.tinymce.plugins.table.html.pt

More info

Poking portal_skins

portal_skins is a persistent tool in Plone site root providing functions to manage skin layers. Its code mostly lives in Products.CMFCore.SkinsTool.

Available skin layers are directly exposed as traversable attributes:

(Pdb) for i in dir(portal_skins): print i
ATContentTypes
ATReferenceBrowserWidget
CMFEditions
COPY
COPY__roles__
ChangeSet
DELETE
...
plone_3rdParty
plone_content
plone_deprecated
plone_ecmascript
plone_form_scripts
plone_forms
plone_images
plone_kss
plone_login
plone_portlets
plone_prefs
plone_scripts
plone_styles
plone_templates

portal_skins.getSkinSelections() will list available skins.

You can edit a specific skin layer:

skin = portal_skins.getSkinByName("Go Mobile Default Theme")

portal_skins.selections is a PersistentDict object holding skin name -> comma separated layer list mappings.




Edit this document

The source code of this file is hosted on GitHub. Everyone can update and fix errors in this document with few clicks - no downloads needed.

  1. Go to Skin layers on GitHub.
  2. Press Fork and edit this file button.
  3. Edit file contents using GitHub's text editor in your web browserm
  4. Fill in the Commit message text box at the end of the page telling why you did the changes. Press Propose file change button next to it when done.
  5. On Send a pull request page you don't need to fill in text anymore. Just press Send pull request button.
  6. Your changes are now queued for review under project's Pull requests tab on Github.

For basic information about updating this manual and Sphinx format please see Writing and updating the manual guide.