Skip to content. | Skip to navigation

Personal tools

>>> ''.join(word[:3].lower() for word in 'David Isaac Glick'.split())

‘davisagli’

Navigation

You are here: Home

David Glick – Plone developer

by admin posted Apr 05, 2010 11:48 PM

Report from Cioppino Sprint 2012

by David Glick posted Mar 26, 2012 11:45 PM

Twelve Plonistas gathered together in Bodega Bay to write documentation and be merry.

Report from Cioppino Sprint 2012

Mr. Crabby

I've just returned from yet another memorable Plone event, the 2nd annual Cioppino Sprint. For the past 4 days, twelve of us gathered at a house in lovely Bodega Bay, CA for a weekend of fun, relaxation, and giving back to the Plone community.

The theme of the sprint was improving Plone's documentation and community infrastructure. On the first evening we gathered to brainstorm tasks, which were recorded on sticky notes to be placed on a scrum board indicating which tasks were pending, in progress, and complete. On Saturday, the sprinting began in earnest.

 

Sprint house

Tasks completed

Ross Patterson wrote a tutorial introducing the basics of zc.buildout. He also started investigating what it would take to let Plone be installed by Microsoft's free Web Platform Installer for Windows/IIS.

Luke Brannon and Ross wrote some CSS for reStructuredText "admonishments" (i.e. note/warning/etc. boxes) that will be added to plone.org for when reST documents are rendered. Luke also refactored and tested Windows installation instructions in the knowledgebase, reviewed KB articles for accurate version metadata, and started looking into sorting KB topic pages by Plone version in addition to modified date.

Spanky Kapanka learned how to contribute to the collective developer manual, then worked on writing several quick start guides to walk Plone beginners through specific tasks. He completed one on subclassing a content type and started one on creating a custom form.

Bill Deegan edited the introduction to the collective developer manual for clarity and to present Plone's capabilities in a more positive light alongside the fair warnings of complexity. He also renamed the files in the collective docs to have a .rst extension so they render when viewed on github, and tested Spanky's quick start.

Mike Cullerton learned git and reStructuredText, then worked on a "quick start" guide on customizing views and viewlets.

Liz Leddy worked on a new set of documentation of how to do Plone core development, which will live in the coredev buildout. She also did a lot of cleanup in Plone's trac and moved information to buildout.coredev and to a new, streamlined "Get Involved" landing page which replaces the front page of trac as an entry point to participating in the Plone community.

Steve McMahon worked on writing a Plone sysadmin manual discussing how to install and configure Plone for deployment scenarios on various platforms. I think he is about two-thirds done and has not published the document yet.

Tyler Randles (of ploneconf panda head fame) and I worked on improving the template for the knowledgebase documentation. We added a box at the top which shows a color-coded warning about the document's version compatibility, and a box at the bottom with a brief invitation to contribute to editing the knowledgebase (you too can help review KB articles to make sure their metadata is correct!). We also created mr.crabby, your blue crabby friend for Plone—can you find him on plone.org? (Hint: search plone.org for info on the Cioppino Sprint.) Tyler also worked on illustrations for Steve's manual.

I, David Glick, also added a version selector to the search forms in the plone.org documentation section, fixed a bug in the knowledgebase permissions so that articles are actually editable by the right people (i.e. anyone logged in), reviewed some of Liz's core dev docs, added i18n support to plone.supermodel and plone.registry's GenericSetup import handler in order to fix a Plone 4.2 blocker, and did some planning about how to better organize Dexterity's documentation.

Fulvio Casali reviewed and tried out the existing Dexterity documentation and created a detailed list of suggested improvements which will be very helpful.

Florian Friesdorf wrote instructions for setting up a Plone development environment using nix, and added support for paged searches to pas.plugins.ldap.

Eric Steele worked on getting Plone 4.2 ready for final release, including a lot of work on the new-style collections. (It needed some work on the date criteria for feature parity with the existing collections, and some work to make sure that Plone's default news and event collections are the new style.) By the end of the sprint Eric announced that there are no more blockers for the second release candidate!

Hanno Schlichting joined the #sprint IRC channel remotely and continued his quest for an improved ZCatalog. He suggested some catalog optimizations for plone.org which I will hopefully review soon, and implemented two long-desired new features: "not" queries and sorting results by multiple indexes.

LolaLola and Zoey, the dogs, sprinted along the beach and made themselves generally lovable.

Highlights

A highlight of the sprint for many of us was the food. Mike made homemade pizzas and introduced us to whoopie pies with habanero. Spanky prepared breakfast after wonderful breakfast, including his recently perfected Hollandaise for eggs benedict with crab cakes. For snacks we had Luke's guacamole and Spanky and Tyler's invention of tater tot-bacon-pepperjack cheese bites. I baked sourdough bread to go with Steve's cioppino featuring fresh crab, mussels and clams, and Liz made a great chili that was dubbed "Holy Crap!" Some of these recipes have been posted to the sprint mailing list so look for them there.

Food!

Another highlight for me was participating in my first "code dojo." This is a technique for sharing knowledge where a group gathers to complete a task surrounding a single big TV or projector screen. One person gets to control the keyboard, another person tells the typist what to do, and everyone else can be consulted for advice. We used this technique to work toward solving issue 12796, as an exercise in how to fix a bug in Plone core for those who hadn't done so before (and as a "functional test" of Liz's new documentation of this process). We didn't have time to complete the task, but I think everyone had fun and learned something new (within the first five minutes I learned a new bash keyboard shortcut!).

Beach exploring


One thing that did not happen at the sprint was a clear designation of a revitalized documentation team to make sure that our documentation is well-managed on an ongoing basis. Personally I feel that this is a role that is lacking in the community—Mikko and others are doing a fantastic job of getting people to add and update documentation in the collective developer manual, but I there is a need for a more focused manual and introductory documentation for people who are trying to learn Plone development for the first time rather than looking up particular tasks or topics—communally edited documentation is inevitably of varying quality and relevance, and newbies have no way to judge that. There is also a need to make sure that old documents are updated or marked as obsolete as appropriate. It's not entirely surprising that we've lacked this editing, as it is a thankless task (everyone can get excited about new documentation; someone always hates you when you delete something). I don't have answers but I hope we can brainstorm as a community about how to solve this problem (or maybe you can convince me I've diagnosed the wrong problem.)

But despite a lack of resolution of those questions, the sprint was a success in the realms of both fun and productivity. Thanks to the participants for a fantastic experience, to the Plone Foundation for sponsoring our lodging, and I look forward to seeing you at Cioppino 2013 if not before!

p.s. A special thanks to Spanky who provided a limo pickup and lodging to Fulvio and me after our train was canceled and we had to change travel plans at the last minute!

More photos

1 comment

Using tiles to provide more flexible Plone layouts

by David Glick posted Feb 04, 2012 01:05 PM

Tiles provide a powerful way to expand the limits of Plone layout while keeping content manageable.

Using tiles to provide more flexible Plone layouts

A tile in action on the Net Impact website

Update 2/8/2012: Fixed the code sample (some HTML markup had gotten filtered out by my blog editor).

Plone does great at in-place editing: navigate to the thing you want to edit, then click the button and edit it. However, this paradigm breaks apart as soon as there is a need for a page to have multiple editable areas—such as for a homepage or section landing page.

At Groundwire, we used to deal with this problem by creating an ongoing series of very similar hacky one-off templates: the sort of template that would have have several areas which each pulled in the content from some item in a hidden folder of page components. Unfortunately this approach did not scale very well: it was tedious for us to set up new templates, and it was cumbersome for editors to remember how everything was set up in order to successfully make changes.

Last year we worked on the Net Impact website which has a different multi-part layout for each section landing page, and we realized that we needed to come up with a better solution. The requirements:

  • Someone writing a template should be able to define an editable area of that template very simply, by just adding a line to the template that specifies the name of the area.
  • There should be support for different types of editable areas; each type may have different settings when editing the area.
  • Editing an area should be triggered by a pencil icon that shows up while hovering over the area for users who have permission to edit the area,
  • All this should be done in a way that is simple to reuse for new sites.

Tiles to the rescue

We realized right away that our requirements were very similar to the functionality provided by the Deco project's implementation of "tiles." Deco is an ambitious project to provide drag-and-drop layout capabilities within Plone. Deco as a whole was not mature enough for us to feel comfortable using it, but I knew that the tile rendering was one of the older and more mature parts of Deco, and we realized that it would not take a lot of effort to use tile rendering without the rest of Deco.

A tile is a snippet that can be inserted into a template as a div with a data-tile attribute, like this:

<div data-tile="/Plone/@@mytile" />

Then some machinery in the publisher provided by plone.app.blocks performs the following steps:

  1. It finds all the divs with a data-tile attribute (let's call them tile placeholders).
  2. For each one, it performs a subrequest to fetch the contents of the tile. Using a URI makes tiles very flexible: a tile could be a browser view, or it could come from some external system.
  3. The tile placeholder is replaced with the contents of the tile's body tag. If the tile has a head tag, its contents will be appended to the head of the page that includes the tile.

That's a great start! As it turns out, other parts of the tiles implementation also help support our use case:

  • plone.tiles has a tile implementation which supports having multiple tile types. A tile turns out to basically be a browser view that also happens to have some associated data. (This is a lot like a portlet renderer, but one that can be added anywhere with a line in a template rather than needing to mess around with portlet managers.) Each type of tile can specify a different schema for its data, and that data can be persisted in different ways.
  • plone.app.tiles provides an edit form that takes care of editing the data for a particular instance of a tile.

In practice: adding a rich text tile

So let's see how this plays out in practice. We are going to:

  1. Set up the basic tile rendering machinery.
  2. Implement a rich text tile that can be added anywhere, and that stores its contents in an annotation of the context where it is added.
  3. Make sure that editors see a pencil icon that brings up an modal overlay to edit the tile.

The basics

Okay, let's get the basics set up.

  1. Create a package that declares dependencies on: lxml, plone.app.blocks, plone.app.textfield, plone.app.tiles, and plone.tiles.
  2. At the time of this writing, you need trunk checkouts of plone.app.blocks, plone.app.tiles, and plone.tiles.
  3. Make sure that your configure.zcml includes <includeDependencies package="."/>.
  4. Make sure the metadata.xml in your package's GenericSetup profile runs the default profiles from plone.app.blocks and plone.app.tiles as dependencies.
  5. Install your package.

The tile

Add a tile.py with the following:

from zope.interface import Interface

from plone import tiles
from zope.schema import Text
from plone.app.textfield import RichText
from plone.app.textfield.interfaces import ITransformer


class IRichTextTileData(Interface):

    text = RichText(title=u'Text')


class RichTextTile(tiles.PersistentTile):

    def __call__(self):
        text = ''
        if self.data['text']:
            transformer = ITransformer(self.context, None)
            if transformer is not None:
                text = transformer(self.data['text'], 'text/x-html-safe')
        return '<html><body>%s</body></html>' % text

In configure.zcml, add:

  <plone:tile
      name="groundwire.tiles.richtext"
      title="Groundwire rich text tile"
      description="A tile containing rich text"
      add_permission="cmf.ModifyPortalContent"
      schema=".tile.IRichTextTileData"
      class=".tile.RichTextTile"
      permission="zope2.View"
      for="*"
      />

This defines a new tile type, called groundwire.tiles.richtext. This tile type has a schema with a single rich text field, and when it is rendered the tile will run the configured text through the safe HTML transform to make sure it is safe.

Wiring in the edit form

Now we just need to make sure that editors will have a way to access the edit interface for tiles.

Add the following javascript. Make sure you put a condition on it like "python:object.portal_membership.checkPermission('Modify portal content', object)" so that it will only run and add the edit links for users who have permission to edit.

jQuery(function($) {
  $('div[data-tile]').each(function() {
      $(this).addClass('tile-editable');
      var href = $(this).attr('data-tile');
      var edithref = href.replace(/@@/, '@@edit-tile/');
      $('<a class="tile-edit-link" href="' + edithref + '"><img height="16" src="pencil_icon.png" width="16" />')
        .appendTo($(this))
        .prepOverlay({
            subtype: 'iframe',
            config: {
                onClose: function() { location.reload(); }
            }
        });
  });
  
  // Check if tiledata is available and valid
  if (typeof(tiledata) !== 'undefined') {

      // Check action
      if (tiledata.action === 'cancel' || tiledata.action === 'save') {
          // Close dialog
          window.parent.jQuery('.link-overlay').each(function() {
              try {
                  window.parent.jQuery(this).overlay({api: true}).close();
              } catch(e) { }
          });
      }
  }
  
});

This adds an edit link to all the divs that have data-tile attributes. It also handles the "tiledata" which is how the plone.app.tiles edit form controls when the overlay it appears in should close.

And finally we need a bit of CSS to style the tiles and edit links:

.tile-editable {
    position: relative;
    outline: 2px dashed #e8e8e8;
    min-height: 1.5em;
}

.tile-editable:hover {
    outline: 2px dashed #b8b8b8;
}

.tile-edit-link {
    display: none !important;
    position: absolute;
    right: 1px;
    bottom: 1px;
    z-index: 500;
}

.tile-editable:hover .tile-edit-link {
    display: block !important;
}

Adding a tile

Okay, now let's add one of these to a template. Pick your favorite template and add:

<div tal:attributes="data-tile string:${context/absolute_url}/@@groundwire.tiles.richtext/hello-world" />

Here's what it looks like in my instance (I added it to the document_view template):

A tile

And here's the editing interface that shows up when I click on the pencil:

Tile editing

(If you want to see how this code all comes together, look at the code in https://groundwire.devguard.com/svn/public/groundwire.tiles/branches/davisagli-blocks)

In conclusion

We are very happy with the way the tile approach turned out for the Net Impact site. Once we had mastered the basic technique for landing pages, we soon realized that tiles provided a useful way to add user-editable content areas anywhere in the site. Site needs a doormat in the footer? Use a tile with the Plone site as its context so it appears the same throughout the site and the client can edit the links. Client wants a block of text they can edit on the login form to promote registering for the site? No problem, just add a tile. Client is repeatedly asking for minor edits to the text introducing a custom form? No problem, we turned it into a tile and told them how to edit it. Since the presentation of tiles in the UI is simple and consistent, the barrier to entry for the client to learn how to edit a new tile was very low.

The approach as described here isn't perfect. One thing that needs some care is cache invalidation. In our case, we wrote an ObjectModified event handler for tiles that updates the modified time of the page on which the tile appears. Another limitation is that text in tiles won't be included in the fulltext index unless you go to extra lengths. Whether that's a feature or a bug depends on your use case.

Overall though, we love the technique and have also started using tiles in other sites. I know that Six Feet Up has also successfully used tiles with at least one client. If you want to expand your Plone layout repertoire without using experimental technology like Deco or removing control over content from your clients, I encourage you to give it a try!

4 comments

Backporting a topic branch with git

by David Glick posted Nov 26, 2011 11:16 AM

As a maintainer of Plone and Dexterity, I frequently find myself with the need to merge a pull request not only to the master branch, but also to a maintenance branch used for bugfix releases to older versions of the software.

When the pull request involves a single commit, it's pretty straightforward: I merge the pull request to master through github's UI, or via the command line with git merge. Then I check out the maintenance branch and use "git cherry-pick" to apply the changeset from the commit relative to the older branch.

But this quickly gets annoying if the branch I'm trying to merge involved multiple commits, and I have to cherry-pick each one in turn. So here's a different approach I used yesterday to backport a changeset from zedr (Rigel di Scala) to add an Italian translation to plone.dexterity.

His pull request was against the master branch, so I first merged that using the github UI.

Then I needed to apply the same change to the 1.x branch of plone.dexterity.

In my local copy of the plone.dexterity repository, I added zedr's fork as a remote, and fetched it.

$ git remote add zedr https://github.com/zedr/plone.dexterity.git
$ git fetch zedr
From https://github.com/zedr/plone.dexterity
 * [new branch]      1.x        -> zedr/1.x
 * [new branch]      davisagli-extend-fsschema -> zedr/davisagli-extend-fsschema
 * [new branch]      jbaumann-locking -> zedr/jbaumann-locking
 * [new branch]      jmehring-drafts -> zedr/jmehring-drafts
 * [new branch]      master     -> zedr/master
 * [new branch]      toutpt-unicode -> zedr/toutpt-unicode

Next I created a new branch called "zedr-merge" specifically for carrying out the merge, based on zedr's forked master branch which contained the change. I needed a temporary branch for this in order to carry out the rebase in the next step.

$ git co -b zedr-merge zedr/master
Branch zedr-merge set up to track remote branch master from zedr.
Switched to a new branch 'zedr-merge'

Now for the fun part. I used rebase to modify the zedr-merge branch's history so that it contains the commits from the 1.x branch, followed by only the existing commits from the zedr-merge branch that I wanted.

$ git rebase -i --onto 1.x a36f40743d67da8e6d5c7b0aee81e786a2de9f5e

Let's break this down. The rebase command will first store all commits on zedr-merge from a36f40743d67da8e6d5c7b0aee81e786a2de9f5e to HEAD in a temporary location (I found this hash using git log to identify the last commit prior to the changes I was trying to backport). Next it resets the history and state of the zedr-merge branch to be equivalent to that of the 1.x branch (because of the "--onto 1.x"). Finally it reapplies the changes that were stored in the temporary location. The end result is that we have exactly the history we want—that is, the 1.x history plus the relevant portion of the zedr/master history—but it is on the zedr-merge branch rather than on 1.x where we want it to end up. We'll deal with that in a moment.

Notice one more thing about the rebase command. I used the -i flag, which means interactive rebase. This means that I'll be prompted in an editor with a list of commits, and can choose to "pick" (include), remove, or "squash" (merge into the prior commit) each commit. In my case, since zedr had a number of commits making small changes to his translation file that were really all part of the same change at the macro level (adding the Italian translation file), I squashed them all together so that I ended up with a single commit at the HEAD of the zedr-merge branch which accomplished the same changes as all of the commits zedr had made on his master branch.

pick 8562549 Added Italian translation
squash d035596 Correct timestamps
squash 9fa9904 Removed template header
squash 4a97700 cancel does not really mean that; fixed (thanks gborelli)
squash aef40f1 messags now coherent with the standard Italian translations found elsewhere
squash 621e185 Updated changelog

# Rebase a36f407..621e185 onto 9408d8d
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

At this point, since I had a single commit that I cared about on zedr-merge, it was a simple matter to use git cherry-pick to apply it to the 1.x branch instead.

$ git co 1.x
$ git cherry-pick 6dec9429d7994f02e332e22f8009a687ee3944c0

And now git log shows the change all together nicely in one commit:

$ git log 1.x
commit 6dec9429d7994f02e332e22f8009a687ee3944c0
Author: zedr <zedr@zedr.com>
Date:   Mon Nov 21 12:29:32 2011 +0100

 Added Italian translation
 
 Correct timestamps
 
 Removed template header
 
 cancel does not really mean that; fixed (thanks gborelli)
 
 messags now coherent with the standard Italian translations found elsewhere
 
 Updated changelog

This is really the story of my first realization of the power of git rebase. But one word of warning: you should not rebase commits if they have been shared (i.e. by pushing to github) and others may have based further work on them. It can lead to duplicate commits in the history if the derivative branch is later merged as well. In my case this is not a concern, though, since the maintenance branch will never be merged back to master.

I really don't know if this is the best method for my use case, but it at least had the end effect I was going for in this case. I'm still not quite sure what the simplest approach would be if I wanted to end up with all the commits from zedr/master separately in the history of the 1.x branch, instead of squashing them. I'd be interested to hear if other people are using different approaches.

2 comments
David Glick

David Glick

I am a problem solver trying to make websites easier to build.

Currently I do this in my spare time as a member of the Plone core team, and during the day as an independent web developer specializing in Plone and custom Python web applications.