Theme Package I: Preparations

Creating a theme product with the Diazo inline editor is an easy way to start and to test, but it is not a solid long term solution and you are also limited in what you can do that way.

Even if plone.app.theming allows importing and exporting of a Diazo theme as a ZIP archive, it might be preferable to manage your theme as an actual Plone product.

One of the most obvious reasons is that it will allow you to override Plone elements that are not accessible via pure Diazo features (such as overloading content view templates, viewlets, configuration settings, etc.).

Preparing Your Setup

Install npm

If you don’t have npm already installed on your system please do it. npm comes with nodejs, we just need to install npm.

On Debian/Ubuntu for example you can do this with apt:

sudo apt install -y npm

On a Mac you can install npm using Homebrew:

brew install node

If you need a newer version of npm just update your version with npm itself:

npm install npm@latest -g

Install Grunt

We also need to install grunt-cli globally.

If you already have it, you can skip this step.

npm install -g grunt-cli

Note

If you get an error with node on Debian/Ubuntu, please check if you already have /usr/bin/node, if not create a symlink like:

ln -s /usr/bin/nodejs /usr/bin/node

Setup A Python Virtual Environment

First, let’s create a Python virtualenv:

virtualenv mrbobvenv

Then we enable the virtualenv:

source mrbobvenv/bin/activate
(mrbobvenv):$

Create A Product To Handle Your Diazo Theme

To create a Plone 5 theme skeleton, you will use mr.bob’s templates for Plone.

Install mr.bob And bobtemplates.plone

To install mr.bob, you can use pip:

(mrbobvenv): pip install mr.bob

and to install the required bobtemplates for Plone, do:

(mrbobvenv): pip install bobtemplates.plone==2.0

Create a Plone 5 theme product skeleton with mrbob. It will ask you some questions about the new theme package:

(mrbobvenv): mrbob -O ploneconf.theme bobtemplates:plone_theme_package

Welcome to mr.bob interactive mode. Before we generate directory structure, some questions need to be answered.

Answer with a question mark to display help.
Values in square brackets at the end of the questions show the default value if there is no answer.


--> Theme name [Theme]: Ploneconf Theme

--> Author name [Your Name]:

--> Author email [your.email@example.com]:

--> Author github username:

--> Package description [An add-on for Plone]: Plone Conference Training Theme

--> Plone version [5.0.8]:

Now you have a new Python package in your current folder:

(mrbobvenv): ls ploneconf.theme
CHANGES.rst            LICENSE.GPL            bootstrap-buildout.py  package.json           src
CONTRIBUTORS.rst       LICENSE.rst            bootstrap-buildout.pyc requirements.txt
Gruntfile.js           MANIFEST.in            buildout.cfg           setup.cfg
HOWTO_DEVELOP.rst      README.rst             docs                   setup.py

It is now safe to deactivate the mrbob virtualenv:

(mrbobvenv): deactivate

Hint

This would be the perfect time to initialize your package with Git and put your files under version control:

cd ploneconf.theme
git init .
git add .
git commit -m "Initial commit."
cd ..

Install zc.buildout And Boostrap Your Development Environment

You can install Buildout globally or on a virtualenv.

We will install zc.buildout in a new virtual environment using the provided requirements.txt.

virtualenv buildoutvenv
source buildoutvenv/bin/activate
(buildoutvenv):$ cd ploneconf.theme
(buildoutvenv):$ pip install -r requirements.txt
(buildoutvenv):$ buildout bootstrap

Now you have everything in place and you can run buildout.

Depending on your internet connection and your local buildout cache this can take several minutes to complete.

(buildoutvenv): ./bin/buildout

After buildout finished successfully it is now safe to deactivate the buildoutvenv virtualenv:

(buildoutvenv): deactivate

This will create the whole development environment for your package:

ls bin
addchangelogentry               code-analysis-zptlint           lasttagdiff                     prerelease
buildout                        coverage                        lasttaglog                      pybabel
bumpversion                     createcoverage                  libdoc                          pybot
check-manifest                  createfontdatachunk.py          longtest                        release
code-analysis                   develop                         painter.py                      ride
code-analysis-check-manifest    enhancer.py                     pilconvert.py                   robot
code-analysis-clean-lines       explode.py                      pildriver.py                    robot-debug
code-analysis-csslint           flake8                          pilfile.py                      robot-server
code-analysis-find-untranslated fullrelease                     pilfont.py                      test
code-analysis-flake8            gifmaker.py                     pilprint.py                     thresholder.py
code-analysis-jscs              i18ndude                        player.py                       viewer.py
code-analysis-jshint            instance                        postrelease

Inspect Your Package Source

Your package source code is in the src folder:

tree src/ploneconf/theme/
src/ploneconf/theme
├── __init__.py
├── browser
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── configure.zcml
│   ├── overrides
│   └── static
├── configure.zcml
├── interfaces.py
├── locales
│   ├── ploneconf.theme.pot
│   └── update.sh
├── profiles
│   ├── default
│   │   ├── browserlayer.xml
│   │   ├── metadata.xml
│   │   ├── registry.xml
│   │   └── theme.xml
│   └── uninstall
│       ├── browserlayer.xml
│       └── theme.xml
├── setuphandlers.py
├── testing.py
├── tests
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── robot
│   │   └── test_example.robot
│   ├── test_robot.py
│   └── test_setup.py
└── theme
    ├── HOWTO_DEVELOP.rst
    ├── backend.xml
    ├── barceloneta
    │   └── less
    │       ├── accessibility.plone.less
    │       ├── alerts.plone.less
    │       ├── barceloneta-compiled.css
    │       ├── barceloneta-compiled.css.map
    │       ├── barceloneta.css
    │       ├── barceloneta.plone.export.less
    │       ├── barceloneta.plone.less
    │       ├── barceloneta.plone.local.less
    │       ├── behaviors.plone.less
    │       ├── breadcrumbs.plone.less
    │       ├── buttons.plone.less
    │       ├── code.plone.less
    │       ├── contents.plone.less
    │       ├── controlpanels.plone.less
    │       ├── deco.plone.less
    │       ├── discussion.plone.less
    │       ├── dropzone.plone.less
    │       ├── event.plone.less
    │       ├── fonts.plone.less
    │       ├── footer.plone.less
    │       ├── forms.plone.less
    │       ├── formtabbing.plone.less
    │       ├── grid.plone.less
    │       ├── header.plone.less
    │       ├── image.plone.less
    │       ├── loginform.plone.less
    │       ├── main.plone.less
    │       ├── mixin.borderradius.plone.less
    │       ├── mixin.buttons.plone.less
    │       ├── mixin.clearfix.plone.less
    │       ├── mixin.forms.plone.less
    │       ├── mixin.grid.plone.less
    │       ├── mixin.gridframework.plone.less
    │       ├── mixin.images.plone.less
    │       ├── mixin.prefixes.plone.less
    │       ├── mixin.tabfocus.plone.less
    │       ├── modal.plone.less
    │       ├── normalize.plone.less
    │       ├── pagination.plone.less
    │       ├── pickadate.plone.less
    │       ├── plone-toolbarlogo.svg
    │       ├── portlets.plone.less
    │       ├── print.plone.less
    │       ├── scaffolding.plone.less
    │       ├── search.plone.less
    │       ├── sitemap.plone.less
    │       ├── sitenav.plone.less
    │       ├── sortable.plone.less
    │       ├── states.plone.less
    │       ├── tables.plone.less
    │       ├── tablesorter.plone.less
    │       ├── tags.plone.less
    │       ├── thumbs.plone.less
    │       ├── toc.plone.less
    │       ├── tooltip.plone.less
    │       ├── tree.plone.less
    │       ├── type.plone.less
    │       ├── variables.plone.less
    │       └── views.plone.less
    ├── barceloneta-apple-touch-icon-114x114-precomposed.png
    ├── barceloneta-apple-touch-icon-144x144-precomposed.png
    ├── barceloneta-apple-touch-icon-57x57-precomposed.png
    ├── barceloneta-apple-touch-icon-72x72-precomposed.png
    ├── barceloneta-apple-touch-icon-precomposed.png
    ├── barceloneta-apple-touch-icon.png
    ├── barceloneta-favicon.ico
    ├── index.html
    ├── less
    │   ├── custom.less
    │   ├── plone.toolbar.vars.less
    │   ├── roboto
    │   │   ├── LICENSE.txt
    │   │   ├── Roboto-Light.eot
    │   │   ├── Roboto-Light.svg
    │   │   ├── Roboto-Light.ttf
    │   │   ├── Roboto-Light.woff
    │   │   ├── Roboto-LightItalic.eot
    │   │   ├── Roboto-LightItalic.svg
    │   │   ├── Roboto-LightItalic.ttf
    │   │   ├── Roboto-LightItalic.woff
    │   │   ├── Roboto-Medium.eot
    │   │   ├── Roboto-Medium.svg
    │   │   ├── Roboto-Medium.ttf
    │   │   ├── Roboto-Medium.woff
    │   │   ├── Roboto-MediumItalic.eot
    │   │   ├── Roboto-MediumItalic.svg
    │   │   ├── Roboto-MediumItalic.ttf
    │   │   ├── Roboto-MediumItalic.woff
    │   │   ├── Roboto-Regular.eot
    │   │   ├── Roboto-Regular.svg
    │   │   ├── Roboto-Regular.ttf
    │   │   ├── Roboto-Regular.woff
    │   │   ├── Roboto-Thin.eot
    │   │   ├── Roboto-Thin.svg
    │   │   ├── Roboto-Thin.ttf
    │   │   ├── Roboto-Thin.woff
    │   │   ├── Roboto-ThinItalic.eot
    │   │   ├── Roboto-ThinItalic.svg
    │   │   ├── Roboto-ThinItalic.ttf
    │   │   ├── Roboto-ThinItalic.woff
    │   │   ├── RobotoCondensed-Light.eot
    │   │   ├── RobotoCondensed-Light.svg
    │   │   ├── RobotoCondensed-Light.ttf
    │   │   ├── RobotoCondensed-Light.woff
    │   │   ├── RobotoCondensed-LightItalic.eot
    │   │   ├── RobotoCondensed-LightItalic.svg
    │   │   ├── RobotoCondensed-LightItalic.ttf
    │   │   └── RobotoCondensed-LightItalic.woff
    │   ├── theme-compiled.css
    │   ├── theme.less
    │   └── theme.local.less
    ├── manifest.cfg
    ├── node_modules
    │   └── bootstrap
    │       ├── CHANGELOG.md
    │       ├── Gruntfile.js
    │       ├── LICENSE
    │       ├── README.md
    │       ├── dist
    │       │   ├── css
    │       │   │   ├── bootstrap-theme.css
    │       │   │   ├── bootstrap-theme.css.map
    │       │   │   ├── bootstrap-theme.min.css
    │       │   │   ├── bootstrap-theme.min.css.map
    │       │   │   ├── bootstrap.css
    │       │   │   ├── bootstrap.css.map
    │       │   │   ├── bootstrap.min.css
    │       │   │   └── bootstrap.min.css.map
    │       │   ├── fonts
    │       │   │   ├── glyphicons-halflings-regular.eot
    │       │   │   ├── glyphicons-halflings-regular.svg
    │       │   │   ├── glyphicons-halflings-regular.ttf
    │       │   │   ├── glyphicons-halflings-regular.woff
    │       │   │   └── glyphicons-halflings-regular.woff2
    │       │   └── js
    │       │       ├── bootstrap.js
    │       │       ├── bootstrap.min.js
    │       │       └── npm.js
    │       ├── fonts
    │       │   ├── glyphicons-halflings-regular.eot
    │       │   ├── glyphicons-halflings-regular.svg
    │       │   ├── glyphicons-halflings-regular.ttf
    │       │   ├── glyphicons-halflings-regular.woff
    │       │   └── glyphicons-halflings-regular.woff2
    │       ├── grunt
    │       │   ├── bs-commonjs-generator.js
    │       │   ├── bs-glyphicons-data-generator.js
    │       │   ├── bs-lessdoc-parser.js
    │       │   ├── bs-raw-files-generator.js
    │       │   ├── change-version.js
    │       │   ├── configBridge.json
    │       │   ├── npm-shrinkwrap.json
    │       │   └── sauce_browsers.yml
    │       ├── js
    │       │   ├── affix.js
    │       │   ├── alert.js
    │       │   ├── button.js
    │       │   ├── carousel.js
    │       │   ├── collapse.js
    │       │   ├── dropdown.js
    │       │   ├── modal.js
    │       │   ├── popover.js
    │       │   ├── scrollspy.js
    │       │   ├── tab.js
    │       │   ├── tooltip.js
    │       │   └── transition.js
    │       ├── less
    │       │   ├── alerts.less
    │       │   ├── badges.less
    │       │   ├── bootstrap.less
    │       │   ├── breadcrumbs.less
    │       │   ├── button-groups.less
    │       │   ├── buttons.less
    │       │   ├── carousel.less
    │       │   ├── close.less
    │       │   ├── code.less
    │       │   ├── component-animations.less
    │       │   ├── dropdowns.less
    │       │   ├── forms.less
    │       │   ├── glyphicons.less
    │       │   ├── grid.less
    │       │   ├── input-groups.less
    │       │   ├── jumbotron.less
    │       │   ├── labels.less
    │       │   ├── list-group.less
    │       │   ├── media.less
    │       │   ├── mixins
    │       │   │   ├── alerts.less
    │       │   │   ├── background-variant.less
    │       │   │   ├── border-radius.less
    │       │   │   ├── buttons.less
    │       │   │   ├── center-block.less
    │       │   │   ├── clearfix.less
    │       │   │   ├── forms.less
    │       │   │   ├── gradients.less
    │       │   │   ├── grid-framework.less
    │       │   │   ├── grid.less
    │       │   │   ├── hide-text.less
    │       │   │   ├── image.less
    │       │   │   ├── labels.less
    │       │   │   ├── list-group.less
    │       │   │   ├── nav-divider.less
    │       │   │   ├── nav-vertical-align.less
    │       │   │   ├── opacity.less
    │       │   │   ├── pagination.less
    │       │   │   ├── panels.less
    │       │   │   ├── progress-bar.less
    │       │   │   ├── reset-filter.less
    │       │   │   ├── reset-text.less
    │       │   │   ├── resize.less
    │       │   │   ├── responsive-visibility.less
    │       │   │   ├── size.less
    │       │   │   ├── tab-focus.less
    │       │   │   ├── table-row.less
    │       │   │   ├── text-emphasis.less
    │       │   │   ├── text-overflow.less
    │       │   │   └── vendor-prefixes.less
    │       │   ├── mixins.less
    │       │   ├── modals.less
    │       │   ├── navbar.less
    │       │   ├── navs.less
    │       │   ├── normalize.less
    │       │   ├── pager.less
    │       │   ├── pagination.less
    │       │   ├── panels.less
    │       │   ├── popovers.less
    │       │   ├── print.less
    │       │   ├── progress-bars.less
    │       │   ├── responsive-embed.less
    │       │   ├── responsive-utilities.less
    │       │   ├── scaffolding.less
    │       │   ├── tables.less
    │       │   ├── theme.less
    │       │   ├── thumbnails.less
    │       │   ├── tooltip.less
    │       │   ├── type.less
    │       │   ├── utilities.less
    │       │   ├── variables.less
    │       │   └── wells.less
    │       └── package.json
    ├── package-lock.json
    ├── package.json
    ├── preview.png
    ├── rules.xml
    ├── template-overrides
    ├── tinymce-templates
    │   └── image-grid-2x2.html
    └── views
        └── slider-images.pt.example

28 directories, 256 files

As you can see, the package already contains a Diazo theme including the Barceloneta resources:

tree -L 2 src/ploneconf/theme/theme/
src/ploneconf/theme/theme/
├── HOWTO_DEVELOP.rst
├── backend.xml
├── barceloneta
│   └── less
├── barceloneta-apple-touch-icon-114x114-precomposed.png
├── barceloneta-apple-touch-icon-144x144-precomposed.png
├── barceloneta-apple-touch-icon-57x57-precomposed.png
├── barceloneta-apple-touch-icon-72x72-precomposed.png
├── barceloneta-apple-touch-icon-precomposed.png
├── barceloneta-apple-touch-icon.png
├── barceloneta-favicon.ico
├── index.html
├── less
│   ├── custom.less
│   ├── plone.toolbar.vars.less
│   ├── roboto
│   ├── theme-compiled.css
│   ├── theme.less
│   └── theme.local.less
├── manifest.cfg
├── node_modules
│   └── bootstrap
├── package-lock.json
├── package.json
├── preview.png
├── rules.xml
├── template-overrides
├── tinymce-templates
│   └── image-grid-2x2.html
└── views
    └── slider-images.pt.example

9 directories, 22 files

This theme basically provides you with a copy of the Plone 5 default theme (Barceloneta), and you can change everything you need to create your own theme. The Barceloneta resources are in the folder barceloneta.

This is basically a copy of the theme folder of plonetheme.barceloneta. We removed some unneeded files there, because we only need the Less part for partially including it in our file theme.less.

We also have the icons and the file backend.xml from Barceloneta in our theme folder.

In the folder theme/less we have our CSS/Less files. Our own CSS goes into the file custom.less.

You can also add more Less files and include them in theme.less, if you have a lot of custom CSS and you like to split it into multiple files.

The file theme.less is our main Less file. Here we include all other files we need.

It already has some includes of Barceloneta, Twitter Bootstrap and our cusomizations from the file custom.less at the bottom.

We also have a file package.json, which we can use to define external dependencies like Twitter Bootstrap or other CSS/JS packages we want to use in our theme. For more information on how to do this, see Install External CSS And JavaScript Libraries With npm And Use Them In Your Theme.

Start Plone And Install Your Theme Product

To start the Plone instance, run:

./bin/instance fg

The Plone instance will then be available at http://localhost:8080. The default username is admin and password is admin.

  1. Go to http://localhost:8080 and click the button Create a new Plone site to add a new Plone site.
  2. Name the site Plone (which should be the default) and click on Create Plone Site.
  3. Go to the Plone Control Panel: toolbar ‣ admin ‣ Site Setup
  4. Go to the Add-ons control panel.
  5. Click on the Install next to Plone Theme: Ploneconf Theme to install your add-on.
  6. The theme will be automatically enabled.

If something is wrong with the theme, you can always go to http://localhost:8080/Plone/@@theming-controlpanel and disable it.

This control panel will never be themed, so it works even if the theme might be broken.

Hint

Don’t forget to commit any changes on your package to version control. After the first buildout run, there are some new files and folders. Some of them (node_modules and package-lock.json) can be ignored, while others (theme-compiled.css) need to be added to the repository.

Edit the .gitignore file and add the following entries:

node_modules/
package-lock.json

Then run the following commands:

$ git add .
$ git commit -m "Add compiled CSS file."

In the next section we will adjust the skeleton we got from bobtemplates.plone and fill it with our custom theme.