Notes on Rolling Your Own Product
Case 1. You have a perfectly lovely site, but you want to make a policy change.
Example A. Adding a Group Action
We expect our clients will want to upload large batches of files, like images into an album. The conventional approach is to upload one at a time. But what if you want multi-file upload? There are some nifty ways to do this with some fancy javascript. There is an add-on product for Plone (collective.uploadify) which utilizes Uploadify. But the product doesn't come with the interface wired into the Plone theme.
See semetricmedia.uploadifyaction
To add this group action, it is necessary to modify "actions.xml". But where is "profiles/default/actions.xml"? What does this mean? The instructions go on to describe how you make the same configuration change through-the-web (TTW) using the Zope Management Interface (ZMI), but that's not what we are interested in, expedient though it may be.
Aspelli's oft-cited Professional Plone Development is a good resource. There are several other Plone books. But none seem to narrowly describe this case.
CASE 2. You have a perfectly lovely site, but you want to add a view to the display menu for collections.
Maybe you want to redesign the search results you get from a collection. Plone comes with several stock views, but maybe you need to create a new one from scratch or based on a stock view.
Similar to the case above, you can do this by making TTW modifications in the ZMI. But this doesn't scale well if you want to repeat the modifications to multiple sites, store the changes in source code management, or bundle it for distribution.
Solution
Paster
Paster gets you halfway there in two different ways.
Paster (aka Paste Script) is a utility for creating product skeletons. It will typically ask a series of configuration questions and spit out the directories and essential configuration files or a Zope/Plone product.
from Practical Plone Development, page 358
To make creating this boilerplate easier, you can use ZopeSkel, a set of PasteScript templates for the paster command that generates common Plone boilerplate.
Plone 3.2+ installations give you ZopeSkel and paster by default. To install ZopeSkel in the older versions of 3.x, you first need to have easy_install installed on your system. If you don't have it already, download and then run ez_setup.py, for example:
$ wget peak.telecommunity.com/dist/ez_setup.py$ python ez_setup.py(where "python" is the version of you are using
with Plone, e.g. python2.4)
Then, to install the ZopeSkel egg and its dependencies (including PasteScript), run:
$ easy_install -U ZopeSkelThis will install the paster command in the place where your Python binaries go. Keep an eye on the output of easy_install, in case you can't find it afterwards. If it's not in your $PATH, you may want to symlink it in there.
For now, create a filesystem product using one of the standard paster recipes
such as:
$ paster create -t plone$ paster create -t plone_appor
$ paster create -t plone3_themeThere are a number of other recipes available. To see the available options, you
can run:
$ paster create --list-templates
In our experience, the "Plone" template for Paster is adequate if your requirements are strictly limited to making policy changes via GenericSetup. On the other extreme, the "Plone3_theme" solution is a good starting point if you are making a complete theme which is based on the Plone Default theme but then includes style sheet, image and javascript replacements.
We have found that a better place to start for anything less than a complete Theme is the "Plone3_theme" template and then strip out some of the items rather than trying to manually add boilerplate ZCML and XML to the "Plone" template.
Here are the steps we use:
In the zinstance/src/ directory, run paster as follows
$ paster create -t plone3_theme
You can accept the default options for each question with the following exceptionsEnter namespace_package (Namespace package (like plonetheme)) ['plonetheme']: mycompany
Enter package (The package contained namespace package (like example)) ['example']: myproduct
Enter skinname (The skin selection to be added to 'portal_skins' (like 'My Theme')) ['']: Example Product
Enter description (One-line description of the package) ['An installable theme for Plone 3']: A product based on plone3_theme.
A full transcript of the paster run.
Resulting Directory Structure
![]()
Modify the Product
Custom configuration starts with src/mycompany.myproduct/mycompany/myproduct/configure.zcml. This file includes several other ZCML files. Together they begin the process of registering resources with Zope.
At the top of configure.zcml, several namespaces are declared. I like to expand this so that all the namespaces we might need are available.
replace
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five"
xmlns:cmf="http://namespaces.zope.org/cmf"
i18n_domain="mycompany.myproduct">
with
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five"
xmlns:cmf="http://namespaces.zope.org/cmf"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="mycompany.myproduct">
As is, the product is a Theme and will register itself with the portal_skins tool as a theme. In this article, we are trying to augment the existing themes. This means adding the skin layers right after the "custom" layer in all of the existing themes.
To do this, we need to modify as follows
in src/mycompany.myproduct/myproduct/mycompany/myproduct/profiles/default/skins.xml
replace
<object name="portal_skins" allow_any="False" cookie_persistence="False"
default_skin="Example SkinName">
with
<object name="portal_skins">
AND
replace
<skin-path name="Example SkinName" based-on="Plone Default">
with
<skin-path name="*">
Rename or Delete Files
The ZopeSkel template for plone3_theme assumes you will override the standard Plone Default styles. Let's not do that here, so rename or delete the following files so they are not found through acquisition.
within src/mycompany.myproduct/myproduct/mycompany/myproduct/
browser/stylesheets/main.css
skins/mycompany_myproduct_styles/base_properties.props
skins/mycompany_myproduct_styles//base.css.dtml
skins/mycompany_myproduct_styles/portlets.css.dtml
skins/mycompany_myproduct_styles/public.css.dtml
We also do not want to register main.css at this time, so modify cssregistry.xml and comment out the stylesheet registration.
Buildout.cfg
No that the directory structure and boilerplate is in place, modify the buildout.cfg file to include the new product in your Plone instance. Modify as follows:
eggs = ... mycompany.myproduct
zcml = ... mycompany.myproduct
develop = ... src/mycompany.myproduct
Re-run buildout on offline mode, it's faster and we haven't added any new dependencies that require downloading any packages.
./bin/buildout -o
Load the Product
Start Plone and load the product through portal_quickinstaller or the Plone Site Setup web interface. It will be named "Example Product" as specified in the paster answer to the question [].
Three new layers are now defined in the portal_skins tool. These are Filesystem Directory views. Each corresponds to a directory in src/mycompany.myproduct/mycompany/myproduct/skins/
mycompany_myproduct_custom_images
mycompany_myproduct_custom_templates
mycompany_myproduct_custom_styles
Files in these folders can only be modified in the filesystem. Any modifications made through the web result in bew entries in the custom layer.
The properties tab of the portal_skins tool shows these three layers added to the existing themes. They have been placed immediately after "custom" which puts them very high in precedence for acquisition.
Additional Modifications to Meet our Requirements
Note: Changes to the product will always require stopping Plone, re-running buildout, and restarting Plone. Depending on the type of change, you may need to uninstall and reinstall the product so that GenericSetup process any new profile modifications.
Adding a Group Action for Uploadify
This is now as simple as creating a file and inserting the XML supplied by the collective.uploadify homepage.
file name and location: src/mycompany.myproduct/mycomapny/myproduct/profiles/default/actions.xml
Adding a Template for a new View
Create the template file using the Plone templating language. Place the file in src/mycompany.myproduct/mycompany/myproduct/skins/mycompany_myproduct_custom_templates
Register the View with a Content Type
This requires a GenericSetup profile change similar to creating the Group Action for Uploadify. See more at Using the Portal Setup tool to reverse Engineer a profile change.