<?xml version="1.0" encoding="utf-8" ?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:syn="http://purl.org/rss/1.0/modules/syndication/" xmlns="http://purl.org/rss/1.0/">




    



<channel rdf:about="http://glicksoftware.com/front-page/RSS">
  <title>David Glick – Plone developer</title>
  <link>http://glicksoftware.com</link>

  <description>
    
      
    
  </description>

  

  
            <syn:updatePeriod>daily</syn:updatePeriod>
            <syn:updateFrequency>1</syn:updateFrequency>
            <syn:updateBase>2010-04-06T06:19:44Z</syn:updateBase>
        

  <image rdf:resource="http://glicksoftware.com/logo.png"/>

  <items>
    <rdf:Seq>
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/goodbye-groundwire"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/report-from-cioppino-sprint-2012"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/using-tiles-to-provide-more-flexible-plone-layouts"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/backporting-a-topic-branch-with-git"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/deep-thoughts-from-plone-irc"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/the-making-of-zodb.ws"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/inspect-your-zodb-with-eye"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/registering-add-on-specific-components-using-z3c.baseregistry"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/plone-4-in-the-news"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/ten-lesser-known-improvements-in-plone-4"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/plone.app.themeeditor-1.0a1-...-and-a-roundup-of-other-recent-releases"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/in-browser-integration-testing-with-windmill"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/review-of-plone-3-products-development-cookbook"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/introducing-the-plone-resource-customizer"/>
      
      
        <rdf:li rdf:resource="http://glicksoftware.com/blog/be-vigilant-know-your-buildout-threat-level"/>
      
    </rdf:Seq>
  </items>

</channel>


  <item rdf:about="http://glicksoftware.com/blog/goodbye-groundwire">
    <title>Goodbye, Groundwire. Hello, world!</title>
    <link>http://glicksoftware.com/blog/goodbye-groundwire</link>
    <description>After 5 years, I'm moving on from Groundwire to work independently.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>In the summer of 2007 I graduated from college, moved to Seattle, and started working as a web developer for <a class="external-link" href="http://groundwireconsulting.com">Groundwire</a>, then called ONE/Northwest. This week, five years later, I'm both sad and excited to announce that I'm moving on.<br /><br />Working at Groundwire has been a fantastic experience for which I am deeply grateful. I got to work with the best colleagues in the world, as well as <a class="external-link" href="http://hcn.org">some</a> <a class="external-link" href="http://wta.org">awesome</a> <a class="external-link" href="http://netimpact.org">clients</a> whose missions I believe in. I learned a lot about shepherding nonprofit organizations through the process of doing a significant technology project. I was privileged to spend time pushing the Plone content management system to the next level and building deep integration between it and the Salesforce.com CRM platform. Groundwire is still doing great work to help nonprofits engage their constituents, and if you're currently looking for a great opportunity to combine your tech skills with a desire to work for good, I can wholeheartedly recommend applying for <a class="external-link" href="http://groundwireconsulting.com/about/careers">one of Groundwire's open positions</a>.<br /><br />But nevertheless, it's time for me to "level up" to the next challenge. After Thursday, I will start working as a freelance contractor specializing in Plone, Python, and web app development. I'm looking forward to working with and contributing my skills to some other great consultancies and clients, as well as learning new tools and techniques for constructing web-based solutions. And I'm looking forward to having a more flexible schedule with more chances to work on my own projects in addition to paid work.<br /><br />The Plone content management system has grown dear to me during my time at Groundwire, and I will not be abandoning it. During the next few weeks leading up to the annual <a class="external-link" href="http://ploneconf.org">Plone conference</a>, I plan to spend as much time as possible working on various Plone-related improvements that I have had scarce time for lately, including:</p>
<ul>
<li>Attending the <a class="external-link" href="http://www.coactivate.org/projects/sea-sprint-2012/project-home">Sea Sprint</a> and pushing forward the Deco layout system</li>
<li>Continuing to improve Dexterity's capabilities for building content types through the web</li>
<li>Helping prepare the release of Plone 4.3 (including completing the demise of KSS)</li>
</ul>
<p>I'll be doing this work on a volunteer basis, but if you've benefited from my contributions to Plone and/or want to help make sure I continue to have time available to work on it, please consider leaving me a tip: <iframe height="20pt" src="https://www.gittip.com/davisagli/widget.html" width="48pt"></iframe><br /><br />Following the Plone conference, I am scheduling projects for mid-October and beyond, and am especially interested in:</p>
<ul>
<li>Chances to work with Plone, Pyramid, and/or other Python-based web frameworks—or train other developers on how to use them well</li>
<li>Web projects that are interactive rather than merely presenting information</li>
<li>Projects that expand what is possible for non-developers to accomplish using Plone</li>
<li>Projects that are driven in part by a "social good" mission rather than merely profit</li>
</ul>
<p>If you have something like that I can assist with, please <a href="http://glicksoftware.com/blog/contact-info" class="external-link">be in touch</a>!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2012-09-11T18:16:57Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/report-from-cioppino-sprint-2012">
    <title>Report from Cioppino Sprint 2012</title>
    <link>http://glicksoftware.com/blog/report-from-cioppino-sprint-2012</link>
    <description>Twelve Plonistas gathered together in Bodega Bay to write documentation and be merry.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I've just returned from yet another memorable Plone event, the 2nd annual <a class="external-link" href="http://www.coactivate.org/projects/cioppino/project-home">Cioppino Sprint</a>. 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.<br /><br />The theme of the sprint was improving Plone's <b>documentation and community infrastructure</b>. 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.</p>
<p> </p>
<p style="text-align: center; "><img src="http://glicksoftware.com/images/photo006.jpg/@@images/b4c9a6c5-93c7-463e-90cf-13cc6b2cbd9b.jpeg" alt="Sprint house" class="image-inline" title="Sprint house" /></p>
<h3>Tasks completed</h3>
<p><b>Ross Patterson</b> wrote a <a class="external-link" href="https://github.com/collective/collective.developermanual/blob/cioppino-buildout-tutorial/source/tutorials/buildout/index.rst">tutorial introducing the basics of zc.buildout</a>. He also started investigating what it would take to let Plone be installed by Microsoft's free <a class="external-link" href="http://www.microsoft.com/web/downloads/platform.aspx">Web Platform Installer</a> for Windows/IIS.<br /><br /><b>Luke Brannon</b> 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.<br /><br /><b>Spanky Kapanka</b> 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 <a class="external-link" href="https://github.com/spanktar/collective.developermanual/commit/cc385909d1ea48ecc71704dfa3e7ac869d3dabb9">completed one on subclassing a content type</a> and started one on creating a custom form.<br /><br /><b>Bill Deegan</b> 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.<br /><br /><b>Mike Cullerton</b> learned git and reStructuredText, then worked on a <a class="external-link" href="http://collective-docs.readthedocs.org/en/latest/tutorials/views.html">"quick start" guide on customizing views and viewlets</a>.<br /><br /><b>Liz Leddy</b> worked on a new set of documentation of <a class="external-link" href="http://readthedocs.org/docs/buildoutcoredev/en/latest/">how to do Plone core development</a>, which will live in the coredev buildout. She also did a lot of cleanup in Plone's trac and moved information<span class="external-link"> to buildout.coredev</span> and to a <a class="external-link" href="http://plone.org/community">new, streamlined "Get Involved" landing page</a> which replaces the front page of trac as an entry point to participating in the Plone community.</p>
<p><b>Steve McMahon</b> 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.<br /><br /><b>Tyler Randles</b> (of ploneconf panda head fame) and I worked on improving the template for the <a class="external-link" href="http://plone.org/documentation/kb">knowledgebase documentation</a>. 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 <a class="external-link" href="http://pypi.python.org/pypi/mr.crabby">mr.crabby</a>, 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.</p>
<p>I, <b>David Glick</b>, 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.<br /><br /><b>Fulvio Casali</b> reviewed and tried out the existing Dexterity documentation and created a detailed list of suggested improvements which will be very helpful.<br /><br /><b>Florian Friesdorf</b> wrote instructions for setting up a Plone development environment using nix, and added support for paged searches to pas.plugins.ldap.<br /><br /><b>Eric Steele</b> 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!<br /><br /><b>Hanno Schlichting</b> 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.</p>
<p><b><img src="http://glicksoftware.com/images/photo020.jpg/@@images/ee97c654-5db3-44b2-ac35-ba72c9599eac.jpeg" style="float: right; " title="Lola" class="image-inline" alt="Lola" />Lola</b> and <b>Zoey</b>, the dogs, sprinted along the beach and made themselves generally lovable.</p>
<h3>Highlights</h3>
<p>A highlight of the sprint for many of us was the food. Mike made <b>homemade pizzas</b> and introduced us to <b>whoopie pies with habanero</b>. Spanky prepared breakfast after wonderful breakfast, including his recently perfected Hollandaise for<b> eggs benedict with crab cakes</b>. For snacks we had Luke's <b>guacamole</b> and Spanky and Tyler's invention of <b>tater tot-bacon-pepperjack cheese bites</b>. I baked <b>sourdough bread</b> to go with Steve's <b>cioppino</b> featuring fresh crab, mussels and clams, and Liz made a great chili that was dubbed "<b>Holy Crap!</b>" Some of these recipes have been posted to the <a class="external-link" href="http://www.coactivate.org/projects/cioppino/lists/cioppino-discussion">sprint mailing list</a> so look for them there.</p>
<p style="text-align: center; "><img src="http://glicksoftware.com/images/photo010.jpg/@@images/e5d613c8-7102-4da3-8fc9-d52d992a18e3.jpeg" alt="Food!" class="image-inline" title="Food!" /></p>
<p>Another highlight for me was participating in my first "<b>code dojo</b>." 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 <a class="external-link" href="https://dev.plone.org/ticket/12796">issue 12796</a>, 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!).</p>
<p style="text-align: center; "><img src="http://glicksoftware.com/images/photo014.jpg/@@images/d214388b-063c-48c7-b688-20cc081fd8c7.jpeg" alt="Beach exploring" class="image-inline" title="Beach exploring" /></p>
<p><br />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.)</p>
<p>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>
<p>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!</p>
<h3>More photos</h3>
<ul>
<li><a class="external-link" href="http://www.flickr.com/photos/bdbaddog/sets/72157629293924250">Bill's</a></li>
<li><a class="external-link" href="http://www.flickr.com/photos/davisagli/sets/72157629313360222/">mine</a></li>
</ul>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2012-03-27T06:45:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/using-tiles-to-provide-more-flexible-plone-layouts">
    <title>Using tiles to provide more flexible Plone layouts</title>
    <link>http://glicksoftware.com/blog/using-tiles-to-provide-more-flexible-plone-layouts</link>
    <description>Tiles provide a powerful way to expand the limits of Plone layout while keeping content manageable.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><i>Update 2/8/2012: Fixed the code sample (some HTML markup had gotten filtered out by my blog editor).</i></p>
<p>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.</p>
<p>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.</p>
<p>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:</p>
<ul>
<li>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.</li>
<li>There should be support for different types of editable areas; each type may have different settings when editing the area.</li>
<li>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, </li>
<li>All this should be done in a way that is simple to reuse for new sites.</li>
</ul>
<h2>Tiles to the rescue</h2>
<p>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.</p>
<p>A tile is a snippet that can be inserted into a template as a div with a data-tile attribute, like this:</p>
<pre class="xml">&lt;div data-tile="/Plone/@@mytile" /&gt;</pre>
<p>Then some machinery in the publisher provided by plone.app.blocks performs the following steps:</p>
<ol>
<li>It finds all the divs with a data-tile attribute (let's call them tile placeholders).</li>
<li>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.</li>
<li>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.</li>
</ol>
<p>That's a great start! As it turns out, other parts of the tiles implementation also help support our use case:</p>
<ul>
<li>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.</li>
<li>plone.app.tiles provides an edit form that takes care of editing the data for a particular instance of a tile.</li>
</ul>
<h2>In practice: adding a rich text tile</h2>
<p>So let's see how this plays out in practice. We are going to:</p>
<ol>
<li>Set up the basic tile rendering machinery.</li>
<li>Implement a <i>rich text tile</i> that can be added anywhere, and that stores its contents in an annotation of the context where it is added.</li>
<li>Make sure that editors see a pencil icon that brings up an <i>modal overlay</i> to edit the tile.</li>
</ol>
<h3>The basics</h3>
<p>Okay, let's get the basics set up.</p>
<ol>
<li>Create a package that declares dependencies on: lxml, plone.app.blocks, plone.app.textfield, plone.app.tiles, and plone.tiles.</li>
<li>At the time of this writing, you need trunk checkouts of plone.app.blocks, plone.app.tiles, and plone.tiles.</li>
<li>Make sure that your configure.zcml includes <code>&lt;includeDependencies package="."/&gt;.</code></li>
<li>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.</li>
<li>Install your package.</li>
</ol>
<h3>The tile</h3>
<p>Add a tile.py with the following:</p>
<pre class="python">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 '&lt;html&gt;&lt;body&gt;%s&lt;/body&gt;&lt;/html&gt;' % text</pre>
<p>In configure.zcml, add:</p>
<pre class="xml">  &lt;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="*"
      /&gt;</pre>
<p>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.</p>
<h3>Wiring in the edit form</h3>
<p>Now we just need to make sure that editors will have a way to access the edit interface for tiles.</p>
<p>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.</p>
<pre class="javascript">jQuery(function($) {
  $('div[data-tile]').each(function() {
      $(this).addClass('tile-editable');
      var href = $(this).attr('data-tile');
      var edithref = href.replace(/@@/, '@@edit-tile/');
      $('&lt;a class="tile-edit-link" href="' + edithref + '"&gt;&lt;img height="16" src="pencil_icon.png" width="16" /&gt;')
        .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) { }
          });
      }
  }
  
});</pre>
<p>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.</p>
<p>And finally we need a bit of CSS to style the tiles and edit links:</p>
<pre class="css">.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;
}</pre>
<h3>Adding a tile</h3>
<p>Okay, now let's add one of these to a template. Pick your favorite template and add:</p>
<pre class="xml">&lt;div tal:attributes="data-tile string:${context/absolute_url}/@@groundwire.tiles.richtext/hello-world" /&gt;</pre>
<p>Here's what it looks like in my instance (I added it to the document_view template):</p>
<p><img src="http://glicksoftware.com/images/tile.png" alt="A tile" class="image-inline" title="A tile" /></p>
<p>And here's the editing interface that shows up when I click on the pencil:</p>
<p><img src="http://glicksoftware.com/images/Screenshot20120204at12.34.23PM.png/@@images/eac56d8d-7e9b-42d2-a38d-c87a4f00198d.png" alt="Tile editing" class="image-inline" title="Tile editing" /></p>
<p>(If you want to see how this code all comes together, look at the code in <a class="external-link" href="https://groundwire.devguard.com/svn/public/groundwire.tiles/branches/davisagli-blocks/">https://groundwire.devguard.com/svn/public/groundwire.tiles/branches/davisagli-blocks</a>)</p>
<h2>In conclusion</h2>
<p>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.</p>
<p>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.</p>
<p>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!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2012-02-04T21:05:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/backporting-a-topic-branch-with-git">
    <title>Backporting a topic branch with git</title>
    <link>http://glicksoftware.com/blog/backporting-a-topic-branch-with-git</link>
    <description></description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>His pull request was against the master branch, so I first merged that using the github UI.</p>
<p>Then I needed to apply the same change to the 1.x branch of plone.dexterity.</p>
<p>In my local copy of the plone.dexterity repository, I added zedr's fork as a remote, and fetched it.</p>
<pre>$ git remote add zedr https://github.com/zedr/plone.dexterity.git<br />$ git fetch zedr<br />From https://github.com/zedr/plone.dexterity<br /> * [new branch]      1.x        -&gt; zedr/1.x<br /> * [new branch]      davisagli-extend-fsschema -&gt; zedr/davisagli-extend-fsschema<br /> * [new branch]      jbaumann-locking -&gt; zedr/jbaumann-locking<br /> * [new branch]      jmehring-drafts -&gt; zedr/jmehring-drafts<br /> * [new branch]      master     -&gt; zedr/master<br /> * [new branch]      toutpt-unicode -&gt; zedr/toutpt-unicode<br /></pre>
<p>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.</p>
<pre>$ git co -b zedr-merge zedr/master<br />Branch zedr-merge set up to track remote branch master from zedr.<br />Switched to a new branch 'zedr-merge'<br /></pre>
<p>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.</p>
<pre>$ git rebase -i --onto 1.x a36f40743d67da8e6d5c7b0aee81e786a2de9f5e</pre>
<p>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.</p>
<p>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.</p>
<pre>pick 8562549 Added Italian translation<br />squash d035596 Correct timestamps<br />squash 9fa9904 Removed template header<br />squash 4a97700 cancel does not really mean that; fixed (thanks gborelli)<br />squash aef40f1 messags now coherent with the standard Italian translations found elsewhere<br />squash 621e185 Updated changelog<br /><br /># Rebase a36f407..621e185 onto 9408d8d<br />#<br /># Commands:<br />#  p, pick = use commit<br />#  r, reword = use commit, but edit the commit message<br />#  e, edit = use commit, but stop for amending<br />#  s, squash = use commit, but meld into previous commit<br />#  f, fixup = like "squash", but discard this commit's log message<br />#  x, exec = run command (the rest of the line) using shell<br />#<br /># If you remove a line here THAT COMMIT WILL BE LOST.<br /># However, if you remove everything, the rebase will be aborted.<br /><br /></pre>
<p>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.</p>
<pre>$ git co 1.x<br />$ git cherry-pick 6dec9429d7994f02e332e22f8009a687ee3944c0</pre>
<p>And now git log shows the change all together nicely in one commit:</p>
<pre>$ git log 1.x<br />commit 6dec9429d7994f02e332e22f8009a687ee3944c0<br />Author: zedr &lt;zedr@zedr.com&gt;<br />Date:   Mon Nov 21 12:29:32 2011 +0100<br /><br /> Added Italian translation<br /> <br /> Correct timestamps<br /> <br /> Removed template header<br /> <br /> cancel does not really mean that; fixed (thanks gborelli)<br /> <br /> messags now coherent with the standard Italian translations found elsewhere<br /> <br /> Updated changelog</pre>
<p>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.</p>
<p>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.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-11-26T19:16:04Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/deep-thoughts-from-plone-irc">
    <title>Deep thoughts from #plone IRC</title>
    <link>http://glicksoftware.com/blog/deep-thoughts-from-plone-irc</link>
    <description></description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<pre class="plain">
kojiro: "end users" is another funny term. I mean, don't we all use our ends? 
kojiro: the end users justify the mean users.
Wyn: kojiro, I completly agree, I have seriously seen people on here who really do not know what a terminal is
kojiro: Wyn: it's the end of something
kojiro: so you can't be an end user unless you have a terminal
</pre>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-05-07T20:50:08Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/the-making-of-zodb.ws">
    <title>The making of zodb.ws</title>
    <link>http://glicksoftware.com/blog/the-making-of-zodb.ws</link>
    <description>An explanation of the pile of hacks we used to get ZODB running in the browser.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>On April 1st Matthew Wilkes and I announced the launch of <a class="external-link" href="http://zodb.ws">ZODB Webscale Edition</a>, which runs the ZODB in a Javascript-based Python interpreter backed by HTML localstorage. It was of course in honor of April Fool's day, as the entire concept of running the ZODB in a browser is a bit silly, but the technology actually works. Here's how we did it.</p>
<h3>The concept</h3>
<p>Matthew first approached me about a month ago with the intent to pull off something epic for April Fool's day this year. His goal, he explained, was to "make something that nutcases would find useful but everyone else knows is stupid." We discussed various ideas such as supporting <a class="external-link" href="http://ymacs.org">ymacs</a> as a richtext editor in Plone before I remembered I had seen a way to run Python in the browser. We quickly ruled out running all of Zope2 in the browser as too big a project, but Matthew suggested doing just the ZODB, and I realized that making it be backed by HTML localstorage could make for a fun, reasonably scoped, buzzword-compliant demo. The idea was born.</p>
<h3>The Emscripten Python interpreter</h3>
<p>The hardest part of the problem—getting a Python interpreter implemented in Javascript—was already basically solved by the <a class="external-link" href="http://code.google.com/p/emscripten/">Emscripten </a>project. <a class="external-link" href="http://syntensity.com/static/python.html">Their Python interpreter</a> was generated by compiling CPython to LLVM bytecode using clang, then using their tools to translate that into Javascript. The result is a 2.8MB closure-compiled "python.js" which includes the logic of CPython as well as implementations of basic C library calls like sprintf and malloc in terms of operations on a heap which consists of a Javascript array. We didn't have time to get the whole Emscripten toolchain set up and working so that we could build a non-packed python.js, but we did need to understand the basics of this Python interpreter, so we used the <a class="external-link" href="http://code.google.com/closure/compiler/">Google Closure Compiler</a> to unpack the whitespace of python.js so it was at least semi-readable.</p>
<p>Unfortunately this Python interpreter had a major limitation—it had no implementation of importing modules, so you were limited to things like "sys" which are included statically in CPython. Obviously this wouldn't work for getting the ZODB working.</p>
<h3>The import system</h3>
<p>I wanted to allow dynamically importing as many things as possible, rather than simply bundling the ZODB code with the interpreter in some fashion. So it seemed like a good approach would be to write a small WSGI import server, and then make the interpreter fetch imports via AJAX in some way. But how, exactly?</p>
<p>I knew that importing in Python calls the __import__ builtin, so I could monkeypatch __builtins__.__import__ to make it somehow fetch the module being imported by name, then manually construct a module using imp.new_module() and exec the fetched code in the new module's namespace. However, this would be Python code running within the sandbox of the Javascript-based interpreter, without the ability to make Javascript calls like fetching via AJAX. So how could we do the actual "fetch the code" step?</p>
<p>We could have used the CPython API (as translated into Javascript) to, from Javascript, create a new "builtin" module with a function for loading a module's code via AJAX. But, neither of us had worked with the Python C API much, let alone with its closure-compiled Javascript variant, and this seemed like too big of a task. So we hit on a simpler hack: we wrote a Javascript function to do the fetching and then "hijacked" the raw_input builtin by replacing the interpreter's reference to it (we picked raw_input because the Emscripten Python interpreter didn't implement it anyway).</p>
<p>The result is a glorious mixture of CPython API (we had to use a bit after all to unpack the argument with the module name and to pack the string with the returned source code) and JQuery:</p>
<pre class="javascript">function raw_input(self, args) { <br />    // stack management<br />    var b = a;
    a += 4;
    for(var d = b;d &lt; a;d++) {
        i[d] = j[d] = 0
    }
    i[b] = 0;
    // unpack argument
    Module._PyArg_UnpackTuple(args, $ba, 0, 1, u([b, 0, 0, 0], 0, o));
    var name = ma(Module._PyString_AsString(Module._PyObject_Str(i[b]))); <br /><br />    // fetch via *synchronous* XMLHTTPRequest
    output('Importing ' + name + '...', 'status');
    var source = '';
    jQuery.ajax({
        url: 'lib/' + name,
        error: function(xhr, status, code) {},
        success: function(result) {
            source = result;
        },
        async: false,
        dataType: 'text',
        cache: true
    });

    // return the source as a pointer into the Python heap
    var h = Module.Pointer_make(Module.intArrayFromString(source))
    a = b;
    return Module._PyString_FromString(h);
}<br />// hijack the raw_input builtin
n[RMb] = Module._builtin_raw_input = raw_input;<p> </p></pre>
<p>The __import__ hook could then be implemented in terms of the new raw_input builtin:</p>
<pre class="python">import sys, imp
_known_bad = set()
def __import__(name, globals={}, locals={}, fromlist=[], level=-1):
    if name in _known_bad:
        raise ImportError('Could not fetch module %s from server.' % name)
    if name in sys.modules:
        return sys.modules[name]

    # call our hook (we hijack raw_input below)
    source = raw_input(name)
    if not source:
        _known_bad.add(name)
        raise ImportError('Could not fetch module %s from server.' % name)

    m = imp.new_module(name)
    m.__file__ = name
    sys.modules[name] = m
    if '.' in name:
        parent, basename = name.rsplit('.', 1)
        if parent in sys.modules:
            setattr(sys.modules[parent], basename, m)
    exec source in m.__dict__
    return m
__builtins__.__import__ = __import__</pre>
<p>This Python is included inline in the HTML, and found and executed during initialization of the interpreter. It is a bit buggy in its handling of packages, but worked well enough to let us move on to the more interesting aspects of the project.</p>
<h3>Making the ZODB work</h3>
<p>So we could import things. "import this" worked great. The ZODB? Not so much. You see, we soon found out that Emscripten's Python interpreter is really quite minimalistic in its builtin modules. "os" is not included, as sandboxed Javascript can't access the local filesystem, so anything like "logging" which depends on it was a problem. Things like "threading", "re", and "time" were similarly missing. Even more problematic was the omission of the following modules which are used in pickling (sort of the core function of the ZODB): cPickle, marshal, and struct.</p>
<p>So we started hacking up our copies of the ZODB and transaction packages. We removed all the logging. We took out the threading locks, with the justification that Javascript is single-threaded anyway. time.time() got replaced with a simple incrementing counter. Et cetera. As for cPickle and its dependencies, we borrowed the pure Python implementations from <a class="external-link" href="http://codespeak.net/pypy/dist/pypy/doc/">PyPy</a>. We also needed Tres Seaver's branch of ZODB to provide a pure Python implementation of the 'persistent' module. It took a couple evenings, but without too much effort we were eventually able to instantiate a DemoStorage, instantiate a DB, connect to it, and commit transactions on the root object. Major win!</p>
<h3>The HTML localstorage backend</h3>
<p>But it still wasn't a great demo. We wanted it to be possible to commit a transaction, then come back after leaving the page and be able to access the data that had been committed. And we wanted the persistence to happen in <a class="external-link" href="http://diveintohtml5.org/storage.html">browser localstorage</a> on the client side, rather than by passing values to the server. So we needed to find a way to modify or replace DemoStorage to pass its values to Javascript to be placed in localstorage, and to retrieve them again when the page is loaded.</p>
<p>After the CPython API hackery needed to get the imports working, I was a bit scared about doing a lot of passing values from Python to Javascript and back. So at this point I thought, "Wait. We have the entire Python interpreter runtime state in these Javascript arrays; why don't I just save and restore the whole interpreter?" Ultimately this led me down a rabbit hole to nowhere. I never quite figured out the correct bootstrapping process to get all the necessary Javascript variables re-initialized on subsequent loads, but with the Python heap, stack, etc replaced with the old state. And I was bumping up against the 5MB limit for what can be placed in localstorage.</p>
<p>Fortunately Matthew came along at this point with a different approach. He wrote a very simple ZODB storage class, the HTML5Storage (<a class="external-link" href="http://zodb.ws/lib/zodbinit">code</a>), which stores pickles of modified objects and writes them to a (Python) global dict, keyed by object id, when a transaction is committed. And instead of messing around with the CPython API to interface Python with Javascript, he simply made a commit print out the JSON representation of that global dict, with a special identifier that the Javascript implementation of print() was modified to watch for and handle specially by parsing the JSON and stuffing it in localstorage. When the page is loaded, the stored values are passed back to Python by converting the localstorage contents to JSON, executing it as Python, and placing the values back in the Python global dict. (There is a bit of extra hackery to encode backslashes in the Python repr of the pickles, handled by the very Britishly named dodgy_encode function.)</p>
<p>At this point, it should have worked. But there was one more hurdle.</p>
<h3>Debugging the pickles</h3>
<p>When we tried to reload the root object from the HTML5Storage, we were getting unexpected errors. I compared the pickles that had been generated in the Emscripten interpreter with those generated for a similar object on a real Python interpreter, and noticed that they were not the same. I used the pickletools.dis() function from the stdlib to examine the pickle bytecode, and figured out that the size of some strings in the pickle was getting recorded incorrectly, so the pickles were not being executed correctly during unpickling. Specifically, the size of some strings was getting recorded as \x02 regardless of the actual length of the string.</p>
<p>I tracked the bug down to the repr() of the pickle that Matthew was doing in his dodgy_encode function. A variety of different bytes were all getting repr'd as \x02. And then I tracked this into the PyString_Repr implementation of the Javascriptified Python interpreter, to where it calls sprintf with a format of "\\x%02x". It turns out that Emscripten's implementation of sprintf was incomplete and supported neither the "02" used immediately after % to give a zero-padding width, nor the "x" used to specify printing a hex value.  It was interpreting the "02" literally, and the x was getting truncated off. Once I figured out where this issue was arising, it was a relatively simple matter to adjust the sprintf implementation to handle this format correctly.</p>
<p>And there was pickling, and there was unpickling: the first day. And Matthew and David looked on the hackery and saw that it was good.</p>
<h3>The launch</h3>
<p>Fortunately we still had a day left to put a bit of polish on the thing before April 1. <a class="external-link" href="http://rynamic.com">Ryan Foster</a> was kind enough to whip up a nice web-2.0-product-style design on short notice. (Kudos to Ryan; I basically said "we want something like evernote.com" and he magically figured out exactly the sort of color scheme and logo I had in mind.) We added a bit of varnish in front and added some social media bling to the footer. In a flash of inspiration, I dubbed the site a project of "POSKey Enterprises," a reference to the POSKeyError one gets when trying to load an object from the ZODB that does not exist. We revamped the input/output to be much nicer and closer to a real Python interpreter.</p>
<p><a class="external-link" href="http://zodb.ws">zodb.ws</a> launched to much fanfare on the morning of April 1. Like, I mean, we got literally dozens of pageviews. (Okay, actually a few hundred. And we realize the thing is a bit esoteric.) :) Some people assumed the thing was just a light frontend to a server-side interpreter, so people were more impressed once we explained that everything was actually executing client-side. Thanks to everyone else who retweeted the link for helping spread the word a bit beyond the tiny Zope circles!</p>
<p>Ultimately, I think the project satisfied our goals; after all, hacking is about the journey, not the destination.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-04-04T04:55:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/inspect-your-zodb-with-eye">
    <title>Inspect your ZODB with Eye</title>
    <link>http://glicksoftware.com/blog/inspect-your-zodb-with-eye</link>
    <description>Eye is a utility for browsing the contents of a ZODB.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>A fairly common complaint about the ZODB is that there's no generic tool for browsing its contents. In fact this is a bit of a lie, as there are at least 3 existing tools called "zodbbrowser," but they all depend on large parts of the Zope stack, and are therefore a bit hard to install.  So at the PyCon sprints I worked on adapting Roberto Allende's zope2.zodbbrowser into a Pyramid-based tool called Eye.</p>
<p>The result is easy to install and looks like it will be fairly useful for seeing all the objects present in a ZODB (not just the ones that the ZMI or some other app-level tool chooses to show). As an added bonus, it knows how to browse "broken" objects, so you don't have to have your application code in Eye's PYTHONPATH.</p>
<p><img src="https://github.com/davisagli/eye/raw/master/screenshot.png" /></p>
<p>(Blue items are persistent objects; black ones are included in the ZODB only by virtue of being referenced by persistent objects, and do not get their own pickle.)</p>
<p>Eye can also be used to take a peek at any old set of Python objects that are not in a ZODB.</p>
<p>See the <a class="external-link" href="http://pypi.python.org/pypi/eye">PyPI page for installation and usage instructions</a>, or <a class="external-link" href="http://github.com/davisagli/eye">clone the project on github</a> and send me pull requests. :)</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2011-03-21T17:50:14Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/registering-add-on-specific-components-using-z3c.baseregistry">
    <title>Registering Add-on-specific components using z3c.baseregistry</title>
    <link>http://glicksoftware.com/blog/registering-add-on-specific-components-using-z3c.baseregistry</link>
    <description>z3c.baseregistry provides a safer way to register Zope components that should only be available within sites that have a particular add-on installed.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p><span class="discreet">(Updated 10/17/2010 to reflect some minor corrections from Martin.)</span></p>
<p>At Groundwire it is very common for us to run a number of Plone sites within the same Zope instance. This introduces some unique requirements for add-on products to follow when they are registering Zope components:</p>
<ol>
<li>If the add-on <i>registers some new component</i>, it should do it in such a way that it is only available within sites that have the add-on installed.</li>
<li>If the add-on<i> overrides some default component</i> from Zope or Plone, it should do it in such a way that it can be further overridden for one particular site.</li>
</ol>
<p>There are several common approaches that can help with these requirements, but each has downsides. Overriding components using overrides.zcml is global (i.e., affects all Plone sites in an instance) and prevents further customization. Registering components for a browser layer only works for components that adapt the request (such as browser views). Registering persistent local utilities or adapters in a site's local component registry keeps things isolated, but can be a headache when it's time to uninstall the add-on or remove the implementation of a component.</p>
<p>There is a lesser-known fourth option: using <a class="external-link" href="http://pypi.python.org/pypi/z3c.baseregistry">z3c.baseregistry</a> to create a registry specific to one add-on.</p>
<h2>Component registries in Zope 2</h2>
<p>In Zope 2 we typically deal with 2 types of <i>component registries</i> (also called <i>site managers</i> historically):</p>
<ol>
<li>The global registry, which is populated with components at startup by processing ZCML.</li>
<li>A local registry associated with each Plone site (implemented in <a class="external-link" href="http://pypi.python.org/pypi/five.localsitemanager">five.localsitemanager</a>). These store components persistently in the ZODB, and can be populated via the <code>zope.component.interfaces.IComponentRegistration</code> API in Python, or via GenericSetup (componentregistry.xml)</li>
</ol>
<p>When Zope traverses over a Plone site, its local registry is set as the active registry (via <code>zope.site.hooks.setSite</code>—or <code>zope.app.component.hooks.setSite</code> in older Zopes—which sets a thread local). After that, this registry is the one that can be obtained by <code>zope.component.getSiteManager</code>, and the one that will be implicitly used by the functions that do component lookups. If a component is looked up but not found in the local registry, it will fall back to checking in that registry's base registries. By default in Plone, there is just one base registry, which is the global registry.</p>
<h2>Introducing z3c.baseregistry</h2>
<p>However, there's no requirement that the global registry be the only base registry. z3c.baseregistry makes it possible to define additional, named registries which can be installed as additional base registries for a particular site.  Then when a component lookup occurs, it will be looked for first in the local registry, then in the custom base registry, and then in the global registry.</p>
<p>The cool thing is that while the installation of a z3c.baseregistry is persistent, the components one contains are not. Instead, the components are populated at Zope startup via ZCML, very much like the global registry.  The <code>registerIn</code> grouping directive lets us specify which registry components should be registered in:</p>
<pre>&lt;registerIn registry=".packageComponents"&gt;<br />  &lt;!-- component directives here --&gt;<br />&lt;/registerIn&gt;</pre>
<p>This means that when you uninstall an add-on that has its own base registry, you just need to remove the registry from the site manager's bases, rather than figuring out how to unregister each individual component as would be necessary for persistent components in the local registry.  It also means that you can safely remove a component's class when you remove its registration without worrying about breaking legacy persistent registrations of that component.</p>
<h2>Step by step</h2>
<p>I've used this approach in a few projects lately. Here's what it looks like:</p>
<ol>
<li>Add z3c.baseregistry to the add-on's install_requires in setup.py, and re-run buildout to make sure it is installed.</li>
<li>
<p>Create a new registry instance.</p>
<p>In __init__.py (or could be elsewhere):</p>
<pre>from zope.component import getGlobalSiteManager<br />from z3c.baseregistry.baseregistry import BaseComponents<br />packageComponents = BaseComponents(getGlobalSiteManager(), 'foo.bar')</pre>
<p>Here, we made sure that the new registry has the global registry as its base, and is named after our add-on package (foo.bar).</p>
</li>
<li>
<p>Register a local utility for looking up the new registry by name (this is used by z3c.baseregistry internally).</p>
<p>In configure.zcml:</p>
<pre>&lt;!-- registry for package-specific components --&gt;<br />&lt;utility<br /> component=".packageComponents"<br /> provides="zope.component.interfaces.IComponents"<br /> name="foo.bar"<br /> /&gt;</pre>
</li>
<li>
<p>Install the new registry in the bases for a particular site. z3c.baseregistry includes a form for doing this through the web, but it doesn't seem to work in Zope 2. Oh well, we can do it with a GenericSetup import handler instead.</p>
<p>In setuphandlers.py:</p>
<pre>from zope.component import getSiteManager<br />from zope.component.interfaces import IComponents<br /><br />def install_base_registry(site):<br />    sm = getSiteManager(context=site)<br />    reg = sm.getUtility(IComponents, name=u'foo.bar')<br />    sm.__bases__ = tuple([reg] + [r for r in sm.__bases__ if r is not reg])</pre>
<p>You would then call this from the add-on's custom "import various" GenericSetup handler.</p>
</li>
<li>
<p>Now components can be registered for the new add-on specific registry, using the registerIn grouping directive.</p>
<p>In configure.zcml:</p>
<pre>&lt;!-- make sure we can use registerIn --&gt;<br />&lt;include package="z3c.baseregistry" file="meta.zcml"/&gt;<br /><br />&lt;registerIn registry=".packageComponents"&gt;<br /><br />  &lt;browser:page<br />    for="*"<br />    name="foobar"<br />    template="foobar.pt"<br />    permission="zope.Public"<br />    /&gt;<br /><br />&lt;/registerIn&gt;</pre>
<p>These components will be found within sites that have the product installed, but not within sites that don't!</p>
</li>
</ol>
<h2>Caveats</h2>
<ul>
<li>It should be obvious, but this only localizes the effects of ZCML directives whose effect is to register something in the component registry (e.g. utility, adapter, subscriber, browser:page). Directives that mutate other things, such as <code>&lt;class&gt;</code> which directly modifies a class, will still have a global effect.</li>
<li>Don't forget to make sure that the base registry gets removed from the local registry's bases when the add-on is uninstalled. Otherwise removing the product will break the site when it tries to unpickle the base registry.</li>
</ul>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-10-16T22:45:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/plone-4-in-the-news">
    <title>Plone 4 in the news</title>
    <link>http://glicksoftware.com/blog/plone-4-in-the-news</link>
    <description>Plone 4 made a cameo appearance on NBC Nightly News coverage of Google's new instant search feature.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>In case you missed it, last night Plone 4 made an appearance on NBC Nightly News!  Its inclusion was happenstance—it came during a demonstration of Google's new instant search feature, with a search for "how much faster is it."  The first result was Hanno's <a class="external-link" href="http://blog.hannosch.eu/2010/01/plone-4-how-much-faster-is-it.html">blog entry</a> discussing performance improvements in Plone 4!</p>
<p><img src="http://glicksoftware.com/images/plone_nbc.png" alt="Plone on NBC Nightly News" class="image-inline" title="Plone on NBC Nightly News" /></p>
<p>(Thanks to Darci for the screen capture.)</p>
<p>You can still see <a class="external-link" href="http://www.google.com/search?q=how+much+faster+is+it">the results</a> yourself, although Plone is now second after a blog post about Google's instant search.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-09-09T15:44:40Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/ten-lesser-known-improvements-in-plone-4">
    <title>Ten lesser-known improvements in Plone 4</title>
    <link>http://glicksoftware.com/blog/ten-lesser-known-improvements-in-plone-4</link>
    <description>The just-released Plone 4 includes rather a lot of big enhancements. Here are some of my favorite little ones.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>As you've probably heard by now, <a class="external-link" href="http://plone.org/4">Plone 4.0 final</a> was released this week! (The cynical among you are probably muttering that we <a class="external-link" href="http://rakudo.org/announce/rakudo-star/2010.08">couldn't even beat Perl 6</a>, but hey, it's here now :) ). Working on Plone 4 has been a huge part of my life for the past year, but I did far from all of the work. Thanks to every one of you who contributed; I couldn't be prouder of what we've created together!</p>
<p>See the link above for info on major features in the release, but there are a lot more little things included too. Without further ado, here are ten of my favorite Plone 4 improvements that nobody's talking about...</p>
<h3>General / site admin features</h3>
<p>1. <b>New editor features.</b> Check the control panel for TinyMCE (our new WYSIWYG editor) to find some useful buttons that aren't enabled by default:</p>
<ul>
<li>Paste as Plain Text (preserves newlines)</li>
<li>Paste from Word (strips junk markup)</li>
<li>Insert/edit Media</li>
</ul>
<p>(among others)</p>
<p>2. <b>Better date/time handling.</b> The display of event times in listings is now smarter. If the event begins and ends on different days, the time is not shown. If the start and end times are the same, it is not shown twice.</p>
<p>In addition, dates and times are now stored with a timezone, with proper consideration of daylight savings time.</p>
<p>3. <b>Inline error tracebacks.</b> If you're logged in as a Manager and encounter an error, the traceback is shown immediately, rather than requiring you to click through to the error_log in the ZMI.</p>
<p>4. <b>Permission auditing.</b> Thanks to a new feature in Zope 2.12, it's now easy to double-check what permissions a particular user will have in a particular context, via this button on the Security tab in the ZMI:</p>
<p style="text-align: center; "><img src="http://glicksoftware.com/images/Screenshot20100902at11.56.40PM.png" alt="Permission auditing" class="image-inline" title="Permission auditing" /></p>
<h3 style="text-align: left; ">For integrators</h3>
<p style="text-align: left; ">5. <b>Better control over portlet columns</b>. In Plone 3, it was possible to make a template prevent rendering of the portlet columns by filling METAL slots. In Plone 4, this can be done by setting the <i>disable_plone.leftcolumn</i> and <i>disable_plone.rightcolumn</i> flags on the request before calling main_template. For example:</p>
<pre class="xml">&lt;tal:block tal:define="foo python:request.set('disable_plone.leftcolumn', 1)"/&gt;<br />&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"<br /> xmlns:tal="http://xml.zope.org/namespaces/tal"<br /> xmlns:metal="http://xml.zope.org/namespaces/metal"<br /> xmlns:i18n="http://xml.zope.org/namespaces/i18n"<br /> lang="en"<br /> metal:use-macro="context/main_template/macros/master"<br /> i18n:domain="plone"&gt;<br /><br />(snip)<br />&lt;/html&gt;<br /></pre>
<p>(This might also be done in Python in the __init__ method of a browser view.) Handily, it's actually a 3-way flag: set it to True to force hiding the colum, False to force displaying it even if there are no portlets assigned, and None to use the default logic based on whether there are portlets assigned.</p>
<p>6. <b>"All content" listing view and content-core macro.</b> There is a new folder listing display option called "All content", which renders the full content of each item. (See it in action on the homepage of this website.)</p>
<p>This listing takes advantage of the new <i>content-core</i> slot which is defined in main_template following the title and description and between the various above/below viewlet managers, and filled by each content type's default view. This gives each content type control over how it renders in the "all content" listing. (The "all content" listing also takes advantage of some new slots/macros in the folder_listing template to reuse its logic for batching and fetching item properties; this may be a useful model for other custom folder listings.)</p>
<p>It also makes it simpler to write a view template for a content type, since one no longer needs to include the title, description, and common viewlet managers as boilerplate. The simplest view template is now (this is a copy of Plone 4's document_view):</p>
<pre>&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"<br /> xmlns:tal="http://xml.zope.org/namespaces/tal"<br /> xmlns:metal="http://xml.zope.org/namespaces/metal"<br /> xmlns:i18n="http://xml.zope.org/namespaces/i18n"<br /> lang="en" metal:use-macro="context/main_template/macros/master"<br /> i18n:domain="plone"&gt;<br /> &lt;body&gt;<br />  &lt;metal:content-core fill-slot="content-core"&gt;<br />   &lt;metal:content-core define-macro="content-core"&gt;<br />    &lt;metal:field use-macro="python:context.widget('text', mode='view')"&gt;<br />     Body text<br />    &lt;/metal:field&gt;<br />   &lt;/metal:content-core&gt;<br />  &lt;/metal:content-core&gt;<br /> &lt;/body&gt;<br />&lt;/html&gt;</pre>
<p>For backwards compatibility, templates may still continue to fill the <i>main</i> slot instead of <i>content-core</i>, and provide their own title, description, and viewlet managers.</p>
<p>7. <b>Flexible image scaling.</b> There is now an <i>Image Handling</i> control panel which allows configuring the standard image scale sizes used by Plone.</p>
<p>In addition, since image scales are now generated on demand, it's quite easy for a template to request an image scale of a non-standard size, using this idiom:</p>
<pre>&lt;img tal:define="scale context/@@images"<br />     tal:replace="structure python: scale.scale('image', width=42, height=42).tag()" /&gt;</pre>
<p>Including a standard image scale is simpler too:</p>
<pre>&lt;img tal:replace="structure context/@@images/image/mini" /&gt;<br /></pre>
<p>See <a class="external-link" href="http://pypi.python.org/pypi/plone.app.imaging">plone.app.imaging</a> for additional options.</p>
<h3>For developers</h3>
<p>8. <b>Automatic registering of Zope 2 permissions.</b> In Zope 2.10, registering a new permission took two steps (using the &lt;permission/&gt; ZCML directive to register a Zope 3 permission utility, and registering it as a Zope 2 permission, usually by calling Products.CMFCore.permissions.setDefaultRoles during product initialization).</p>
<p>In Zope 2.12, only the first step is required.  The equivalent of setDefaultRoles is automatically called after processing the <i>permission</i> directive, and sets the default roles to ('Manager',).</p>
<p>If you want different default roles, you must still call setDefaultRoles yourself, prior to loading of ZCML (e.g., at import time). This will improve in Plone 4.1 / Zope 2.13, where the <i>permission</i> directive gets an optional <i>role</i> subdirective to specify the desired default roles in ZCML.</p>
<p>9. <b>Fixed ordering of CMF/Archetypes interaction during object creation.</b> In Plone 3, when you created a new content item, CMF called the factory method of an Archetypes content type, then called its ObjectCreated event, <i>then</i> updated the item's <i>portal_type</i> attribute. This had the unfortunate effect that if you were trying to create a schema extender that used the portal_type to determine whether the extender should be applied (perhaps based on some configurable property), and your site used content types that shared the class of other content types, you were out of luck.</p>
<p>This is now fixed so that ObjectCreated is not fired until the portal_type is set. I think we also managed to avoid calling some event twice during object creation that was causing duplicate catalog indexing, but I could be misremembering that.</p>
<p>10. <b>Folder ordering adapters.</b> The order of items in a folder is now looked up via a named adapter of the folder to plone.folder.interfaces.IOrdering—and it is looked up dynamically when ordering catalog results by getObjPositionInParent, rather than consulting an index. This means that it should now be simpler to customize how a particular folder is ordered (e.g. to automatically sort alphabetically or by date), by writing a new adapter that provides IOrdering and setting a folder to use it. To set the name of the IOrdering adapter that is used for a particular folder, use its setOrdering method.</p>
<p>Out of the box, most folders use the DefaultOrdering adapter, which allows user-configurable order. This should be appropriate in many cases. Folders that were migrated from Plone 3's Large Plone Folders use the "unordered" ordering (but this shouldn't be needed for new folders that will store many items, as the default ordering is reasonably performant). plone.folder also has a "partial" ordering which allows specifying the order for some but not all subitems, but this is not directly used or supported in the Plone UI at this time.</p>
<hr />
<p> </p>
<p>On to 4.1 and beyond!</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-09-03T16:50:19Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/plone.app.themeeditor-1.0a1-...-and-a-roundup-of-other-recent-releases">
    <title>plone.app.themeeditor 1.0a1 ... and a roundup of other recent releases</title>
    <link>http://glicksoftware.com/blog/plone.app.themeeditor-1.0a1-...-and-a-roundup-of-other-recent-releases</link>
    <description>The Groundwire team and I have been busy bringing you updates to your favorite Plone add-ons.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Sometimes it's easy to get carried away fixing bugs and making new releases without remembering to announce them.  So here's a blog post to catch up on what the <a class="external-link" href="http://groundwire.org">Groundwire</a> web team and I have been up to in Plone add-on land the past few months.<br /><br />Most noteworthily, David Bain has been working on plone.app.themeeditor, an improved UI for customizing Plone theme resources, as his Google Summer of Code (GSOC) project. This week we were finally ready to release the <a class="external-link" href="http://pypi.python.org/pypi/plone.app.themeeditor/1.0a1">first alpha</a>. Please try it out and give us feedback so that we can continue to make it better!  (Note the special installation instructions if you're on Plone 3.)  GSOC is almost over, but David is still at work trying to add support for automatically exporting customized items to a filesystem theme product.  If you missed the blog post where I introduced the theme editor, you can go <a href="http://glicksoftware.com/blog/introducing-the-plone-resource-customizer" class="internal-link">watch the demo screencast</a>.<br /><br />And a bunch more...<br /><br /><a class="external-link" href="http://plone.org/products/pfg.donationform">pfg.donationform</a> 1.0 - This product simplifies the process of setting up a PloneFormGen-based donation form that processes payments via PloneGetPaid. It provides a custom add form that creates a PloneFormGen form with fields for contact and billing info, and an adapter to fill a cart and initiate checkout. It includes a custom "donation field" and widget for use with PloneFormGen, which allows choosing from a list of predefined named donation levels or choosing an arbitrary amount to donate. It also supports an option to make a recurring donation if you're using a GetPaid payment processor that supports that (currently the Authorize.net and PayPal processors).<br /><br /><a class="external-link" href="http://plone.org/products/pressroom">Products.PressRoom</a> 3.8 - We've been fixing various issues with Plone 4 compatibility, and Matt Yoder just made a change so that the various listings (press releases, press clippings, etc.) are now based on Collections, for improved customizability.<br /><br /><a class="external-link" href="http://plone.org/products/salesforcebaseconnector">Products.salesforcebaseconnector</a> 1.3 - Query results can now be accessed from RestrictedPython in templates, and there is a new validateCredentials method to aid in remotely monitoring to confirm that the Salesforce connection is working.<br /><br /><a class="external-link" href="http://plone.org/products/salesforcepfgadapter">Products.salesforcepfgadapter</a> 1.6.2 - The new release includes a bugfix to support the case when you're using the adapter in update mode and the user is trying to clear a non-required field. (thanks to Matt Yoder)<br /><br /><a class="external-link" href="http://plone.org/products/megaphone">collective.megaphone</a> 1.4 - This new release of Groundwire's online advocacy campaign tool contains some fixes for Plone 4 compatibility, as well as a rewrite of the drag-and-drop UI for reordering fields to avoid a dependency on collective.jqueryui.  Look for a bigger Megaphone release including a new Petition feature soon.<br /><br /><a class="external-link" href="http://pypi.python.org/pypi/collective.salesforce.authplugin">collective.salesforce.authplugin</a> 1.4 - Contains a couple bugfixes, plus some improvements to make it easier to use multiple auth plugins in the same Plone site (i.e. if you need to authenticate against multiple types of Salesforce objects).<br /><br /><a class="external-link" href="http://plone.org/products/simplesocial">collective.simplesocial</a> 1.3 - Adds a couple features including a collective.googleanalytics plugin to track clickthroughs when prompting people to post to their Facebook feed, and an option to only show the latter after an edit. (thanks to Matt Yoder)<br /><br /><a class="external-link" href="http://plone.org/products/collective.z3cform.wizard">collective.z3cform.wizard</a> 1.3.2 - Avoids some spurious inline editing errors.<br /><br /><a class="external-link" href="http://plone.org/products/plone.app.jquerytools">plone.app.jquerytools</a> 1.1.2 - This is mostly Steve McMahon's brainchild, but in the most recent release I added a stylesheet for Plone 3 based on the overlay styles from Plone 4's Sunburst theme, so that add-ons using jquerytools don't have to provide styles for the popup overlays.<br /><br /><a class="external-link" href="http://pypi.python.org/pypi/Products.PloneFormGen">Products.PloneFormGen</a> 1.6.0b4 - We shifted to a different approach for getting keys for storing items in the saved data adapter, which should help avoid conflicts in heavy-write scenarios.<br /><br />Finally, as I write this, Martin Aspeli is making a new beta release of the <a class="external-link" href="http://plone.org/products/dexterity">Dexterity</a> content type system, incorporating numerous bugfixes.<br /><br /></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-08-05T05:55:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/in-browser-integration-testing-with-windmill">
    <title>In-browser integration testing with Windmill</title>
    <link>http://glicksoftware.com/blog/in-browser-integration-testing-with-windmill</link>
    <description>Windmill makes it easy to run automated tests of a Plone project in a real browser.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>I really like writing integration tests for web projects using the <a class="external-link" href="http://pypi.python.org/pypi/zope.testbrowser">Zope testbrowser</a>, which is a convenience API around the mechanize library. But its Achilles heel has always of course been that it only operates on the HTML response, so can't test interactive functionality built with Javascript and AJAX. So I've wanted to try one of the options for running tests in a real browser for a while. Actually it's a testament to the utility of the Zope testbrowser (or my own laziness?) that I made it this long. But the <a href="http://glicksoftware.com/blog/introducing-the-plone-resource-customizer" class="internal-link">Plone resource customizer</a> uses a lot of AJAX, so it was time.<br /><br />So first I tried <a class="external-link" href="http://seleniumhq.org">Selenium</a>. I'd heard about Selenium from a number of people, and it's cool. There is a Firefox plugin that lets you record actions and assertions and play them back. It also lets you export these tests to Python to be run through the selenium Python bindings in conjunction with Selenium RC. There's even a Selenium Grid for running tests in parallel on multiple machines.<br /><br />Unfortunately, while I found <a class="external-link" href="http://pypi.python.org/pypi/collective.ploneseltest">collective.ploneseltest</a> which looked like just what I needed—it provides a base Selenium test case for use with the Zope testrunner—I had trouble getting it to actually work. Using version 1.0.3 of Selenium RC and of the Python bindings, Selenium RC was sending an extra HEAD request before each GET request, which was getting interpreted incorrectly by the ZPublisher during traversal in Zope.  A query to Twitter yielded the information (from the <a class="external-link" href="http://twitter.com/hugs">creator of selenium</a> himself) that new Python bindings for selenium 2.0 are available as of last week, but I found they don't work (at least not yet) with Python 2.4. And anyway, by that point Martin Aspeli had also replied to my query and suggested trying Windmill instead.<br /><br /><a class="external-link" href="http://getwindmill.com">Windmill</a> is another web testing framework that actually seems to have quite similar functionality to Selenium, at least from a cursory examination. It also has an in-browser controller for recording, and can export tests to Python code. There is a Zope testrunner integration for Windmill too, in the <a class="external-link" href="http://pypi.python.org/pypi/niteoweb.windmill">niteoweb.windmill</a> package.</p>
<p>And Windmill was quite a bit easier to get working. I just added the following to my package's setup.py to define a new installation "extra":</p>
<pre>extras_require = {<br />    'test': ['niteoweb.windmill',],<br />},</pre>
<p>And then modified my buildout's test runner to include that extra:</p>
<pre>[test]<br />recipe = zc.recipe.testrunner<br />eggs =<br />    ${instance:eggs}<br />    plone.app.skineditor [test]<br />defaults = ['--exit-with-status', '--auto-color', '--auto-progress']</pre>
<p>I added a new test module called test_integration.py and made it use the base test case from niteoweb.windmill:</p>
<pre>import unittest<br />from niteoweb.windmill import WindmillTestCase<br />from Products.PloneTestCase.setup import setupPloneSite<br />from Products.PloneTestCase.layer import onsetup<br />from Products.Five.zcml import load_config<br />from Testing import ZopeTestCase as ztc<br /><br />@onsetup<br />def load_zcml():<br />    import plone.app.skineditor<br />    load_config('configure.zcml', plone.app.skineditor)<br />    ztc.installPackage('plone.app.skineditor')<br /><br />load_zcml()<br />setupPloneSite(products=['plone.app.skineditor'])<br /><br />class SkinEditorIntegrationTestCase(WindmillTestCase):<br /><br />    def afterSetUp(self):<br />        """Setup for each test<br />        """<br />        ztc.utils.setupCoreSessions(self.app)<br />        self.setRoles(['Manager'])<br />        self.login_user()<br /><br />    def test_customize_logo(self):<br />        import pdb; pdb.set_trace()<br /><br />def test_suite():<br />    return unittest.defaultTestLoader.loadTestsFromName(__name__)</pre>
<p>(Most of this is standard test setup boilerplate. I could probably be a bit more sophisticated about the test setup and use layers or something, but this is the tried and true test setup I've been using since Martin published it in his book. The load_zcml method and setupPloneSite method are deferred and run when the Plone test layer is set up by the test runner.)<br /><br />So far I just added one test that enters pdb. At this point, I can run the test with bin/test -s plone.app.skineditor, and Windmill will start up the ZServer with a dummy Plone site, fire up a browser and a controller window, and then pause at the pdb. (Unlike Selenium, I don't have to install a browser plugin or have another process running first. Nice!) Then I can play around with the controller and start recording tests.  The afterSetUp method runs before each test, and calls niteoweb.windmill's "login_user" helper, which does an initial login to the site as a Manager user.  It also tells the test infrastructure to support sessions, which are needed for my app.<br /><br />Actually recording a test is mostly a point-and-click affair—hit the "record" button and go at it—but with a few caveats that I'll note below. It took a little bit for me to get used to the ways of selecting parts of the page and making assertions, but at least Windmill provides some flexibility. You can select by id, via XPath expressions, via JQuery selectors, or several other methods.  And assertions range from asserting that certain text is on the page to asserting that an arbitrary Javascript expression evaluates to true.<br /><br />Here's the full test case I ended up with (dumped from the controller using the "save" button):</p>
<pre>def test_customize_logo(self):<br />    client = self.wm<br />    client.click(id=u'user-name')<br />    # load customizer<br />    client.click(link=u'Site Setup')<br />    client.waits.forPageLoad(timeout=u'20000')<br />    client.click(link=u'Theme Editor')<br />    # go into advanced mode and customize based on the logo in the<br />    # non-active "plone_images" layer (Windmill doesn't do file uploads)<br />    client.click(link=u'Advanced')<br />    client.click(id=u'plone-app-skineditor-name-field')<br />    client.type(text=u'logo', id=u'plone-app-skineditor-name-field')<br />    client.click(id=u'plone-app-skineditor-filter-button')<br />    client.waits.forElement(timeout=u'', id=u'plone-app-skineditor-browser')<br />    client.click(xpath=u"//a[@id='skineditor-logo.png']/dt")<br />    client.waits.forElement(xpath=u"//dd[@class='plone-app-skineditor-layers']")<br />    client.click(jquery=u"('a[href*=plone_images/logo.png/manage_main]')[0]")<br />    client.waits.forElement(jquery=u"('#pb_1 input[value=Customize]')")<br />    client.click(name=u'submit')<br />    client.waits.forElement(timeout=u'', id=u'pb_2')<br />    # now reload and make sure the logo has the height we expect from the<br />    # customized image<br />    client.refresh()<br />    client.asserts.assertJS(js=u"$('#portal-logo').height() == 57")<br />    # now remove the customization<br />    client.click(id=u'plone-app-skineditor-name-field')<br />    client.type(text=u'logo', id=u'plone-app-skineditor-name-field')<br />    client.click(id=u'plone-app-skineditor-filter-button')<br />    client.waits.forElement(timeout=u'', id=u'skineditor-logo.png')<br />    client.click(xpath=u"//a[@id='skineditor-logo.png']/dt")<br />    client.waits.forElement(xpath=u"//dd[@class='plone-app-skineditor-layers']")<br />    client.click(link=u'Remove')<br />    # and confirm we're back to the original height<br />    client.refresh()<br />    client.asserts.assertJS(js=u"$('#portal-logo').height() == 56")</pre>
<p>And here's what it looks like to run that. (No clicking on my part involved!) The excitement starts about 0:17...</p>
<p>
<object data="http://wglick.org/jingswfplayer.swf" height="349" id="scPlayer" type="application/x-shockwave-flash" width="600">
<param name="data" value="http://wglick.org/jingswfplayer.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#FFFFFF" />
<param name="flashVars" value="containerwidth=600&amp;containerheight=349&amp;content=http://wglick.org/windmill.swf" />
<param name="allowFullScreen" value="true" />
<param name="scale" value="showall" />
<param name="allowScriptAccess" value="always" />
<param name="base" value="http://wglick.org/" />
<param name="src" value="http://wglick.org/jingswfplayer.swf" />
<param name="flashvars" value="containerwidth=600&amp;containerheight=349&amp;content=http://wglick.org/windmill.swf" />
<param name="allowfullscreen" value="true" />
</object>
</p>
<p><br />Finally, here are a few caveats I ran into while setting up my first test, to customize the logo using plone.app.skineditor, and some last observations.<br /><br /></p>
<ul>
<li>Windmill can't do file uploads. This is a limitation of browser Javascript support / sandboxing, not of Windmill per se.  It would be nice if there were some command that would prime the Windmill HTTP proxy to add a particular file to the next HTTP request that comes through, so that uploads could at least be faked.</li>
<li>Windmill could be a bit smarter about handling AJAX requests when recording.  I needed to manually add a "waitForElement" step after each click that resulted in an AJAX load, to make sure that the load was complete before subsequent steps run.  It would be nice if there was a "wait for all ajax to complete" command so that I didn't have to identify a particular element to wait for.</li>
<li>I wish that there was a wrapper that would let me control Windmill using the same API as zope.testbrowser, to make it easier to convert existing tests to full browser tests.</li>
<li>So far I've only tried running tests in my default browser (Firefox), but it's possible to run in other browsers as well.  The niteoweb.windmill page on PyPI gives an example of a test layer for doing this.</li>
<li>It remains to be seen how cumbersome this sort of test will be to keep up-to-date as the product evolves.</li>
</ul>
<p>Overall Windmill provides a decent experience for recording tests and a really nice one for running them. Perhaps it is a tool we can use to do real browser testing for Plone core?<br /><br />If you've used Windmill and have any insights into how to use it effectively, I'd love to hear your thoughts in the comments.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-06-10T06:16:24Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/review-of-plone-3-products-development-cookbook">
    <title>Review of "Plone 3 Products Development Cookbook"</title>
    <link>http://glicksoftware.com/blog/review-of-plone-3-products-development-cookbook</link>
    <description>Packt Publishing recently released their new title, "Plone 3 Products Development Cookbook"</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Last month <a class="external-link" href="http://packtpub.com">Packt Publishing</a> released a new title to add their growing line of books on Plone development —<a class="external-link" href="https://www.packtpub.com/plone-3-3-products-development-cookbook/book"> <i>Plone 3 Products Development Cookbook</i></a> by Juan Pablo Giménez and Marcos F. Romero, reviewed by Martin Aspeli, Alec Mitchell, and Emanuel Sartor. The book aims to be a useful recipe-oriented resource for beginner- to intermediate-level developers of Plone add-on products. Having read the review copy Packt was kind enough to send me, I can recommend it as an up-to-date and more example-oriented complement to Packt's still-great established title on Plone 3 development, Martin Aspeli's <i><a class="external-link" href="https://www.packtpub.com/Professional-Plone-web-applications-CMS/book">Professional Plone Development</a></i>.<br /><br />I am a bit skeptical of the traditional cookbook format, in which a series of disjointed "recipes" are presented for dealing with various scenarios.  In practice these are most useful when your needs match the problem expressed in the recipe exactly, which is seldom. So I was glad to see that the authors of this cookbook have departed from that pattern somewhat. The book does contain a series of recipes, but they are all related to an overall project, to create a digital newspaper website, which runs throughout the book. This helps provide continuity and give clarity on which pieces of the included recipes are needed for the particular project as opposed to being general.<br /><br />The "recipe" concept is still evident in the book's format, though. Each chapter is divided into several tasks, each of which has 2 main parts: a "How to do it" section which lists the steps needed to complete the task, followed by a "How it works" section which describes the theory behind the task as well as the finer points of any of the steps which were non-obvious. It may seem a little weird that the explanation is separate from the instructions, but I think it could be a useful approach for a reader who learns well by example, and wants to try to figure out the meaning of the steps as they go before the answers are given. This format is one of the main things that sets this book apart from <i>Professional Plone Development</i>, which otherwise covers some similar topics.  I would recommend the latter to someone who learns best by reading first or who wants a reference, and this book to someone who learns best by doing first.<br /><br />In addition, this book is somewhat more up-to-date with current tools and techniques (though I anticipate that will be fully rectified in the <a class="external-link" href="http://www.martinaspeli.net/articles/professional-plone-development-plone-4-edition">upcoming Plone 4 edition of PPD</a> later this year).  Though the Products Development Cookbook goes into less depth in terms of explanation, the authors have been quite good about including links to up-to-date online resources such as the <a class="external-link" href="http://plonemanual.twinapex.fi/">Plone Developer Manual</a> where they don't have space to cover a subject in depth. The title, <i>Plone 3 Products Development Cookbook</i>, is a bit of a misnomer, by the way.  As far as I know, almost all the techniques described should work just as well in Plone 4.  (Unfortunately this repeats a mistake in naming from Erik Rose's <a class="external-link" href="https://www.packtpub.com/plone-3-for-education/book"><i>Plone 3 for Education</i></a> and Alex Clark's <a class="external-link" href="https://www.packtpub.com/plone-3-3-site-administration/book"><i>Plone 3 Site Administration</i></a>, which also both aim to be relevant for Plone 4...it is a shame Packt didn't realize it is possible to write a book that targets both Plone 3 and Plone 4.)<br /><br />I particularly enjoyed the chapters discussing content type creation. The book has examples of building an Archetype using ArgoUML and ArchGenXML, of building an Archetype using the 'archetype' ZopeSkel template, of creating a basic type using plone.app.content, and of creating a type using Dexterity...so this makes for an interesting quick comparison of the steps required in each case. (It is not an in-depth discussion of any of these approaches, though.  I would use the online manuals for Archetypes and Dexterity to learn the finer points of either system.)<br /><br />There are also several chapters that did a good job of recording some of the lore about Plone development tools and processes that has mostly been passed along in blogs and sprints so far.  For example, it covers the use of IPython and ipdb, plone.reload, in-browser testing with selenium, load testing with funkload, and a full production buildout including ZEO, varnish, pound, supervisor, and assorted utilities.  As a seasoned Plone developer myself, I even learned a few things from these sections. In addition to the topics mentioned above, the book also covers installing Plone, testing, internationalization, workflow and permissions, KSS, portlets, use of the Zope Component Architecture, and integrating with an external system (OpenX) via XML-RPC.<br /><br />For more information, orders, and errata, visit Packt Publishing's page for <a class="external-link" href="https://www.packtpub.com/plone-3-3-products-development-cookbook/book"><i>Plone 3 Products Development Cookbook</i></a>.  As usual for Packt, the book is available both in print and as an ebook.<br /><br /></p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-06-07T01:24:11Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/introducing-the-plone-resource-customizer">
    <title>Introducing the Plone resource customizer</title>
    <link>http://glicksoftware.com/blog/introducing-the-plone-resource-customizer</link>
    <description>Thanks to the sprint at Plone Symposium East, the Plone resource customizer is almost ready for a beta release.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>Going into the ZMI to edit templates and stylesheets and replace images sucks. Especially once we made it so there are two places you might need to go in Plone 3 (portal_skins and portal_view_customizations).  I've been working on a better way, the (drumroll please) Plone resource customizer<a class="anchor-link" href="#betternames">*</a>.</p>
<p>Thanks to the great work of my fellow sprinters at Plone Symposium East, I'm now ready to give a preview of the tool, which hopefully will see a first beta release real soon now™. Here's the screencast...<b><br /></b></p>
<p>
<object data="http://wglick.org/jingswfplayer.swf" height="349" id="scPlayer" type="application/x-shockwave-flash" width="600">
<param name="data" value="http://wglick.org/jingswfplayer.swf" />
<param name="quality" value="high" />
<param name="bgcolor" value="#FFFFFF" />
<param name="flashVars" value="thumb=http://content.screencast.com/users/davisagli/folders/Jing/media/ba6aedad-4c59-44cc-ba7d-d9a96bb3df48/FirstFrame.jpg&amp;containerwidth=600&amp;containerheight=349&amp;content=http://wglick.org/customizer.swf" />
<param name="allowFullScreen" value="true" />
<param name="scale" value="showall" />
<param name="allowScriptAccess" value="always" />
<param name="base" value="http://wglick.org/" />
<param name="src" value="http://wglick.org/jingswfplayer.swf" />
<param name="flashvars" value="thumb=http://content.screencast.com/users/davisagli/folders/Jing/media/ba6aedad-4c59-44cc-ba7d-d9a96bb3df48/FirstFrame.jpg&amp;containerwidth=600&amp;containerheight=349&amp;content=http://wglick.org/customizer.swf" />
<param name="allowfullscreen" value="true" />
</object>
</p>
<p>I forgot to mention in the screencast that Eric Steele has also started work on integrating this tool with Gloworm, so that it will be possible to find a resource to customize just by pointing and clicking (a la Firebug).</p>
<p>Currently the customizer displays items from CMF skin layers, browser view templates, viewlets, and portlets. Support for other things like browser resources or ZMI pages could probably be added, at least in a read-only fashion. The infrastructure is flexible enough to support new ways of registering resources that haven't been invented yet.</p>
<p>For now, if you're adventurous and want to try out the customizer, get a copy of the Plone 4 coredev buildout, and run it using -c experimental/skineditor.cfg</p>
<p>We are tracking bugs and ideas for improvement in the Plone bug tracker, with the component set to "Skin Editor."</p>
<p>This is only the beginning...David Bain is working on the customizer as part of the Google Summer of Code, and we've got lots of ideas about how to make it even more useful.</p>
<p><a name="betternames"></a>*better names hereby solicited :)</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    
      <dc:subject>sprint</dc:subject>
    
    
      <dc:subject>customizer</dc:subject>
    
    <dc:date>2010-06-02T05:50:00Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>


  <item rdf:about="http://glicksoftware.com/blog/be-vigilant-know-your-buildout-threat-level">
    <title>Be vigilant: Know your Buildout Threat Level</title>
    <link>http://glicksoftware.com/blog/be-vigilant-know-your-buildout-threat-level</link>
    <description>The Buildout Threat Level indicator reports the percentage of buildouts that have succeeded in the past 4 hours.</description>
    <content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[<p>For those who missed my announcement last week on Twitter –</p>
<p>As a result of a mostly tongue-in-cheek conversation on IRC with Elizabeth Leddy, Alex Clark, and Matthew Wilkes, I decided to set up the global <a class="external-link" href="http://buildthreat.appspot.com">zc.buildout threat level indicator</a>.</p>
<p>The indicator collects reports of success and failure from actual buildout runs, and displays the current threat level based on the percentage of buildouts that have succeeded in the past 4 hours.</p>
<p>I'm happy to report that the current threat level is LOW.</p>
<p><img src="http://glicksoftware.com/images/Screenshot20100509at8.47.21PM.png" alt="Buildout Threat Level: Low" class="image-inline" title="Be vigilant: Know your Buildout Threat Level" /></p>
<p>Creating the indicator was mostly an excuse to try writing a Google App Engine app, but it's already proved its utility.  Last Thursday the threat level reached ELEVATED after a new distribute release started causing buildouts to fail under Python 2.4. Thanks to Tarek Ziadé for the quick response with a new distribute release to fix that.</p>
<p>You can help make the indicator more accurate!  Just add the <a class="external-link" href="http://pypi.python.org/pypi/buildout.threatlevel">buildout.threatlevel</a> extension to your buildout:</p>
<pre>[buildout]<br />extensions = buildout.threatlevel<br /></pre>
<p>This will display the current threat level as one of the initial steps when you run buildout (so that you have an opportunity to abort if it has reached SEVERE), and will ping my app engine app when the buildout is done to report success or failure. (No data is collected in this ping aside from a boolean indicating success, time, and IP address.)</p>
<p>Code for the GAE app and buildout extension is <a class="external-link" href="http://svn.plone.org/svn/collective/buildout/buildout.threatlevel">in the collective</a>.</p>]]></content:encoded>
    <dc:publisher>No publisher</dc:publisher>
    <dc:creator>David Glick</dc:creator>
    <dc:rights></dc:rights>
    <dc:date>2010-05-10T04:09:15Z</dc:date>
    <dc:type>Blog Entry</dc:type>
  </item>





</rdf:RDF>
