Categories

Minifying JavaScript and CSS with buildout

An easy way to increase the performance of a web-page is to minify the used CSS and JavaScript resources. There are ready available tools that strip the comments and whitespaces from JavaScript and CSS-files.

Plone itself ships with a big amount of uncompressed JavaScript and CSS which are compiled in the resource registry. The usual minifying recipes don’t work in this case. To benefit from minifying resources there are some buildout recipes, which eased my life. I recently switched to TinyMCE, because kupu isn’t compatible with IE8. TinyMCE  comes with really many plain JavaScript-files. I believe this is true for any (multilanguage) WYSIWYG-editor. So before putting it into production, I minified all the resources of TinyMCE, with a striking performance increase. What I did was changing my buildout the following way:

With the hexagonit.recipe.download-recipe I download the Java-sources of the yui-compressor

[yui-compressor]
recipe = hexagonit.recipe.download
url = http://yuilibrary.com/downloads/yuicompressor/yuicompressor-2.4.2.zip
strip-top-level-dir = true

The collective.recipe.ant-recipe built the yui-compressor from the previously downloaded sources for me. This recipe assumes there Java and ant are installed and working. If ant is not in the PATH-environment, the recipe provides the ant-home-option for specifying the location of ant.

[yui-compressor-build]
recipe = collective.recipe.ant
ant-options =
    -buildfile ${yui-compressor:location}/build.xml

Finally I used the collective.recipe.minify-recipe to add a wrapper for minifying ALL resources of TinyMCE. The recipe has a paths-option, where you can specify a list of paths to products, which should be minified. The wrapper will walk these paths recursively, look for *.css and *.js-files and minify them, if needed. The products don’t necessarily need to be check-outs or development-eggs. Already packaged 3rd party eggs can be walked too.

[minify]
recipe = collective.recipe.minify
paths =
    src/Products.TinyMCE
ignore =
verbose = true
include-devel = false
css-command = java -jar ${yui-compressor:location}/build/yuicompressor-2.4.2.jar --type css
js-command = java -jar ${yui-compressor:location}/build/yuicompressor-2.4.2.jar --type js

The full inclusion of all parts looks like this:

[buildout]
parts =
zope2
productdistros
instance
yui-compressor
yui-compressor-build
minify

[yui-compressor]
recipe = hexagonit.recipe.download
url = http://yuilibrary.com/downloads/yuicompressor/yuicompressor-2.4.2.zip
strip-top-level-dir = true

[yui-compressor-build]
recipe = collective.recipe.ant
ant-options =
    -buildfile ${yui-compressor:location}/build.xml

[minify]
recipe = collective.recipe.minify
paths =
    src/Products.TinyMCE
ignore =
verbose = true
include-devel = false
css-command = java -jar ${yui-compressor:location}/build/yuicompressor-2.4.2.jar --type css
js-command = java -jar ${yui-compressor:location}/build/yuicompressor-2.4.2.jar --type js

...

After running the buildout I had a minify-wrapper script in the bin-directory of my buildout. Executing it took some time (about 20minutes on my machine) and issued a

INFO: Minified 13 CSS and 917 JavaScript-files

And here is the difference:

Loading the default page of Plone in authenticated mode without minified TinyMCE:

Loading times of Plone

Loading the default page of Plone in authenticated mode with minified TinyMCE:

Plone loading times minified

The size of the TinyMCE-JavaScript reduced from 329.2 KB to 181.8 KB and the loading time decreased from 523 ms to 248 ms. This is less than half!

Migrating to Plone 4.

Recently I tried to migrate the website of the university I work for to Plone 4. The first thing I did was to checkout the development buildout from Plone http://svn.plone.org/svn/plone/buildouts/plone-coredev/branches/4.0. The alpha2 release is quite near, but I thought I can watch the changes/bugfixes better, when I use the development buildout.

The next thing I did was to include our custom and third-party products the site uses into the buildout configuration. This was the easy part, but running the buildout gave me the first error.  It complained about some custom packages, which were not included as development eggs. We use the haufe.eggserver for our internal eggs and most of them were compiled in the Python 2.4 version. I took me a while to figure out I had to provide the 2.6 version of the eggs or the source-distributions. (BTW There seems to be a bug in both setuptools and distribute with the upload command in Python 2.6. They do not accept the -r, –repository flag and always try to upload the egg to pypi) For that reason I chose the source format and the eggs were recognized flawlessly.

Ready for the next step I tried to start the instance in foreground mode. Of course there were some errors. I had to use the Plone 4 branches of  PloneFormGen and CacheFu and the the trunk of the PloneFormGen dependencies TALESField, PythonField and TemplateFields. In my custom products I had two errors occuring quite frequently:

  1. I use safeEditProperty quite frequently. The import location changed from Products.CMFPlone.migrations.migration_util to plone.app.upgrade.utils
  2. The registerType-method of Archetypes (1.6) now takes two parameters: klass and package. The klass parameter has been there before. The package parameter is optional in Plone 3. It is the name of the product for packages in the Products namespace and the full name of the package for all others I guess.

Well now the instance with all dependencies still does not start because of the p4a.plonecalendar, which still imports the object events from zope.app.event. This is where I stopped today…. more to come ….

Customize Plone translations with iw.recipe.cmd

Sometimes the good translations of Plone do not fit your usecase or screen space. Overriding the translations in a pre-buildout area was easy. All you needed to do is to put a directory i18n in your instance home containing a file custom-plone-en.po. The structure of the filename is important: custom-DOMAIN-LANGUAGECODE.po

Nowadays all people use buildouts and adding something to the instance doesn’t seem to be a good idea. It lives in the parts directory and gets overridden on every “install” run of buildout. A solution I use (on Linux) is to maintain the translations in a directory i18n in the buildout and let a recipe copy it to the instance.

For the recipe I use iw.recipe.cmd and the buildout snippet looks like:

[i18n]
recipe = iw.recipe.cmd
on_install = true
on_update = true
cmds =
test -d ${instance:location}/i18n && rm -rf ${instance:location}/i18n || true
cp -R ${buildout:directory}/i18n ${instance:location}/

Using archetypes.referencebrowserwidget with Plone 3.3

If you need to reference objects from big containers, the current ATReferenceBrowser product, which is bundled with Plone may not be your first choice. It is very slow with many objects and it is not very well tested, if at all. The at.referencebrowserwidget aims to be 100% UI compatible but using modern components in the background and there should be tests for all the features it provides. Well, and here it is. It is not finished yet but it works and is used in some productive environments.

You can include it like any other product in your buildout

[buildout]
...
eggs =
...
archetypes.referencebrowserwidget
 
[instance]
...
zcml =
...
archetypes.referencebrowserwidget
...

This will enable it for being installed via quickinstaller. If you do so the skin of ATReferenceBrowserWidget gets overridden by the one of archetypes.referencebrowserwidget. This means the widget code is still ATRefernceBrowserWidget, but the popup and everything behind it is archetypes.referencebrowserwidget.

But you can have more, if you want (and using Plone >= 3.2).

There is a replacement of ATReferenceBrowserWidget, which can be used as stub to satisfy current dependencies on import location and names.
You’ll have to checkout 

http://svn.plone.org/svn/archetypes/MoreFieldsAndWidgets/ATReferenceBrowserWidget/branches/tom_gross_skeletononly/

named “Products.ATReferenceBrowserWidget” in the “src“-directory of your buildout. And change the buildout.cfg to the following:
[buildout]
...
develop = src/Products.ATReferenceBrowserWidget
 
[versions]
Products.ATReferenceBrowserWidget = 3.0-withatref
...
 
[instance]
...
eggs =
 ${buildout:eggs}
 Plone
 archetypes.referencebrowserwidget
...

That’s it. This will enable you the full power of the new widget. Have fun!