Sublime Text 2 tips for Python and web developers

Update 2014-03: Please see updated blog post for Sublime Text 3.

[toc]

Sublime Text 2 is a very powerful text editor which has gained popularity recently – for good reasons. It is commercial (59 USD). Plenty of power comes from the fact that Sublime has a plug-in framework built in Python. Even though the core app remains closed there exist a vibrant plug-in ecosystem around the editor.

Note: You can try Sublime for free. It simply gives nagging dialog “Please buy” now and then.

Here is my collection of tips how I squeezed more power out of the editor. The tips are written from OSX perspective, but should work fine on Linux and Windows too.

I used to be Aptana Studio (Eclipse) junkie. Even though Sublime does not have all the GUI power of Aptana (e.g. see the end of post here) I have found the current versions of Sublime serving my needs better. The biggest reason for the switch is that Aptana / Eclipse forces folders and files to be encapsulated inside “Eclipse space” to work well.

Sublime is more suitable for work where you work with various open source components which do not follow such rigid order you might find in in-house corporate software. When you need to integrate different tools and projects, Sublime scripting opportunities are more straightforward versus building thousands of lines Java code what could be need with Eclipse.

Note: Don’t write anything about Vim or Eclipse in this paragraph.

Unofficial manual

There exist a third-party maintained manual for Sublime. Especially parts for settings and keyboard shortcuts come very handy.

Add-on manager

Install Sublime Package Control. You need run this in order to install any Sublime Text plug-ins. It magically pulls plug-ins from thin air (or GitHub).

Image

After Package Control has been installed you can add new packages with CMD + SHIFT + P, search Package Install. It has context sensitive search and you can find all packages by name.

Favorite plug-ins list

Based on the feedback from the blog friends you might want to install the following plug-ins through Sublime Package Control

More about specific plug-ins later.

Open files from command-line

I have the following in my shell .rc file, so I can open files directly from the terminal:

alias subl="'/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl'"
alias nano="subl"
export EDITOR="subl"

Note: nice fallback for nano command which comes from the muscle memory sometimes.

Open folders as projects from command-line

You can also open folders in Sublime Text.

Just type e.g.

subl src

… and the whole src/ folder is opened in the Sublime Text project explorer (right hand).

Image

Note: One folder = one project = one window? I am not sure if there are ways to have multiple projects in the same window.

Searching multiple files

First open a folder as a project in Sublime Text 2. You can do this from the command line, as instructed above, or from File > Open menu.

Then right click the folder in the sidebar to search it:

Image

You can also specify a file extension mask as a comma separated in the Where: field.

Image

Configure sane tab and whitespace policy and other settings

Never save your files with hard tabs characters in them. The same goes for trailing whitespaces which are against policy of many programming language style guides.

In the menu Sublime Text 2 > Preferences > File Settings – User drop in this snippet:

// Place user-specific overrides in this file, to ensure they're preserved
// when upgrading. See
// http://docs.sublimetext.info/en/latest/customization/settings.html
// for more info
{

    // Tab and whitespace handling.
    // Indent using spaces, 4 spaces ber indent by default, clean up extra whitespaces on save
    "tab_size": 4,
    "translate_tabs_to_spaces": true,
    "trim_automatic_white_space": true,
    "trim_trailing_white_space_on_save": true,

    // Do not try to detect the tab size from the opened file
    "detect_indentation" : false,

     // Don't do any VIM keybindings or other VIM fanatic sugar
    "ignored_packages": ["Vintage"],

    // Don't complain about plug-in responsive
    // (Too verbose when you have slow computer)
    "detect_slow_plugins": false,

    // http://www.granneman.com/webdev/editors/sublime-text/top-features-of-sublime-text/auto-completion-in-sublime-text/
    // The delay, in ms, before the auto complete window is shown after typing
    "auto_complete_delay": 500,

    // Install better them
    // (You can get this from the package control)
    // https://github.com/buymeasoda/soda-theme/
    "theme": "Soda Dark.sublime-theme",

    // Download better font
    // https://github.com/adobe/Source-Code-Pro

    "font_face" : "Source Code Pro",
     // Don't show hidden (dotted) directories and binary files in the sidebar
    // (You are not going to edit them in any case)
    "file_exclude_patterns": [".*", "*.pyc", "*.pyo", "*.exe", "*.dll", "*.obj","*.o", "*.a", "*.lib", "*.so", "*.dylib", "*.ncb", "*.sdf", "*.suo", "*.pdb", "*.idb", ".DS_Store", "*.class", "*.psd", "*.db"]
}

See more about the settings.

Note: Don’t confuse File Settings – User with Global Settings – User in Preferences menu. The latter doesn’t seem to work.

Note: Even though you configure this policy, Sublime Text 2 may auto-detect another tab_size policy when you open a file. See the explanation below.

Converting existing files to use spaces instead of tabs

Sublime tries to autodetect tab settings settings for every opened file and may fail, so keep your eye on this when working with uncleaned files. You might want to use git pre-commit to prevent committing files with tabs in them.

Do View > Indentation > Convert Indentation to Spaces and make sure Indent using spaces is turned on in the same menu. The new versions of Sublime should remember this setting on file type basis.

There are also config files which you can access from Preferences menu, but after many failed attempts and hacking several config files it did me no good. Maybe autodetect was overriding my simply attempt of never use tab.

Map file formats to syntax highlighting

If you a have a file format you want to recognize under a certain highlighter e.g. map ZCML files to XML highlighter.

Open any file of the format.

Then: View > Syntax > Open all with current extension as… ->[your syntax choice].

Image

ZCML, now with full color

More info.

Disable automatic loading of the last session

By default, Sublime Text re-opens all files and folders you had when you closed the editor last time. Some people don’t like this.

Instructions to disable automatic session restore for Sublime Text.

Lint and validate your files while typing

SublimeLinter scans your files on the background when typing using validators and linters for various errors. Please see Configuration section in README as you might need to install additional software, like Node.js, to run some of the linters.

Image

Detect mistypes CSS, yay!

SublimeLinter comes with built-in pylint and has Node’s jshint and csslint packages includes

Add CodeIntel autocompletion support

Install CodeIntel from Package Control.

If you are working with Python projects, using buildout, this recipe comes to handy.

[codeintel]
recipe = corneti.recipes.codeintel
eggs = ${instance:eggs}
extra-paths =
    ${omelette:location}

This will generate .codeintel file inside your buildout folder.

CodeIntel plug-in assumes .codeintel file is in your project root. Simply type

subl .

to open your buildout folder as Sublime project folder.

Now Sublime should support auto-completion. E.g. start typing

from zope.interface import <--- Auto completion pops up here

Image

Also, with CodeIntel, ALT + mouse click takes you the source code of import, or the declaration of any function you click.

CodeIntel also supports PHP, Ruby and Javascript, to name few.

Note: If you are not using buildout, or Python, you can always create CodeIntel configuration file in old-fashioned way.

Go to anywhere shortcut

CMD + P. Type in a part of a filename and a part of a function / rule name. You are there. Very powerful, yet so simple feature.

Image

Go to line shortcut

Use Go To Line functionality CTRL+G for more traditional jumps.

Context sensitive in-file search shortcut

Handy for Javascript, CSS, Python, etc. CMD + R. Type your method or rule name and Sublime automatically jumps into its declaration.

Image

… or in Python …

Image

Edit multiple words or lines simultaneously using multi cursor

This trick is handy if you need to wrap / unwrap stuff in quotes, add commas, add parenthesis etc. on multiple lines or items simulatenously.

First select lines or items. You can select multiple individual words by holding down CMD and double clicking words. For lines you can do just the normal SHIFT selection.

Press SHIFT + CMD + L to activate the multi cursor mode.

Image

Then edit all the entries simultaneously. Use CMD + left and CMD + right etc. to move al the cursors to the beginning or the end of the linen and so on.

Open OS file browser for the currently opened file or any of its parent directories

CTRL + mouse click filename in the title bar of the edit window to show the full path to the file and open any of its parent folder.

Note: This is OSX’s Finder file browser standard behavior and might not work on other platforms.

Image

More powerful sidebar

Install SideBarEnhanchements package to get more menu entries to sidebar which you can use to manage your source folder.

Image

Theme and font

To further enhance readability of your text editing environment

See the results below.

Image

Syncing and back-uping Sublime Text 2 settings and plug-ins with Dropbox

You may want to

  • Save your Sublime Text 2 configuration for which you have spend so many tears to tune it up
  • Sync your Sublime Text 2 configuration across different computers

Here are instructions for syncing and saving Sublime Text 2 settings with Dropbox.

HTML / XML tag tools

No one loves XML sit-ups. XML’ish languages where not intended to be written by hand. Sublime Text provides some pain killers on the matter.

Install Tag from Package Control. It comes handy when you are cleaning up those hard tabs….

Image

Select text and then search from Command Palette (CMD + SHIFT + P): Tag: Auto Format Tags on Selection. Your HTML indent and other weirdness will be cleaned up. You can also configure it for different tag writing style preferences.

There is also built-in HTML: Wrap selection with tag command to insert parent or middle level tags in your HTML source.

Git

Though Sublime’s file explorer doesn’t support fiel state coloring or context sensitive menu  shortcuts like in Eclipse, you still get some Git commands thru kemayo’s git plug-in (from Package Control, again).

Image

Still unresolved

More tips

Translations

This article is translated to Serbo-Croatian language by Vera Djuraskovic from Webhostinggeeks.com.

\"\" Subscribe to RSS feed Image Follow me on Twitter Image Follow me on Facebook Image Follow me Google+

Importing and exporting Plone folders using quintagroup.transmogrify

Exporting single folder only

Here is explained how to export and import Plone CMS folders between different Plonen versions, or different CMS systems, using XML based content marshalling and quintagroup.transmogrifier.

This overcomes some problems with Zope management based export/import which uses Python pickles and thus needs identical codebase on the source and target site. Exporting and importing between Plone 3 and Plone 4 is possible.

You can limit export to cover source content to with arbitary portal_catalog conditions. If you limit source content by path you can effectively export single folder only.

The recipe described here assumes the exported and imported site have the same path for the folder. Manually rename or move the folder on source or target to change its location.

Note: The instructions here requires quintagroup.transmogrify version 0.4 or later … or trunk version during the writing of this blog post.

Source site

Execute these actions on the source Plone site.

Install quintagroup.transmogrifier via buildout and Plone add-on control panel.

Go to Site setup > Content migration.

Edit export settings. Remove unnecessary pipeline entries by looking the example below. Add a new catalogsource blueprin. The exclude-contained option makes sure we do not export unnecessary items from the parent folderst:

.

[transmogrifier]
pipeline =
    catalogsource
    fileexporter
    marshaller
    datacorrector
    writer
    EXPORTING

[catalogsource]
blueprint = quintagroup.transmogrifier.catalogsource
path = query= /isleofback/ohjeet
exclude-contained = true

Also we need to include some field-level exluding bits for the folders, because the target site does not necessary have the same content types available as the source site and this may prevent setting up folderish content settings:

[marshaller]
blueprint = quintagroup.transmogrifier.marshaller
exclude =
  immediatelyAddableTypes
  locallyAllowedTypes

You might want to remove other, unneeded blueprints from the export pipeline. For example, portletexporter may cause problems if the source and target site do not have the same portlet code.

Go to Zope Management Interface > portal_setup > Export tab. Check Content (transmogrifier) step. Press Export Selected Steps at the bottom of the page. Now a .tar.gz file will be downloaded.

During the export process instance.log file is updated with status info. You might want to follow it in real-time from UNIX command line

tail -f var/log/instance.log

In log you should see entries running like:

2010-12-27 12:05:30 INFO EXPORTING _path=sisalto/ohjeet/yritys/yritysten-tuotetiedot/tuotekortti
2010-12-27 12:05:30 INFO EXPORTING
Pipeline processing time: 00:00:02
          94 items were generated in source sections
          94 went through full pipeline
           0 were discarded in some section

Target site

Execute these actions on the target Plone site.

Install quintagroup.transmogrifier via buildout and Plone add-on control panel.

Open target site instance.log file for monitoring the import process

tail -f var/log/instance.log

Go to Zope Management Interface > portal_setup > Import tab.

Choose downloaded setup_toolxxx.tar.gz file at the bottom of the page, for Import uploaded tarball input.

Run import and monitoring log file for possible errors. Note that the import completes even if the target site would not able to process incoming content. If there is a serious problem the import seems to complete succesfully, but no content is created.

Note: Currently export/import is not perfect. For example, ZMI content type icons are currently lost in the process. It is recommended to do a test run on a staging server before doing this process on a production server. Also, the item order in the folder is being lost.

\"\" Subscribe to RSS feed Image Follow me on Twitter Image Follow me on Facebook Image Follow me Google+

Integrating and theming WordPress with your CMS site using XDV

Introduction

XDV is an external HTML theming engine, a.k.a. theming proxy, which allows you to mix and match HTML and CSS from internal and external sites by using simple XML rules. It separates the theme development from the site development, so that people with little HTML and CSS knowledge can create themes without need to know underlying Python, PHP or whatever. It also enables integration of different services and sites to one, unified, user experience. For example, XDV is used by plone.org <http://plone.org> to integrate Plone CMS and Trac issue tracker. XDV compiles theming rules to XSL templates, which has been a standard XML based templates language since 1999. XSL has good support in every programming language and web server out there. Example backends to perform XSL transformation include

  • Python and lxml library
  • Apache’s mod_transform
  • nginx web server
  • All XSL capable Java and .NET software out there

XDV theming can be used together with Plone where enhanced support is provided by collective.xdv package package. Technically, collective.xdv adds Plone settings panel and does XSL transformation in Zope’s post-publication hook using lxml library. XDV can be used standalone with XDV package to theme any web site, let it be WordPress, Joomla, Drupal or custom in-house PHP solution from year 2000. XDV is based on Deliverance specification The difference between XDV and Deliverance reference implementation is that XDV internally compiles themes to XSL templates, when Deliverance relies on processing HTML in Python. Currently XDV approach seems to be working better, as we had many problems trying to apply Deliverance for WordPress site (redirects didn’t work, HTTP posts didn’t work, etc.).

Setting up XDV development tools

XDV tools are deployed as Python eggs. You can use tools like buildout <http://www.buildout.org/> configuration and assembly tool or easy_install to get XDV on your development computer and the server. If you are working with Plone you can integrate XDV to your site existing buildout. If you are not working with Plone, XDV home page has instructions how to deploy XDV command standalone.

XDV Rules

Rules (rules.xml) will tell how to fit content from external source to your theme HTML. It provides straightforward XML based syntax to manipulate HTML easily

  • Append, replace and drop HTML pieces
  • Insert HTML snippets
  • CSS or XPath selectors can be used to identify HTML parts
  • It is possible to mix and match content from more than two sites
  • etc.

Rules XML syntax is documented at XDV homepage. Rules will be compiled to XSL template (theme.xsl) by xdvcompiler command. The actual theming is done by one of the XSL backends listed above, by taking HTML as input and applying XSL transformations on it. Note that currently rules without matching selectors are silently ignored and there is no bullet-proof way to debug what happens inside XSL transformation, except by looking into compiled theme.xsl.

Using XDV to theme and integrate a WordPress site

Below are instructions how to integrate a WordPress site to your CMS. In this example CMS is Plone, but it could be any other system. We will create XDV theme which will theme WordPress site to match our CMS site in the fly.

Image

WordPress theme using built with XDV and using a live Plone web page as a theme template. This way WordPress theme inherits “live data” from Plone site, like top tabs (portal sections), footer, CSS and other stuff which can be changed in-the-fly and reflecting changes to two separaet theming products would be cumbersome. Benefits using WordPress for blogging instead of main CMS

  • WordPress post and comment management is easy
  • WordPress does not need to be touched: the old public WordPress instance can keep happily running wherever it is during the whole process
  • You do not need to migrate legacy WordPress installations to your CMS’s internal blogging tool
  • WordPress comes with extensive blog spam filtering tools. We get 11000 spam comments a month.
  • WordPress is designed for blogging and the user interface is good for that
  • WordPress integrates well with blog pingback support services
  • WordPress supports Gravatars and other blogging plug-ins
  • ..and so on…

Benefits of using XDV theming instead of creating native WordPress theme are

  • You need to maintain only one theming add-on product e.g. one for your main CMS and WordPress receives updates to this site and theme automatically
  • WordPress does not need to be touched
  • You can host your WordPress on a different server, even wordpress.com, and still integrate it to your main CMS
  • The theme can be recycled not only for WordPress, but also other external services: Bugzilla, Trac, Webmail, phpBB, you-name-it
  • Even though WordPress has slick UI, it is a well known fact that it is a can of worms internally. My developers do not like the idea of PHP development and would spit on my face if I ask them to go a develop a WordPress theme for us

Theme elements

The theme will consist of following pieces

  • Deliverance rules XML file which defines how to combine Plone and WordPress HTML (rules.xml)
  • Additional CSS definitions active only for WordPress (wordpress.css). Dependency to this CSS in injected to the <head> by rules XML
  • Special Plone page template which will provide slots where WordPress can drop in the content (wordpress_listing.pt)
  • A helper script which makes it easy for repeatable perform theming actions, like recompiling the theme (xdv.py)

CMS page template

This explains how to create a Plone page template where WordPress content will be dropped in. This step is not necessary, as we could do this without touching the Plone. However, it makes things more straightforward and explicit when we known that WordPress theme uses a certain template and we explicitly define slots for WordPress content there. Example:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
      xmlns:tal="http://xml.zope.org/namespaces/tal"
      xmlns:metal="http://xml.zope.org/namespaces/metal"
      xmlns:i18n="http://xml.zope.org/namespaces/i18n"
      lang="en"
      metal:use-macro="here/main_template/macros/master"
      i18n:domain="plone">

<body>

    <div metal:fill-slot="content">

        <div id="wordpress-content">
                <!-- Your WordPress "left column" will go there -->
        </div>

    </div>

</body>
</html>

Theming rules

Following are XDV rules (rules.xml) how we will fit WordPress site to Plone frame. It will integrate

  • Content from WordPress
  • Metadata from WordPress
  • CSS from Plone
  • Page basic structrure from Plone

rules.xml:

<?xml version="1.0" encoding="UTF-8"?>
<rules xmlns="http://namespaces.plone.org/xdv"
       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
       xmlns:css="http://namespaces.plone.org/xdv+css">

    <!-- Remove WordPress CSS by filtering out <style> tags-->
    <drop css:content="style" />

    <!-- Make sure that WordPress metadata is present in <head> section -->
    <append css:content="head link" css:theme="head" />

    <!-- note: replace does not seem to handle multiple meta tags very well -->
    <drop css:theme="meta" />
    <append css:content="head meta" css:theme="head" />

    <!-- Use blog title instead of Plone page title -->
    <replace css:content="title" css:theme="title" />

    <!-- Put WordPress sidebar to Plone's portlets section -->
    <append css:content="#r_sidebar" css:theme="#portal-column-one .visualPadding" />

    <!-- Place wordpress content into our theme content area -->
    <copy css:content="#contentleft" css:theme="#wordpress-content" />

    <!-- This mixes in WordPress specific CSS sheet which is applied for pages
         served from WordPress only and does not concern Plone CMS.
         This stylesheet will theme WordPress specific tags,
         like blog posts and comment fields.
         We keep this file in Plone, but this could be served from elsewhere. -->
    <append css:theme="head">
        <style type="text/css">
           @import url(http://mfabrik.com/++resource++plonetheme.mfabrik/wordpress.css);
        </style>
    </append>

    <!-- This stylesheet is used by special spam protection plug-in NoSpamNX -->
    <append css:theme="head">
        <link rel="stylesheet" href="http://blog.mfabrik.com/wp-content/plugins/nospamnx/nospamnx.css" type="text/css" />
    </append>

    <!-- Remove Google Analytics script used for CMS site -->
    <drop css:theme="#page-bottom script" />

    <!-- Rebuild our Google Analytics code, using a different tracker id this time
         which is a specific to our blog.
      -->
    <append css:theme="#page-bottom">

        <script type="text/javascript">
                var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
                document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
        </script>

        <script type="text/javascript">
                try {
                       var pageTracker = _gat._getTracker("UA-8819100-2");
                       pageTracker._trackPageview();
                } catch(err) {
                }
        </script>
    </append>

</rules>

WordPress specific CSS

This CSS has styles which are applied only to WordPress pages. They are mainly corner case fixes where WordPress and CMS styles must match. The CSS file is loaded when rules.xml injects it to <head> section. wordpress.css:

/* Font and block style fixes */

#wordpress-content h1 {
        border: 0;
}

#wordpress-content .post-end {
        margin-bottom: 60px;
}

#wordpress-content pre {
        width: 600px;
        overflow: auto;
        background: white;
        border: 1px solid #888;
}

#wordpress-content ul {
        margin-left: 20px;
}

#wordpress-content .post-info-date,
#wordpress-content .post-info-categories,
#wordpress-content .post-info-tags {
        font-size: 80%;
        color: #888;
}

/* Make sure that posts and comments look sane in our theme */

#wordpress-content .post {
        margin-top: 15px;
}

#wordpress-content .commentlist li {
        margin: 20px;
        background: white;
        padding: 10px;
}

#wordpress-content .commentlist li img {
        float: left;
        margin-right: 20px;
        margin-bottom: 20px;
}

#wordpress-content #commentform {
        margin: 20px;
}

#wordpress-content {
        margin-left: 20px;
        margin-right: 20px;
}

/* Make WordPress "sidebaar" look like Plone "portlets */

.template-wordpress_listing #portal-column-one ul {
        list-style: none;
        margin-bottom: 40px;
}

.template-wordpress_listing #portal-column-one ul#Recent li {
        margin-bottom: 8px;
}

.template-wordpress_listing #portal-column-one ul#Categories a {
        line-height: 120%;
}

.template-wordpress_listing #portal-column-one h2 {
        background: transparent;
        border: 0;
        font-weight:normal;
        line-height:1.6em;
        padding:0;
        text-transform:none;
        font-size: 16px;
        color: #9b9b9b;
        border-bottom:4px solid #CDCDCD;
}

Helper script

The following Python script (xdv.py) makes it easy for us

  • Recompile the theme
  • Test the theme applied on the site
  • Preview the theme in our browser
It is basically wrapped with default file locations around
bin/xdvcompiler and bin/xdvrun commands with some webbrowser opening magic.

xdv.py:

"""

 This command line Python script compiles your rules.xml to XDV XSL

 Modify it for your own needs.

 It assumes your buildout.cfg has xdv section and generated XDV
 commands under bin/

 To compile, execute in the buildout folder::

     python src/plonetheme.mfabrik/xdv.py

 To build test HTML::

     python src/plonetheme.mfabrik/xdv.py --test

 To build test HTML and preview it in browser, execute in buildout folder::

     python src/plonetheme.mfabrik/xdv.py --preview

"""

import getopt, sys
import os
import webbrowser

# rules XML for theming
RULES_XML = "src/plonetheme.mfabrik/deliverance/etc/rules.xml"

# Which XSL file to generate for compiled XDV
OUTPUT_FILE = "theme.xsl"

# Which file to generate applied theme test runs
TEST_HTML_FILE = "test.html"

# Our "theme.html" is a remote template served for each request.
# Because we are doing live integrattion, this is a HTTP resource,
# not a local file.
THEME="http://mfabrik.com/news/wordpress_listing/"

#
# External site you are theming.
# Note: must have ending slash (lxm cannot handle redirects)
#
SITE="http://blog.twinapex.fi/"

try:
    opts, args = getopt.getopt(sys.argv[1:], "pt", ["preview", "test"])
except getopt.GetoptError, err:
    # print help information and exit:
    print str(err) # will print something like "option -a not recognized"

# Convert options to simple list
opts = [ opt for opt, value in opts ]

print "Compiling transformation"
value = os.system("bin/xdvcompiler -o " + OUTPUT_FILE + " " + RULES_XML +" " + THEME)
if value != 0:
    print "Compilation failed"
    sys.exit(1)

if "-p" in opts or "--preview" in opts or "-t" in opts or "--test" in opts:
      print "Generating test HTML page"
      value = os.system("bin/xdvrun -o " + TEST_HTML_FILE + " " + OUTPUT_FILE + " " + SITE)
      if value != 0:
          print "Page transformation failed"
          sys.exit(1)

if "-p" in opts or "--preview" in opts:
    # Preview the result in a browser
    # NOTE: OSX needs Python >= 2.5 to make this work

    # Make sure test run succeeded
    url = "file://" + os.path.abspath(TEST_HTML_FILE)
    print "Opening:" + url

    # We prefer Firefox for preview for its superious
    # Firebug HTML debugger and XPath rule generator
    try:
        browser = webbrowser.get("firefox")
    except webbrowser.Error:
        # No FF on the system, or OSX which can't find its browsers
        browser = webbrowser.get()

    browser.open_new_tab(url)

Compiling the theme

This will generate XSL templates to do theming transform. It will compile rules XML with some boilerplate XSL. Running our compile script:

python src/plonetheme.mfabrik/xdv.py

Since Plone usually does not use any relative paths or relative resources in HTML, we do not give the parameter “Absolute prefix” to the compilation stage. In Plone, everything is mapped through a virtual hosting aware resource locator: portal_url and VirtualHostMonster. For more information see

Testing the theme

The following command will apply theme for an example external page:

bin/xdvrun -o theme.html theme.xsl http://blog.twinapex.fi
firefox theme.xhtml

… or we can use shortcut provided by our script …

python src/plonetheme.mfabrik/xdv.py --preview

Applying the theme in Apache production environment

These steps tell how to apply the integration theme for WordPress when WordPress is running under Apache virtualhost.

Installing dependencies

We use Apache and mod_transform. Instructions how to set up modules for Apache are available on XDV homepage. Some hand-build modules must be used, but instructions to set them up for Ubuntu / Debian are available. Apache 2 supports filter chains which allow you to perform magic on HTTP response before sending it out. This corresponds Python’s WSGI middleware. We’ll use special built of mod_transform and mod_depends which are known to working. These modules were forked from their orignal creations to make them XDV compatible, as the orignal has not been updated since 2004 (here you can nicely see how open source guarantees “won’t run out of support” freedom).

Example:

sudo -i
apt-get install libxslt1-dev libapache2-mod-apreq2 libapreq2-dev apache2-threaded-dev
wget http://html-xslt.googlecode.com/files/mod-transform-html-xslt.tgz
wget http://html-xslt.googlecode.com/files/mod-depends-html-xslt.tgz
tar -xzf mod-transform-html-xslt.tgz
tar -xzf mod-depends-html-xslt.tgz
cd mod-depends-html-xslt ; ./configure ; make ; make install ; cd ..
cd mod-transform-html-xslt ; ./configure ; make ; make install ; cd ..

Enable built-in Apache modules:

a2enmod filter
a2enmod ext_filter

For modules depends and transform you need to manually add them to the end of Apache configuration, as they do not provide a2enmod stubs for Debian. Edit /etc/apache2/apache.conf:

LoadModule depends_module /usr/lib/apache2/modules/mod_depends.so
LoadModule transform_module /usr/lib/apache2/modules/mod_transform.so

You need to hard reset Apache to make the new modules effective:

/etc/init.d/apache2 force-reload

Virtual host configuration

Below is our virtualhost configuration which runs WordPress and PHP. Transformation filter chain has been added in. /etc/apache/sites-enabled/blog.mfabrik.com:

<VirtualHost *>

    ServerName blog.mfabrik.com
    ServerAdmin info@mfabrik.com

    LogFormat       combined
    TransferLog     /var/log/apache2/blog.mfabrik.com.log

    # Basic WordPress setup

    Options +Indexes FollowSymLinks +ExecCGI

    DocumentRoot /srv/www/wordpress

    <Directory /srv/www/wordpress>
        Options FollowSymlinks
        AllowOverride All
    </Directory>

    AddType application/x-httpd-php .php .php3 .php4 .php5
    AddType application/x-httpd-php-source .phps

    # Theming set-up

    # This chain is used for public web pages
    FilterDeclare THEME
    FilterProvider THEME XSLT resp=Content-Type $text/html

    TransformOptions +ApacheFS +HTML
    # This is the location of compiled XSL theme transform
    TransformSet /theme.xsl

    # This will make Apache not to reload transformation every time
    # it is performed. Instead, a compiled version is hold in the
    # virtual URL declared above.
    TransformCache /theme.xsl /srv/plone/twinapex.fi/theme.xsl

    # We want to apply theme only for
    # 1. public pages (otherwise WordPress administrative interface stops working)
    <Location "/">
        FilterChain THEME
    </Location>

    # 2. Admin interface and feeds should not receive any kind of theming
    <LocationMatch "(wp-login|wp-admin|wp-includes)">
        # The following resets the filter chain
        # http://httpd.apache.org/docs/2.2/mod/mod_filter.html#filterchain
        FilterChain !
    </LocationMatch>

</VirtualHost>

Running it

After Apache has all modules enabled and your virtualhost configuration is ok, you should see WordPress through your new theme by visiting at the site served through Apache:

Automatically reflecting CMS changes back to XDV theme

The theme should be recompiled every time

  • Plone is restarted: CSS references change in <head> as CSS cache is rebuilt
  • CSS is modified: CSS references change in <head> as CSS cache is rebuilt
  • Plone content is changed and changes reflect back to WordPress theme (e.g. a new top level site section is being added)

This is because the compilation will hard-link resources and template snippets to resulting the theme.xsl file. If hard-linked resources change on the Plone site, the transformation XSL file does not automatically reflect back the changes. It could be possible to use Plone events automatically to rerun theme compilation when concerned resources change. However, the would be quite complex. For now, we are satisfied with a scheduled task which will recompile the theme now and then. Alternatively, mod_transforms could be run in non-cached mode with some performance implications. Here is a shell script, update-wordpress-theme.sh, which will perform the recompilation and make Apache’s transformation cache aware of changes:

#!/bin/sh
#
# Periodically update WordPress theme to reflect changes on CMS site
#

# Recompile theme
sudo -H -u twinapex /bin/sh -c cd /srv/plone/twinapex.fi ; python src/plonetheme.mfabrik/xdv.py

# Make Apache aware of theme changes
sudo apache2ctl graceful

Then we call it periodically in cron job, every 15 minutes in /etc/cron.d/update-wordpress:

# Make WordPress XDV theme to reflect changes on CMS
0,15,30,45 * * * * /srv/plone/twinapex.fi/update-wordpress-theme.sh

Updating WordPress settings

No changes on WordPress needed if the domain name is not changed in the theme transformation process.

Site URL

Unlike Plone, WordPress does not have decent virtual hosting machinery. It knowns only one URL which is uses to refer to the site in the external context (e.g. RSS feeds). This setting can be overridden in

  • WordPress administrative interface
  • wp-config.php

Here is an example how we override this in our wp-config.php:

// http://codex.wordpress.org/Editing_wp-config.php#WordPress_address_.28URL.29
define('WP_HOME','http://blog.mfabrik.com');
define('WP_SITEURL','http://blog.mfabrik.com');

HTTP 404 Not Found special case

Http 404 Not Found responses are not themed by Apache filter chain. This is not possible due to order of pipeline in Apache. As a workaround you can set up a custom HTTP 404 page in WordPress which does not expose the old theme.

  • Go to WordPress admin interface, Theme editor
  • Edit 404.php and modify it so that it does not pull in the WordPress theme:
    <html>
    <head>
    <title>Not found</title>
    </head>
    <body>
    
            <h1>Not Found, Error 404</h1>
            <p>Aaaaw, snap! The page you are looking for no longer exists. It must be our hamster who ate it.</p>
    
            <a href="<?php bloginfo('url'); ?>">Go to blog homepage</a>
    
            <a href="http://mfabrik.com">mFabrik business site</a>
    </body>
    </html>
    

For more information see

Roll-out checklist

Below is a checklist you need to go to through to confirm that the theme integration works on your production site

  • WordPress public pages are loaded with the new theme
  • WordPress login works
  • WordPress administrative interface works
  • RSS feed from WordPress works and contain correct URLs
  • HTTP 404 not found is handled correctly
  • HTTP 302 redirect is handled correctly (i.e. missing / at the end of blog post URL)
  • Changes on CMS site are reflected to WordPress theme within the update delay
  • Old blog site is redirected to new site using HTTP 301 (if applies)