Deploying Plone and Zine together with Deliverance using Repoze

written by Calvin Hendryx-Parker, on Apr 27, 2009 10:21:00 AM.

Using the right tool for the right job

Six Feet Up loves Plone, but using Plone as a blogging tool has never been a strong point. Blogging isn't something that we felt that Plone did well out of the box. Much like most Plone products, you have to add in various third party products or create your own products if you want to get some of the really nice features that many of the typical blogging platforms like WordPress offer today like pingbacks. We had tried quite a few of these ways of stuffing blogging into Plone and ran into various issues of dependencies or incompatibilities along the way so we decided to go for a solution that wasn't in Plone.

Zine is a blogging platform written in Python and supports being deployed as a WSGI application, so it seemed like the perfect fit. Zine is basically a python clone of WordPress and has all the nice goodies and plugins that modern blogging platforms have come to enjoy. It is also relatively easy to theme and write plugins to extend its behavior.

We can also run Plone as a WSGI application by using the Repoze application repoze.zope2 instead of the standard Zope ZPublisher. The big benefit is that we can run multiple WSGI applications in a single process and utilize WSGI middleware like Deliverance to theme all of the applications as they are delivered. This type of setup can simplify deployment greatly and all works well with buildout, which we have a lot of knowledge about already.

Bringing together the pieces

Let's start by getting repoze.zope2, zine and deliverance added to our buildout. I added four additional parts to our existing buildout profile to get this process started.

[buildout]
...
parts = 
  ...
  repoze # this needs to be before the zope2 part or plone won't work
  ...
  deliverance # these have to go at the end or paster won't get all the right eggs listed
  zine-deps
  zine

I also added this to the [buildout] section to pin the version of the zopelib:

find-links =
    ...
    http://dist.repoze.org/zope2/latest/zopelib-2.10.7.0.tar.gz

...
versions=versions

[versions]
zopelib = 2.10.7.0

In these four new parts we will add in the new bits required to host the full WSGI stack for our deployment. Here are the parts we added:

[repoze]
recipe = zc.recipe.egg
eggs =
     PasteScript
     WSGIUtils
     repoze.zope2
     ${plone:eggs}
index = http://dist.repoze.org/plone/latest/simple

[deliverance]
recipe = infrae.subversion
as_eggs = true
urls =
    http://codespeak.net/svn/z3/deliverance/trunk deliverance

[zine-deps]
recipe = zc.recipe.egg
extra-paths = parts/zine/lib/zine
interpreter = python
eggs = Paste
       PasteScript
       PasteDeploy
       ${instance:eggs}
       ${repoze:eggs}
       ${deliverance:eggs}
       SQLAlchemy>=0.5.0
       Jinja2>=2.1.0
       Werkzeug>=0.4
       simplejson
       html5lib
       pytz
       Babel>=0.9.4
       lxml>=2.0
       Pygments>=0.9
       docutils==0.5
       zinebuildout>=0.4
       MySQL-python

[zine]
recipe = zc.recipe.cmmi
url = http://zine.pocoo.org/releases/Zine-0.1.2.tar.gz
extra_options = --python=python

With all of that added to your buildout profile you need to re-run buildout and it will download and install all the needed parts for you.

Configuring the paster WSGI stack

Before we can start up the new WSGI stack with paster, we will need to create a paster.ini for the WSGI stack and a rules.xml file for deliverance to use.

You can put both of these files in the root of your buildout. The paste.ini is using the zinebuildout egg to allow us to use Zine as an application in our paster stack. It is configured in the [app:zine] section. You will need to make an empty zine directory in your buildout that will contain the zine.ini once Zine has been configured through the web.

The paste.ini file configures two pipelines, one for each application. The blog pipeline has the deliverance filter applied to it so you get the Plone theme applied to the Zine blog.

paste.ini

[DEFAULT]
debug = True

[app:zine]
use = egg:zinebuildout
instance_folder = %(here)s/zine

[app:zope2]
paste.app_factory = repoze.obob.publisher:make_obob
repoze.obob.get_root = repoze.zope2.z2bob:get_root
repoze.obob.initializer = repoze.zope2.z2bob:initialize
repoze.obob.helper_factory = repoze.zope2.z2bob:Zope2ObobHelper
zope.conf = %(here)s/parts/instance/etc/zope.conf

[filter:errorlog]
use = egg:repoze.errorlog#errorlog
path = /__error_log__
keep = 20
ignore = paste.httpexceptions:HTTPUnauthorized
       paste.httpexceptions:HTTPNotFound
       paste.httpexceptions:HTTPFound

[pipeline:zope]
pipeline = egg:Paste#cgitb
           egg:Paste#httpexceptions
           egg:repoze.retry#retry
           egg:repoze.tm#tm
           egg:repoze.vhm#vhm_xheaders 
           errorlog
           zope2

[pipeline:blog]
pipeline = egg:Paste#cgitb
           egg:Paste#httpexceptions
           errorlog
           deliverance
           zine

[filter:deliverance]
use = egg:deliverance#main
rule_filename = %(here)s/rules.xml

[server:main]
use = egg:repoze.zope2#zserver
host = localhost
port = 51047

[composite:main]
use = egg:Paste#urlmap
/blog = blog
/zope = zope

Configuring Deliverance to theme Zine with the Plone site theme

Deliverance is configured in the rules.xml file and points to the Plone application's main_template to get the look and feel. The rest of the rules drop bits that aren't needed and throw the Zine portlets and content into the right places in the Plone theme. We created a Zine plugin to wrap the Zine portlets with a more Plone like structure so that they blend right into the Plone theme.

rules.xml

<ruleset>
<theme href="/zope/Plone/main_template" />
<rule>
  <prepend theme="//head" content="//head/link" nocontent="ignore" /> 
  <prepend theme="//head" content="//head/style" nocontent="ignore" />
  <append theme="//head" content="//head/script" nocontent="ignore" />    
  <append theme="//head" content="//head/meta" nocontent="ignore" />
  <replace theme="//head/title" content="//head/title"  nocontent="ignore" />
  <!-- we don't need the reddit zine links -->
  <drop content="//div[@class='reddit']"/>
  <!-- let's drop the breadcrumbs from the plone theme -->
  <drop theme="#portal-breadcrumbs"/>
  <!-- let's drop the plone login box -->
  <drop theme="//td[@id='portal-column-one']/div/div"/>
  <!-- put the zine portlets in column one -->
  <append theme="//td[@id='portal-column-one']/div" content="//div[@class='sidebar']/*" nocontent="ignore"/>
  <!-- put the body of the zine in the content region -->
  <append theme="children:#region-content" content="children://div[@class='contents']" nocontent="ignore" />
  <!-- put the administrative links in column two -->
  <prepend theme="children:#portal-column-two" content="//div[@class='body']/ul" nocontent="ignore"/>
  <!-- make sure the login box has a spot when we need it -->
  <append theme="#viewlet-above-content" content="//div[@class='login-box']" nocontent="ignore"/>
</rule>
</ruleset>

Time to start up paster

Once these are all in place and you have created the zine directory at the root of your buildout, we can start the paster server. It is started like this:

bin/paster serve paste.ini

Now in your browser you can go to http://localhost:8080/zope to see the Zope/Plone instance. You will need to add a Plone Site to your instance. In my demo I gave it an id of "Plone". If you have a Plone Site with a different id, you will need to change the path to the in your rules.xml.

Now, when you go to http://localhost:8080/blog you will be presented with the Zine setup screens. If all went well, the screens should look like your Plone Site theme. At this point you will need to setup a MySQL database to hold the blog data and provide connection information as you go through the Zine setup wizard.

Now you can fix up and modify the rules.xml and theme in your Plone site to make the Zine blog blend into your site seamlessly. Enjoy!

Download the buildout used in this post

Comments

  • This is excellent!

    My first reaction after downloading Zine and trying to install it's various dependencies was, "a buildout script would have been nice..".

    My friend Jace is adding some features and fixes (Wordpress imports etc) to Zine here: bitbucket.org/jace/zine-main/ . I'll try to extract the zine buildout bits from here and contribute to the project.

    Thanks for the detailed tutorial.

    Comment by Pradeep Gowda — Apr 30, 2009 10:50:47 AM | # - re

  • Can you post the buildout.cfg in its entirety?
    Thanks!

    Comment by Rob Cecil — Apr 30, 2009 4:39:15 PM | # - re

    • Hey Rob,

      I attached a tarball to the post that has the complete buildout inside.

      Enjoy!
      Cal

      Comment by Calvin Hendryx-Parker — May 1, 2009 11:01:43 AM | # - re

  • More specifically, I am trying to understand how to get your changes into a buildout.cfg produced from

    paster create -t plone3_buildout MyPlone.

    My attempts to edit my buildout.cfg look like:

    [buildout]
    parts =
    repoze
    zope2
    productdistros
    instance
    zopepy
    deliverance
    zine-deps
    zine

    1. Change the number here to change the version of Plone being used
      extends = dist.plone.org/release/3.2.1/versions.cfg
      versions = versions
    1. Add additional egg download sources here. dist.plone.org contains archives
    2. of Plone packages.
      find-links =
      dist.plone.org/release/3.2.1
      download.zope.org/ppix/
      download.zope.org/distribution/
      effbot.org/downloads
      dist.repoze.org/zope2/latest/zopelib-2.10.7.0.tar.gz

    versions=versions

    [versions]
    zopelib = 2.10.7.0

    1. Add additional eggs here
      eggs =
    1. Reference any eggs you are developing here, one per line
    2. e.g.: develop = src/my.package
      develop =

    [zope2]

    1. For more information on this step and configuration options see:
    2. pypi.python.org/pypi/plone.recipe.zope2install
      recipe = plone.recipe.zope2install
      fake-zope-eggs = true
      additional-fake-eggs =
      ZODB3
      url = ${versions:zope2-url}
    1. Use this section to download additional old-style products.
    2. List any number of URLs for product tarballs under URLs (separate
    3. with whitespace, or break over several lines, with subsequent lines
    4. indented). If any archives contain several products inside a top-level
    5. directory, list the archive file name (i.e. the last part of the URL,
    6. normally with a .tar.gz suffix or similar) under 'nested-packages'.
    7. If any archives extract to a product directory with a version suffix, list
    8. the archive name under 'version-suffix-packages'. [productdistros]
    9. For more information on this step and configuration options see:
    10. pypi.python.org/pypi/plone.recipe.distros
      recipe = plone.recipe.distros
      urls =
      nested-packages =
      version-suffix-packages =

    [instance]

    1. For more information on this step and configuration options see:
    2. pypi.python.org/pypi/plone.recipe.zope2instance
      recipe = plone.recipe.zope2instance
      zope2-location = ${zope2:location}
      user = admin:admin
      http-address = 8080
      #debug-mode = on
      #verbose-security = on
    1. If you want Zope to know about any additional eggs, list them here.
    2. This should include any development eggs you listed in develop-eggs above,
    3. e.g. eggs = Plone my.package
      eggs =
      Plone
      ${buildout:eggs}
    1. If you want to register ZCML slugs for any packages, list them here.
    2. e.g. zcml = my.package my.other.package
      zcml =

    products =
    ${buildout:directory}/products
    ${productdistros:location}

    [zopepy]

    1. For more information on this step and configuration options see:
    2. pypi.python.org/pypi/zc.recipe.egg
      recipe = zc.recipe.egg
      eggs = ${instance:eggs}
      interpreter = zopepy
      extra-paths = ${zope2:location}/lib/python
      scripts = zopepy

    [repoze]
    recipe = zc.recipe.egg
    eggs =
    PasteScript
    WSGIUtils
    repoze.zope2
    ${plone:eggs}
    index = dist.repoze.org/plone/latest/simple

    [deliverance]
    recipe = infrae.subversion
    as_eggs = true
    urls =
    codespeak.net/svn/z3/deliverance/trunk deliverance

    [zine-deps]
    recipe = zc.recipe.egg
    extra-paths = parts/zine/lib/zine
    interpreter = python
    eggs = Paste
    PasteScript
    PasteDeploy
    ${instance:eggs}
    ${repoze:eggs}
    ${deliverance:eggs}
    SQLAlchemy>=0.5.0
    Jinja2>=2.1.0
    Werkzeug>=0.4
    simplejson
    html5lib
    pytz
    Babel>=0.9.4
    lxml>=2.0
    Pygments>=0.9
    docutils==0.5
    zinebuildout>=0.4
    MySQL-python

    [zine]
    recipe = zc.recipe.cmmi
    url = zine.pocoo.org/releases/Zine-0.1.2.tar.gz
    extra_options = --python=python

    ---

    Then I run python bootstrap, then bin\buildout -v. This is what I get:

    C:\PloneZineDeliverance>bin\buildout.exe -v
    Installing 'zc.buildout', 'setuptools'.
    We have the distribution that satisfies 'zc.buildout==1.1.1'. We have the distribution that satisfies 'setuptools==0.6c9'.
    While:
    Installing.
    Getting section repoze.
    Initializing section repoze.
    Getting option repoze:eggs.
    Getting section plone.
    Error: The referenced section, 'plone', was not defined.

    Any ideas?

    Comment by Rob Cecil — Apr 30, 2009 11:42:19 PM | # - re

  • This is a great help, ties things together for me. Looking forward to trying it out. Thanks.

    Comment by chris shenton — May 1, 2009 10:18:43 AM | # - re

  • The buildout file needs to include PIL, without which the plone products were not available for install in the ZMI.

    Comment by Pradeep Gowda — May 1, 2009 3:08:54 PM | # - re

  • Have you any experience on deploying multiple zine instances with one paste deploy? It seems that zine only works with one zine instance per python interpreter so you'd need paste to somehow fire it off in a separate python instance somehow. Any thoughts?

    Comment by Nathan Van Gheem — Jul 12, 2009 5:39:57 AM | # - re

  • Good tutorial - very helpful! I made buildout quite similar to solution described in this blog post. It uses Plone 3.3rc4, deliverance 0.3 and mod_wsgi for production. In development it also includes useful addons (like: stxnext.pdb, or egg:Paste#evalerror).

    lichota.pl/blog/2009/07/12/buildout-for-plone-3-with-deliverance-on-wsgi

    Comment by Wojciech Lichota — Jul 12, 2009 1:05:35 PM | # - re

  • Can we run multiple instance of Zine at a time on the same machine? cloudycombo.blogspot.com/2009/12/plone-fone.html

    Comment by haas schuster — Jan 15, 2010 7:54:31 AM | # - re

  • This is a great help, ties things together for me. Looking forward to trying it out. Thanks

    Comment by voicechat — Jan 30, 2010 11:36:28 AM | # - re

  • only works with one zine instance per python interpreter so you'd need paste to somehow fire it off in a separate python instance somehow. Any thoughts?

    Comment by ??? ???? — Jan 30, 2010 11:39:51 AM | # - re

Leave a Reply