In a recent posting I outlined my plans for the next generation of the "Produce & Publish Cloud Edition".
Over the last days I found some time prototyping the new core implementation which is basically responsible for storing editoral content and published content. A major requirement for the storage implementation is that it should be "cloud-aware". Fortunately there is a very nice Python project called pyfilesystem. pyfilesystem provides a filesystem-ish layer on top of various filesystems and cloud-ish services like
- Amazon S3
- Local filesystem
- Remote filesystem access through SFTP
The benefit is obvious: you write your code once and it will run (in theory) out-of-the-box either on the local filesystem or against a remote filesystem.
And in fact: my code that I wrote and tested originally only against the local filesystem worked basically out-of-the-box with Amazon S3 and over SFTP. Support for Dropbox over dropbox-fs has been added yesterday. This took longer than expected because Dropbox provides only access through OAuth. So I had to automate the complete OAuth request/acknowledge game that usually happens manually using some lines of mechanize in Python. So Dropbox supports works in general however the underlaying dropbox-fs or the Dropbox Python SDK seems to be a bit flaky - but the errors are reproducable - so there is hope that the issues can be fixed easily.
Produce & Publish now got a solid filesystem storage basis (instead of Plone-only). This will make the further progress so much easier and more straight-forward than depending on the Plone-API and Plone annoyances only.
Issues I had with Bootstrap:
- At the bottom of the zopyx.de front-page there is an image section with reference customer logos. The number of images to be displayed depends on the width of the browser windows (for desktop browser I am using six images, iPad in landscape mode shows up with five images and iPad in portrait mode displays 4 images). By default Bootstrap on iPad moved two references logos into a second row because six images did not fit into one row...working but ugly....as a workaround I added some media queries just hiding the 5th or 6th logo depending on the width of the browser window.
- For iPhone I made the decision to disable parts of the front-page e.g. the large image slider and the customer references do not show up (display: none). However the images are still being loaded although not loaded...nasty and a waste of bandwidth.
Although I am pretty happy with the result so far I am convinced that the current programming model for responsive sites is partly broken.
Decisions when to render what and how is scattered among different places
- CSS (with media queries)
- backend code
I am currently working on the relaunch of my Produce & Publish product site. I decided to go with Pyramid (instead of Plone) this time since different functionality has to be integrated under one hood. The core application is based on a twitter/bootstrap theme (nothing special so far). I wrote already a bunch of documentation using Sphinx (result hosted on docs.produce-and-publish.com). Now the Sphinx site/content should be integrated into the Pyramid application. One option would be using a dedicated twitter/bootstrap theme for Sphinx or using Diazo. I decided against both options. A twitter/bootstrap theme for Sphinx is doable but requires double work (keeping the twitter/bootstrap theme in Sphinx and Pyramid in sync). Using Diazo also was not much appealing...yet another moving part.
The solution I come up with is the following:
- Sphinx supports the export/generation of the content as JSON (JSON builder aka "make json").
- The generated JSON documents can be used easily inside my Pyramid application through a tiny traverser dealing with paths and extensions
- The traverser code can be found here: https://github.com/zopyx/zopyx.pyramid.ppdemo/blob/master/ppdemo/views.py
Now traversing to /documentation/index will cause Pyramid to traverse into a specific part of the filesystem holding the documentation JSON-ified by Sphinx, picking up the JSON data and rendering it using a tiny template (see https://github.com/zopyx/zopyx.pyramid.ppdemo/blob/master/ppdemo/templates/sphinx.pt).
Disclaimer: the code is a proof of concept and more a hack than a final solution...but it works nicely so far...all image are being display and all links are fully working
For a long time there was a test() method available in ZPT:
<td tal:attributes="class python: test(some_condition, 'even', 'odd')">
With the move to browser views the test() had gone and you had to write something like
<td tal:attributes="class python: some_condition and 'even' or 'odd')">
as a replacement. This notation may have side-effects in some rare cases.
Now with Python 2.6 or higher you can rewrite your code the following way
<td tal:attributes="class python: 'even' if some_condition else 'odd')">
in a more clean way. In addition this notation is free of side-effects.
We had some internal discussions about using LessCSS in upcoming Plone projects for simplifying work (more compact CSS code, less redundant information etc.) so I played with LessCSS a bit today. The basic problem I cam e across was the issue how to integrate LessCSS with Plone from the web designers prospective. The typical working pattern of a Plone web designer is the following:
- start your instance in debug mode
- edit the styles files (.css) on the file-system
- save style files
- reload site in browser
Working with LessCSS is slightly different:
- you need to edit a .less file
- you need to compile the .less file manually using the lessc compiler
So basically one manual step more than necessary.
Here is a quick solution for the problem:
Based on the watchdog module for Python I wrote a small observer module that can be included with your package easily (from the __init__.py file of your package). The code below will start the observer only if your Zope/Plone instance is running in foreground.
from App.config import getConfiguration
zconfig = getConfiguration()
After restarting your instance a dedicated observer thread will watch for file system changes inside the current policy or theme package. The event handler listens only to files matching *.less. For modification and creation event the handler will automatically call the LessCSS compiler and generate a corresponding .css file inside the same directory.
Cavecats: you need to add the watchdog egg manually to your buildout configuration
Some notes on the 1. German Plone Conference in Munich and recent developments in Produce & Publish
The first German Plone Conference is finally over. It was a great conference with about 170 attendees - both Plone developers, Plone users and interested people. Especially a lot of new people from the Plone users side made this conference an interesting event - lots of interesting people and interesting conversations.
By accident an attendee point me to a PDF converter with similar capabilities to our current converter PrinceXML used for professional high-quality PDF documents. The new converter is PDFreactor and is in many ways comparable to PrinceXML. It also accepts HTML/XML for the content part and supports the styling of output PDF document through CSS. In fact PDFreactor provides a similar output compared to PrinceXML when using the unmodified stylesheets of Produce & Publish by default.
Some remarkable differences (and advantages over PrinceXML) are
- support for tagged PDFs (PDF/a)
- support for images in CMYK colorspace
- generates PDF forms
- PDF signing and encryption
- support for generating barcodes and QR codes
- slightly cheaper than PrinceXML
Downsides of PDFreactor
- slower (based on Java) but supports a webservice API to avoid startup times [Update: PDFreactor is usually run as a server process and access through a webservice API)
- significant higher memory fingerprint
- no floating of images or tables [Update: floating is possible]
- font-configuration not possible through CSS (fonts must be configured as part of the PDFreactor configuration) [Update: PDFreactor version 6 now supports arbitrary webfonts]
What are the benefits for the Produce & Publish plattform?
- another high-quality PDF converter option
- more scenarios where Produce & Publish might ba good choice
- optional PDF generation of accessible PDF documents when needed
- (optional) support of PDFreactor on the Produce & Publish server side (through the newest zopyx.convert2 module with support for PDFreactor)
- (optional) support of PDFreactor through the Produce & Publish Plone client connector (converter=pdf-pdfreactor parameter)
- no direct support for PDFreactor for generating PDF from within the Produce & Publish Authoring Environment (hard-coded PDF converter name - likely to be lifted soon)
A short review of Booktype and a comparison with my own Produce & Publish product.
Last week a new product Booktype appeared in public. Booktype is an open-source authoring solution for creating PDF files and EBooks through the web. Booktype is completely implemented in Python (it is uses Django). It follows the paradigm Single-Source Multi-Channel Publishing which basically means: one content-source for all kind of publications and output formats. As author of the Produce & Publish solution I became curious and did some testing.
The first visual difference to Produce & Publish is that the UI is completely task-oriented. The primary task is: I want to write a book.
Produce & Publish itself supports the same functionality but the approach is a bit more generic and basically offers only the standard Plone user interface to content authors. Produce & Publish provides infinite nesting of chapters, sections etc. while Booktype provides only chapters and sections.
Editing content in Booktype basically works the same way as in Plone. Booktype uses TinyMCE as primary content editor. The editor opens inline (similar to former Plone versions using KSS for inline editing). Honestly I love the inline editing in Booktype feature (as I loved it in Plone 3) - it gives me a better user experience.
In order to generate an Ebook or a PDF file Booktype provides some simple wizards where you can choose from some pre-defined options like fonts, font sizes etc.
The conversion request seems to end up in queue. After the conversion Booktype will present you an URL where you can download the generated output format.
So far, so good. Booktype is very easy to use. The perfect solutions for average editors since the user interface gives you only the options that you need to get your task (writing a book) done. Produce & Publish in comparison exposes many more advanced options to the average editor. So the complexity of Produce & Publish might be overwhelming for this editor type.
Comparison of features: the feature list of Produce & Publish is more complete compared to Booktype. Produce & Publish provides powerful support for features like cross-references, listings for indexes, tables and images, sophisticated handling of images and their resolution. Apart from that each content project in Produce & Publish can be configured with individual stylesheets, assets and templates. Its overall architecture appears more open and pluggable (perhaps as a result of using features of the Zope Component Architecture where needed).
Comparison of output results: at the time of writing I have no idea how content is converted to PDF or to EPUB. The open-source code base does not contain any references to external converters. I assume Booktype provides conversion as a hosted service. A tests reveal that the creator of the PDF files is "ghostscript" but there is no indication what is used for generating the input for "ghostscript". The overall PDF quality is average. Produce & Publish provides high-quality hyphenation support (similar to TeX or LaTeX) - nothing that I could detect in Booktype. Produce & Publish also support features like multi-column rendering of content in PDF, image floats etc.
Overall conclusion: Booktype is great tool for average content editors that need to write a book without having the need to deal with stylesheet or complex configurations. It provides a limited functionality and a great user interface for getting the job done. However Produce & Publish is the better solution (feature-wise and quality-wise).
Latin learning material produced using Produce & Publish is now available from Amazon
The Plone-based Produce & Publish plattform is used to produce high-quality learning materials for the Latein education in German schools.
The project Lektürewerkstatt maintains the complete content inside Plone and generates a PDF from the content that is directly used for the print process without further post-processing.
- Case study: "Einsatz von Produce & Publish am Albrecht-Ernst Gymnasium"
Implementing content-types for Plone site is one part of every typical Plone project. Styling & testing is another one. Both testers and designer need content for testing.
As part of a new project I decided to provide example content for each new content-type. The two core components are the loremipsum module for Python and the lorempixel.com web-service. The Python module can be used to generate single sentences of text or multiple paragraphs of text. The web-service provided random images in arbitrary sizes for you.
Here is a picture of a sample glossary
and one of a simple picture database:
Here is some example code for generating content and random image from using the mentioned services:
return u'/'.join([p for p in loremipsum.Generator().generate_paragraphs(num)])
return u'/'.join([s for s in loremipsum.Generator().generate_sentences(length)])
def random_image(width, height):
url = 'http://lorempixel.com/%d/%d/' % (width, height)
The code for generating a gallery as seen above looks like this:
def installAssets(self, site): service = site.restrictedTraverse('deutschland/de/service') assets = invokeFactory(service, 'Folder', 'Assets') for width,height in ((200,200), (400,400), (600, 400), (800, 600),
(800,800), (1024, 768))
imagefolder_id = '%sx%s' % (width, height) images = invokeFactory(assets, 'Folder', imagefolder_id) for i in range(20): img = invokeFactory(images, 'Image') img.setImage(random_image(width, height)) img.reindexObject()
The invokeFactory() method used here is just a tiny wrapper around the standard invokeFactory() method of a folder object that generates a random title and a normalized id for the new content object plus doing some pre-allocation of the description and text field (if available) on the created content-object.
The complete code can be found on the website of my Plone partner Veit Schiele (German only).