<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet href="http://blogs.nuxeo.com/rss.css" type="text/css"?>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns="http://purl.org/rss/1.0/"
  xmlns:xhtml="http://www.w3.org/1999/xhtml">

  <channel rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/exportrss">
    <title>Florent Guillaume</title>
    <description>RSS 1.0 export from the folder 'Florent Guillaume'.</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/exportrss</link>

    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_12_javaone-2007-slides" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_03_discover-nuxeo-5-at-javaone-2007" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_10_18_what-s-point-jcr" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_10_18_nuxeo-5-unifying-content-apis" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_01_24_genericsetup-for-cps-cmf" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_10_object-event-dispatching" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_08_events-in-zope-2-9" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_10_28_cps-second-bug-day" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_09_21_cps-3-3-6-released" />
        <rdf:li rdf:resource="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_09_16_cps-bug-day-wrapup" />

      </rdf:Seq>
    </items>

  </channel>


  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_12_javaone-2007-slides">
    <title>JavaOne 2007 Slides</title>
    <description>&lt;a href="http://java.sun.com/javaone/sf/"&gt;JavaOne&lt;/a&gt; is now finished. We
  had a very interesting few days there, meeting lots of people interested in
  &lt;a href="http://www.nuxeo.com/en/products/"&gt;Nuxeo&lt;/a&gt; and wanting to know
  more about the technology and the business around it. My talk itself was
  well received, and I hope it gave people a good sense of what is possible
  and desirable in the &lt;a href="http://www.nuxeo.com/en/ecm/"&gt;Open Source
  ECM&lt;/a&gt; problem space.&lt;br /&gt;
   &lt;br /&gt;
   You can view the &lt;a
  href="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_12_javaone-2007-slides/downloadFile/attachedFile_f0/Nuxeo-JavaOne-2007.pdf"&gt;
  slides of the talk here&lt;/a&gt;.&lt;br /&gt;
   &lt;br /&gt;
   I also attended some very interesting sessions regarding subjects that are
  important when writing an ECM framework: persistence, distribution,
  scalability, validation, object-relational modeling, etc. A number of
  efforts are ongoing to standardize as JSRs some important aspects related to
  this. In the coming days I'll be blogging in more detail about these JSRs
  that we'll be tracking closely.&lt;br /&gt;
   &lt;br /&gt;
   For our first participation, this was a very fruitful experience, and we'll
  certainly be going again next year!&lt;br /&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_12_javaone-2007-slides</link>
    <dc:date>2007-05-12</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>apogee</dc:subject>
    <dc:subject>eclipse</dc:subject>
    <dc:subject>ecm</dc:subject>
    <dc:subject>java</dc:subject>
    <dc:subject>jboss</dc:subject>
    <dc:subject>nuxeo</dc:subject>
    <dc:subject>nuxeo5</dc:subject>
    <dc:subject>slides</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_03_discover-nuxeo-5-at-javaone-2007">
    <title>Discover Nuxeo 5 at JavaOne 2007</title>
    <description>&lt;a href="http://java.sun.com/javaone/sf/"&gt;JavaOne 2007&lt;/a&gt; is in a few days
  now, and we'll be giving a talk about &lt;a
  href="http://www.nuxeo.com/en/products/"&gt;Nuxeo 5&lt;/a&gt;.&lt;br /&gt;
   &lt;br /&gt;
   The talk has ID &lt;a
  href="http://www28.cplan.com/cc158/sessions_catalog.jsp?is=yes&amp;amp;icriteria9=TS-4532"&gt;
  TS-4532&lt;/a&gt; (in the Java EE track), and takes place on Thursday the 10th at
  5:30 pm in Hall E - 134. The title of the session is &lt;a
  href="http://www28.cplan.com/cc158/session_details.jsp?isid=286532&amp;amp;ilocation_id=158-1&amp;amp;ilanguage=english"&gt;
  Building an Embeddable Enterprise Content Management Core with the Latest
  Java Technologies&lt;/a&gt;. &lt;br /&gt;
   &lt;br /&gt;
   This is a great opportunity if you want to discover more about how Nuxeo 5
  is architected, see demos of the product, or talk to us about technical or
  business-related questions.&lt;br /&gt;
   &lt;br /&gt;
   As a teaser, here's the overview of the talk:&lt;br /&gt;
   &lt;br /&gt;
   &lt;b&gt;Learn about the design and use of Nuxeo 5, an embeddable, extensible
  Enterprise Content Management framework for Java EE and other
  platforms&lt;/b&gt;&lt;br /&gt;
   &lt;br /&gt;
   Agenda:&lt;br /&gt;
   

  &lt;ul&gt;
   &lt;li&gt;What is Enterprise Content Management&lt;/li&gt;

   &lt;li&gt;What do we want to achieve&lt;/li&gt;

   &lt;li&gt;Core Framework&lt;/li&gt;

   &lt;li&gt;Core and High-Level Services&lt;/li&gt;

   &lt;li&gt;UI Layers&lt;/li&gt;

   &lt;li&gt;Extensible and Pluggable&lt;/li&gt;

   &lt;li&gt;Using the Framework&lt;/li&gt;
  &lt;/ul&gt;
  &lt;br /&gt;
   See you in San Francisco!&lt;br /&gt;
  &lt;br /&gt;
   &lt;br /&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2007_05_03_discover-nuxeo-5-at-javaone-2007</link>
    <dc:date>2007-05-03</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>apogee</dc:subject>
    <dc:subject>eclipse</dc:subject>
    <dc:subject>ecm</dc:subject>
    <dc:subject>java</dc:subject>
    <dc:subject>jboss</dc:subject>
    <dc:subject>nuxeo</dc:subject>
    <dc:subject>nuxeo5</dc:subject>
    <dc:subject>slides</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_10_18_what-s-point-jcr">
    <title>What's the point of JCR?</title>
    <description>&lt;p&gt;
Nuxeo is switching its &lt;a href="http://www.nuxeo.com/en/products/"&gt;ECM&lt;/a&gt; to Java, and we're using &lt;a href="http://jcp.org/en/jsr/detail?id=170"&gt;JCR&lt;/a&gt; for our document storage. JCR (Java Content Repository, standardized by &lt;a href="http://jcp.org/en/jsr/detail?id=170"&gt;JSR-170&lt;/a&gt; and the upcoming &lt;a href="http://jcp.org/en/jsr/detail?id=283"&gt;JSR-283&lt;/a&gt;) is a young specification with a promising future &amp;mdash; but what's its point, you may ask, as all existing content management systems are already storing content very well without it? Its goal is &lt;em&gt;interoperability&lt;/em&gt; between vendors, which will make it possible for people who write applications needing to store content to have a unified API for such manipulations. All major content repository vendors are active in the JSR-283 expert group, and all are working on JCR bindings for their various proprietary repositories.
&lt;/p&gt;
&lt;p&gt;
Of course a standardized and wildly successful way of manipulating content already existed before JCR: &lt;a href="http://en.wikipedia.org/wiki/SQL"&gt;SQL&lt;/a&gt;. But SQL and JCR have a different focus:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL is a language; it manipulates rows and is geared toward generic relation manipulation,&lt;/li&gt;
&lt;li&gt;JCR is a Java API; it manipulates nodes and is geared toward hierarchical manipulation (parent-children).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
SQL and JCR have quite different underlying data models:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SQL's model is that of tables with fixed schemas, and relations between tables,&lt;/li&gt;
&lt;li&gt;JCR's model is that of a tree of nodes with flexible schemas and with parent-children relations as the main focus &amp;mdash; although other types of relations exist.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
JCR also offers higher level features than SQL, notably workspace and version management.
&lt;/p&gt;
&lt;p&gt;
For many kinds of applications, there is a focus on being able to arrange documents in folder hierarchies, and to have a wild variety of structure for these documents. In this case, a storage model based on JCR is much more suited than something based on SQL.
&lt;/p&gt;
&lt;p&gt;
It should be noted that many things in the computing world already are based on the notion of folder hierarchies storing arbitrary documents:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filesystems are a tree of (unstructured) documents,&lt;/li&gt;
&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/Revision_control"&gt;Revision control systems&lt;/a&gt; are based on filesystem concepts but add a lot of structure on top of them,&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.webdav.org/"&gt;WebDAV&lt;/a&gt; (along with DAV and DELTAV) is a protocol that addresses documents using a path, and where documents have a flexible set of properties,&lt;/li&gt;
&lt;li&gt;most proprietary content management systems are based on (or offer the notion of) organizing the content in a hierarchy,&lt;/li&gt;
&lt;li&gt;Nuxeo's &lt;a href="http://www.nuxeo.com/en/products/cps/"&gt;CPS&lt;/a&gt; is itself based on this classic model.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
This is why JCR has emerged as a common and useful storage API for all these use cases.
&lt;/p&gt;
&lt;p&gt;
For an ECM framework like &lt;a href="http://www.nuxeo.com/en/products/ "&gt;Nuxeo 5&lt;/a&gt;, JCR interoperability can be seen from two different directions:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;JCR provides an API that we can use to store our content, to make us vendor-independent and flexible regarding storage,&lt;/li&gt;
&lt;li&gt;JCR provides an API through which we can expose our content, which makes our platform usable by any external system that understands it &amp;mdash; we're ourselves a vendor providing JCR bindings.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
For its initial release, Nuxeo 5 is focusing on the use of JCR as its main storage implementation and uses &lt;a href="http://jackrabbit.apache.org/"&gt;Jackrabbit&lt;/a&gt; to store most of our content. In the future, we'll also provide JCR bindings so that the high-level content we provide can be directly accessed from external applications using JCR too.
&lt;/p&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_10_18_what-s-point-jcr</link>
    <dc:date>2006-10-18</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>cps</dc:subject>
    <dc:subject>ecm</dc:subject>
    <dc:subject>java</dc:subject>
    <dc:subject>nuxeo5</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_10_18_nuxeo-5-unifying-content-apis">
    <title>Nuxeo 5: Unifying the content APIs</title>
    <description>&lt;p&gt;
In a content management system, the actual data that the system or the users manipulate comes from many kinds of sources. Content can come from a &lt;a href="http://jcp.org/en/jsr/detail?id=170"&gt;JCR&lt;/a&gt; repository, or from a &lt;a href="http://en.wikipedia.org/wiki/Relational_database"&gt;relational database&lt;/a&gt;, or from an &lt;a href="http://en.wikipedia.org/wiki/LDAP"&gt;LDAP&lt;/a&gt; directory, or from a semantic storage engine like &lt;a href="http://jena.sourceforge.net/"&gt;Jena&lt;/a&gt;, or from any other kind of open or proprietary storage engine.
&lt;/p&gt;
&lt;p&gt;
But fundamentally all these kinds of content, which I'll call "records", aren't very different:
&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a record can be created, viewed, modified, deleted,&lt;/li&gt;
&lt;li&gt;a record can often be copied or moved,&lt;/li&gt;
&lt;li&gt;a record obeys a schema that can be known to the system, this means that its individual fields are strictly typed,&lt;/li&gt;
&lt;li&gt;when being viewed or modified, a record has a user interface that is based on forms, labels, widgets, depending on the schema,&lt;/li&gt;
&lt;li&gt;records can be searched and a result set returned,&lt;/li&gt;
&lt;li&gt;records can be listed in a compact form (search results, folder contents, user dashboard, workflow workitems, RDB table listing, user information browsing, etc.),&lt;/li&gt;
&lt;li&gt;records have an identity (like a unique id) or a location (like a path), sometimes both.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;
One of the strengths of &lt;a href="http://www.nuxeo.com/en/products/cps/"&gt;CPS&lt;/a&gt; is to use a common abstraction for many of these concepts, embodied in the CPSSchemas component. In &lt;a href="http://www.nuxeo.com/en/products/"&gt;Nuxeo 5&lt;/a&gt; we want to go further and provide even more integration for all these, the base components for these abstractions are &lt;a href="http://maven.nuxeo.org/NXCore/"&gt;NXCore&lt;/a&gt; and &lt;a href="http://maven.nuxeo.org/NXTypeManager/"&gt;NXTypeManager&lt;/a&gt;.
&lt;/p&gt;
&lt;p&gt;
The reasons to strive for convergence are numerous:
&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;this merges into unique concepts things that had been previously separated because of different implementation choices. For instance, an LDAP schema is not fundamentally different from an SQL schema (or from an &lt;a href="http://www.w3.org/XML/Schema"&gt;XML Schema&lt;/a&gt; if one is interested in the relevant subset).&lt;/li&gt;
&lt;li&gt;this gives the programmers a common API for all data-related operations, which means more reusability. For instance changing an attribute in an LDAP entry doesn't have to be different from changing the title of a document or changing a value in an RDB row; processing a list of search results to display them in a table doesn't have to be different from processing the children of a folder to display the folder's contents.&lt;/li&gt;
&lt;li&gt;this gives the framework developers a way to optimize some operations because of commonalities in the underlying implementations. For instance you don't need three kinds of events dispatching for "LDAP entry modified", "RDB row modified" or "document modified".&lt;/li&gt;
&lt;li&gt;this gives the users a unified way of manipulating different kinds of data when there's really no need to have a different UI for them. When a user fills a form it's really the same process whether he's modifying his personal preferences, adding a keyword to a document, or changing a quantity in an RDB row.&lt;/li&gt;
&lt;li&gt;this allows very simple migrations between storage technologies, when these are felt necessary. A customer could start with an LDAP database for its user base and later have the need to move them to an RDB table. User entries in an RDB table may need to be versioned and moved to a JCR storage. An application should survive all these with only configuration changes and no code to rewrite.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;
It should be noted that this means that JCR is in no way the primary storage model for Nuxeo 5, it's only the first one to be implemented. In the future, it will be possible to store documents in LDAP or an RDB. When a suitable storage model is devised and implemented, you'll be able to apply workflow or versioning to RDB-based documents for instance.
&lt;/p&gt;
&lt;p&gt;
This convergence is quite exciting to us, and our goal is to allow people to build complex applications with Nuxeo 5 in a more straightforward manner.
&lt;/p&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_10_18_nuxeo-5-unifying-content-apis</link>
    <dc:date>2006-10-18</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>cps</dc:subject>
    <dc:subject>ecm</dc:subject>
    <dc:subject>java</dc:subject>
    <dc:subject>nuxeo5</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_01_24_genericsetup-for-cps-cmf">
    <title>GenericSetup for CPS, CMF and Zope</title>
    <description>&lt;p&gt;GenericSetup is a framework to describe the configuration of a Zope site
as a set of XML files (and sometimes other associated files). It can
import profiles, which may create objects or change their configuration,
and export profiles, which makes a snapshot of the configuration and
writes it to a set of XML files.&lt;/p&gt;
&lt;p&gt;GenericSetup provides a tool that can store snapshots of a configuration
in the ZODB itself, where it can be examined and even modified. It can
also do diffs between two snapshots, which is very useful to find out
what changed in a configuration (it's a good idea to take a full
snapshot anytime some significant changes are made to the
configuration).&lt;/p&gt;
&lt;p&gt;GenericSetup differentiates between Base and Extension profiles. A Base
profile is a profile that describes &amp;quot;everything&amp;quot;. When it is imported,
it removes and overwrites any previous configuration. An Extension
profile is a profile designed to be incrementally added on top of a
previously existing configuration. It may of course overwrite some
settings, or even in some case remove objects, but its goal is generally
to add optional configuration on top of a main one&lt;/p&gt;
&lt;p&gt;GenericSetup is based on a small number of concepts: the toolset, and
some import and export steps. GenericSetup provides a framework where
import and export steps can be written simply using Zope 3 adapters.&lt;/p&gt;
&lt;p&gt;GenericSetup was born as &amp;quot;CMFSetup&amp;quot; but was later made generic and can
be used by any Zope 2 application. It is now available at
&lt;a class="reference" href="http://svn.zope.org/GenericSetup/trunk/"&gt;http://svn.zope.org/GenericSetup/trunk/&lt;/a&gt;. A subclass with additional
features is used in &lt;a class="reference" href="http://www.cps-project.org/"&gt;CPS&lt;/a&gt;.&lt;/p&gt;
&lt;div class="section" id="setup-tool"&gt;
&lt;h1&gt;&lt;a name="setup-tool"&gt;Setup tool&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The setup tool is called &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;setup_tool&lt;/span&gt;&lt;/tt&gt; by default, and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;portal_setup&lt;/span&gt;&lt;/tt&gt;
in CPS (if missing, it can be instantiated by selecting &amp;quot;CPS Tools&amp;quot; from
the ZMI Add menu).&lt;/p&gt;
&lt;p&gt;At a given time, the setup tool knows about a few things:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;the currently selected profile, which may point to an area in the
filesystem where a profile has been registered, or to a path in the
ZODB where a snapshot was taken,&lt;/li&gt;
&lt;li&gt;the current toolset,&lt;/li&gt;
&lt;li&gt;the current import steps,&lt;/li&gt;
&lt;li&gt;the current export steps.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When a new profile it selected, its toolset, import steps and export
steps XML files are loaded and merged with the tool's current ones.&lt;/p&gt;
&lt;p&gt;Any import or export is based on the &lt;em&gt;full&lt;/em&gt; toolset or import/export
steps (even if the currently selected profile is an extension profile),
but the source of XML configuration files depends solely on the
currently selected profile.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="toolset"&gt;
&lt;h1&gt;&lt;a name="toolset"&gt;Toolset&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The file &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;toolset.xml&lt;/span&gt;&lt;/tt&gt; describes the Toolset, which is the set of
tools needed in a given configuration. (While the name &amp;quot;tool&amp;quot; would
suggest CMF, it's just a set of objects that can be instantiated at the
root of the configured site.)&lt;/p&gt;
&lt;p&gt;Beyond being based on data that is updated when an extension profile is
selected, the toolset is a normal import/export step.&lt;/p&gt;
&lt;p&gt;When the toolset is imported, all the objects in the toolset are
instantiated if they're missing or if their class doesn't match with
what it's supposed to be.&lt;/p&gt;
&lt;p&gt;An excerpt of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;toolset.xml&lt;/span&gt;&lt;/tt&gt; for the CPS Tree Tool would be:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;tool-setup&amp;gt;
  ...
  &amp;lt;required tool_id=&amp;quot;portal_trees&amp;quot;
            class=&amp;quot;Products.CPSCore.TreeTool.TreeTool&amp;quot;/&amp;gt;
  ...
&amp;lt;/tool-setup&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="import-steps"&gt;
&lt;h1&gt;&lt;a name="import-steps"&gt;Import steps&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Import steps (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;import_steps.xml&lt;/span&gt;&lt;/tt&gt;) describe a set of configuration
steps available for import, and their dependencies. A step is just the
dotted name of a function, and the dependencies is simply the steps that
have to be run before this one can be done (most steps depend on the
toolset, because they need a base tool to be instantiated before it can
be configured).&lt;/p&gt;
&lt;p&gt;While an import step can do anything it likes, GenericSetup provides a
framework based on Zope 3 adapters to simply describe how a single
object is imported from XML, and to recurse among objects to create or
configure them one by one.&lt;/p&gt;
&lt;div class="section" id="purge"&gt;
&lt;h2&gt;&lt;a name="purge"&gt;Purge&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;During import, there are two possible behaviors, corresponding to the
two kinds of profiles. For a Base profile, the import happens in &amp;quot;purge&amp;quot;
mode, while for an extension profile the import doesn't purge. The
functions and adapters doing the import have to take that into account
when they read a profile.&lt;/p&gt;
&lt;p&gt;In purge mode, every previous configuration has to be removed, so that
the result is indistinguishable from an install from scratch. In
non-purge mode, care must be taken to not overwrite or remove settings
or objects which are not explicitely specified in the imported XML file.&lt;/p&gt;
&lt;p&gt;An excerpt of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;import_steps.xml&lt;/span&gt;&lt;/tt&gt; for the CPS Tree Tool would be:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;import-steps&amp;gt;
  ...
  &amp;lt;import-step id=&amp;quot;trees&amp;quot; version=&amp;quot;20051230-01&amp;quot;
               handler=&amp;quot;Products.CPSCore.exportimport.importTreeTool&amp;quot;
               title=&amp;quot;Tree Tool&amp;quot;&amp;gt;
    &amp;lt;dependency step=&amp;quot;toolset&amp;quot;/&amp;gt;
    Import tree tool and tree caches.
  &amp;lt;/import-step&amp;gt;
  ...
&amp;lt;/import-steps&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="export-steps"&gt;
&lt;h1&gt;&lt;a name="export-steps"&gt;Export steps&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Export steps (&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;export_steps.xml&lt;/span&gt;&lt;/tt&gt;) describe the list of steps available
for export. An export step is used in exactly the same way than an
import step, except for the fact that there are no dependencies between
export steps, and that instead of being read, XML files are written.&lt;/p&gt;
&lt;p&gt;One important thing to note is that export steps &lt;em&gt;cannot&lt;/em&gt; do the
&amp;quot;incremental exports&amp;quot; that many people expect. When an extension profile
is read by the import steps, only the available XML files for that
profile are read. However when writing, there's no way to choose which
XML files are relevant, so the whole profile for that step is written
(and recursion is done in all subobjects).&lt;/p&gt;
&lt;p&gt;It's possible that future versions of GenericSetup will have some
capabilities to do incremental exports, but this is not possible for
now.&lt;/p&gt;
&lt;p&gt;An excerpt of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;export_steps.xml&lt;/span&gt;&lt;/tt&gt; for the CPS Tree Tool would be:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot;?&amp;gt;
&amp;lt;export-steps&amp;gt;
  ...
  &amp;lt;export-step id=&amp;quot;trees&amp;quot;
               handler=&amp;quot;Products.CPSCore.exportimport.exportTreeTool&amp;quot;
               title=&amp;quot;Tree Tool&amp;quot;&amp;gt;
    Export tree tool and tree caches.
  &amp;lt;/export-step&amp;gt;
  ...
&amp;lt;/export-steps&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="adapters"&gt;
&lt;h1&gt;&lt;a name="adapters"&gt;Adapters&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The standard work done in an import or export step is to find the base
tool, and call &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;importObjects&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;exportObjects&lt;/span&gt;&lt;/tt&gt; on it; these are
recursive functions that take each object, find an adapter for them
describing how the XML import or export is done, and call it.&lt;/p&gt;
&lt;p&gt;For the CPS Tree Tool, the import steps and export steps call the
following functions:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def exportTreeTool(context):
    &amp;quot;&amp;quot;&amp;quot;Export Tree tool and tree caches as a set of XML files.
    &amp;quot;&amp;quot;&amp;quot;
    site = context.getSite()
    tool = getToolByName(site, 'portal_trees', None)
    if tool is None:
        logger = context.getLogger('trees')
        logger.info(&amp;quot;Nothing to export.&amp;quot;)
        return
    exportObjects(tool, '', context)

def importTreeTool(context):
    &amp;quot;&amp;quot;&amp;quot;Import Tree tool and tree caches as a set of XML files.
    &amp;quot;&amp;quot;&amp;quot;
    site = context.getSite()
    tool = getToolByName(site, 'portal_trees')
    importObjects(tool, '', context)
&lt;/pre&gt;
&lt;p&gt;This is pretty boileplate and could even be simplified in the future
through ZCML declarations. Above, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;''&lt;/span&gt;&lt;/tt&gt; simply refers to the root of
the profile directory.&lt;/p&gt;
&lt;p&gt;The adapters are multi-adapters, adapting both an object and an import
context (called &amp;quot;environ&amp;quot; in GenericSetup), to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IBody&lt;/span&gt;&lt;/tt&gt; interface
that basically describes a file body. They can be registered through
ZCML using the standard statement:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;adapter
    factory=&amp;quot;.exportimport.TreeToolXMLAdapter&amp;quot;
    provides=&amp;quot;Products.GenericSetup.interfaces.IBody&amp;quot;
    for=&amp;quot;.interfaces.ITreeTool
         Products.GenericSetup.interfaces.ISetupEnviron&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This assumes of course that the exported object is described through an
interface, for instance here declared as:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from zope.interface import Interface

class ITreeTool(Interface):
    &amp;quot;&amp;quot;&amp;quot;Tree Tool.
    &amp;quot;&amp;quot;&amp;quot;
&lt;/pre&gt;
&lt;p&gt;The interface is implemented by the object's class with:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from zope.interface import implements

class TreeTool(UniqueObject, Folder):
    &amp;quot;&amp;quot;&amp;quot;Tree Tool that caches information about the site's hierarchies.
    &amp;quot;&amp;quot;&amp;quot;
    implements(ITreeTool)

    id = 'portal_trees'
    meta_type = 'CPS Tree Tool'
    ...
&lt;/pre&gt;
&lt;p&gt;When doing an export, the adapters build a DOM tree for the
configuration. When doing an import, they read the DOM tree and create
or modify properties as needed. The standard adapters don't create
subobjects or recurse in them, this is left to the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;importObjects&lt;/span&gt;&lt;/tt&gt;
function.&lt;/p&gt;
&lt;p&gt;These adapters can be written easily because GenericSetup provides
helpers for the common cases of objects configured only through standard
Zope 2 properties (PropertyManager), or having subobjects
(ObjectManager).&lt;/p&gt;
&lt;p&gt;Of course all this can be changed for specific cases. Many older CMF
tools are configured through things that are not standard properties,
and for instance CPS needs to do recursion into more than simple
ObjectManager subobjects. It is also possible to read and write files
that are not XML files, CPS does this for the images included in portlet
objects, where it writes a real image file.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="additional-cps-feature-upgrades"&gt;
&lt;h1&gt;&lt;a name="additional-cps-feature-upgrades"&gt;Additional CPS feature: upgrades&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;CPS has extended the setup tool to provide a basic Upgrade feature, that
is related to the configuration of the site but cannot be expressed by
the standard GenericSetup profiles.&lt;/p&gt;
&lt;p&gt;An upgrade step is registered through ZCML with something like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;cps:upgradeStep
    title=&amp;quot;Replace CMF URL tool with a CPS-specific one&amp;quot;
    source=&amp;quot;3.3.4&amp;quot; destination=&amp;quot;3.3.5&amp;quot;
    handler=&amp;quot;.upgrade.upgradeURLTool&amp;quot;
    checker=&amp;quot;.upgrade.check_upgradeURLTool&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This describes between what CPS versions this upgrade is needed, how to
do it, and how to check if it's already been done.&lt;/p&gt;
&lt;p&gt;The setup tool lists which steps have not yet been done, and provides a
way to run them one by one or all at once. At a given time, a CPS site
&amp;quot;knows&amp;quot; (through a site-global property &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;last_upgraded_version&lt;/span&gt;&lt;/tt&gt;) up to
which version it's been upgraded.&lt;/p&gt;
&lt;p&gt;The checker is optional; if it would be too costly to check for the
conversion, it can be omitted. This is the case typically for an upgrade
step that would recurse in the content object to do some fixups; to
check if they've already been done it would have to recurse in the
content objects too, and that's too much work for a simple check. The
setup tool won't list a step without a checker if the portal has already
been upgraded to a later version than the step's &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;destination&lt;/span&gt;&lt;/tt&gt;
version.&lt;/p&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2006_01_24_genericsetup-for-cps-cmf</link>
    <dc:date>2006-01-24</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume, root</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>cps</dc:subject>
    <dc:subject>five</dc:subject>
    <dc:subject>zope</dc:subject>
    <dc:subject>zope3</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_10_object-event-dispatching">
    <title>Object event dispatching in Zope</title>
    <description>&lt;p&gt;Here are some explanations about what happens in Zope 3.2 (and Zope 2.9
when using Five) when an event notification is sent by some code, up to
a specific subscriber. It focuses more specifically on object events,
which go through some additional hoops. All this is complex because
there are many simple components that are linked together.&lt;/p&gt;
&lt;p&gt;Let's start with some framework code that sends an event after an object
has been added (similar to what &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope.app.container.contained&lt;/span&gt;&lt;/tt&gt;
actually does):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
event = ObjectAddedEvent(ob, container, name)
zope.event.notify(event)
&lt;/pre&gt;
&lt;p&gt;In &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope.event&lt;/span&gt;&lt;/tt&gt; we have the definition for this function:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
subscribers = [] # registered subscribers
def notify(event):
    for subscriber in subscribers:
        subscriber(event)
&lt;/pre&gt;
&lt;p&gt;During initialization, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope.app.event.dispatching&lt;/span&gt;&lt;/tt&gt; has registered a
subscriber:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def dispatch(*event):
    # Iterating over subscribers assures they get executed.
    for ignored in zope.component.subscribers(event, None):
        pass
zope.event.subscribers.append(dispatch)
&lt;/pre&gt;
&lt;p&gt;The function &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope.component.subscribers&lt;/span&gt;&lt;/tt&gt; will then call all matching
subscribers.&lt;/p&gt;
&lt;p&gt;During initialization, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope/app/event/configure.zcml&lt;/span&gt;&lt;/tt&gt; has registered
a subscriber for &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope.app.event.interfaces.IObjectEvent&lt;/span&gt;&lt;/tt&gt; with the
handler &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope.app.event.objectevent.objectEventNotify&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;subscriber
    for=&amp;quot;zope.app.event.interfaces.IObjectEvent&amp;quot;
    handler=&amp;quot;zope.app.event.objectevent.objectEventNotify&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This handler does:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def objectEventNotify(event):
    adapters = zope.component.subscribers((event.object, event), None)
    for adapter in adapters:
        pass # Getting them does the work.
&lt;/pre&gt;
&lt;p&gt;This means that the event will be redispatched, but this time a
subscriber can match using multi-adaptation on both the object and the
event interfaces, which gives much more flexibility and filtering
possibilities.&lt;/p&gt;
&lt;p&gt;During initialization, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope/app/container/configure.zcml&lt;/span&gt;&lt;/tt&gt; has
registered a multi-subscriber:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;subscriber
    for=&amp;quot;zope.app.location.interfaces.ILocation
         zope.app.container.interfaces.IObjectMovedEvent&amp;quot;
    handler=&amp;quot;zope.app.container.contained.dispatchToSublocations&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Note that &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IContained&lt;/span&gt;&lt;/tt&gt;, a base interface for most content objects,
derives from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ILocation&lt;/span&gt;&lt;/tt&gt;, so this subscriber will match most content
objects. Also, the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IObjectAddedEvent&lt;/span&gt;&lt;/tt&gt; sent initially derives from
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IObjectMovedEvent&lt;/span&gt;&lt;/tt&gt; so it will be matched.&lt;/p&gt;
&lt;p&gt;When using Five, in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;Five/event.zcml&lt;/span&gt;&lt;/tt&gt; some similar subscribers are
registered to react on &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IObjectManager&lt;/span&gt;&lt;/tt&gt; instead of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ILocation&lt;/span&gt;&lt;/tt&gt;, and
to dispatch using the same handler.&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;dispatchToSublocations&lt;/span&gt;&lt;/tt&gt; handler is:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def dispatchToSublocations(object, event):
    subs = ISublocations(object, None)
    if subs is not None:
        for sub in subs.sublocations():
            for ignored in zapi.subscribers((sub, event), None):
                pass # They do work in the adapter fetch
&lt;/pre&gt;
&lt;p&gt;This redispatches the same event to all subobjects, where &amp;quot;subobjects&amp;quot;
is defined using the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ISublocations&lt;/span&gt;&lt;/tt&gt; adapter. Now,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;zope/app/container/configure.zcml&lt;/span&gt;&lt;/tt&gt; has an adapter:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;adapter
    for=&amp;quot;zope.app.container.interfaces.IReadContainer&amp;quot;
    provides=&amp;quot;zope.app.location.interfaces.ISublocations&amp;quot;
    factory=&amp;quot;zope.app.container.contained.ContainerSublocations&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Most Zope 3 container objects are also &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IReadContainer&lt;/span&gt;&lt;/tt&gt;. The
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ContainerSublocations&lt;/span&gt;&lt;/tt&gt; handler does simply get the sublocations
using:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class ContainerSublocations(object):
    def __init__(self, container):
        self.container = container
    def sublocations(self):
        container = self.container
        for key in container:
            yield container[key]
&lt;/pre&gt;
&lt;p&gt;When using Five, a similar adapter is registered:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;adapter
    for=&amp;quot;OFS.interfaces.IObjectManager&amp;quot;
    provides=&amp;quot;zope.app.location.interfaces.ISublocations&amp;quot;
    factory=&amp;quot;OFS.subscribers.ObjectManagerSublocations&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;And the Five adapter does something comparable to the Zope 3 one:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class ObjectManagerSublocations(object):
    def __init__(self, container):
        self.container = container
    def sublocations(self):
        for ob in self.container.objectValues():
            yield ob
&lt;/pre&gt;
&lt;p&gt;All in all, our initial event will be redispatched to the sublocations
of the original object, and the process will be done recursively to all
the sublocations.&lt;/p&gt;
&lt;p&gt;Now some user code's &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;configure.zcml&lt;/span&gt;&lt;/tt&gt; can registered a
multi-subscriber:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;subscriber
    for=&amp;quot;.interfaces.IFoo
         zope.app.container.interfaces.IObjectAddedEvent&amp;quot;
    handler=&amp;quot;.foo.reactOnAdd&amp;quot;
    /&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Which ties to the handler:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def reactOnAdd(ob, event):
    &amp;quot;&amp;quot;&amp;quot;Does something.&amp;quot;&amp;quot;&amp;quot;
&lt;/pre&gt;
&lt;p&gt;In the handler, which will be called for all sublocations, &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ob&lt;/span&gt;&lt;/tt&gt; is any
sublocation of the original object (including the object itself), and
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;event&lt;/span&gt;&lt;/tt&gt; is the original event (which means that &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;event.object&lt;/span&gt;&lt;/tt&gt; is
the original object).&lt;/p&gt;
&lt;p&gt;This concludes our dive into through Zope 3 events. You can read in
&lt;a class="reference" href="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_08_events-in-zope-2-9"&gt;http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_08_events-in-zope-2-9&lt;/a&gt;
how object events can be used in Zope 2.9 to do what used to be done
using &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt;.&lt;/p&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_10_object-event-dispatching</link>
    <dc:date>2005-11-10</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>zope</dc:subject>
    <dc:subject>zope3</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_08_events-in-zope-2-9">
    <title>Events in Zope 2.9</title>
    <description>&lt;p&gt;Zope 2.9 (and Zope 2.8 when using Five 1.2) introduces a big change: Zope 3 style container events.&lt;/p&gt;
&lt;p&gt;With container events, you finally have the ability to react to things
happening to objects without have to subclass &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt;,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_beforeDelete&lt;/span&gt;&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterClone&lt;/span&gt;&lt;/tt&gt;. Instead, you just have
to register a subscriber for the appropriate event, for instance
IObjectAddedEvent, and make it do the work.&lt;/p&gt;
&lt;p&gt;Indeed, the old methods like &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt; are now deprecated, you
shouldn't use them anymore.&lt;/p&gt;
&lt;p&gt;Let's see how to migrate your products.&lt;/p&gt;
&lt;div class="section" id="old-product"&gt;
&lt;h1&gt;&lt;a name="old-product"&gt;Old product&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Suppose that in an old product you have code that needs to register
through a central tool whenever a document is created. Or it could be
indexing itself. Or it could initialize an attribute according to its
current path. Code like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class CoolDocument(...):
    ...
    def manage_afterAdd(self, item, container):
        self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
        getToolByName(self, 'portal_cool').registerCool(self)
        super(CoolDocument, self).manage_afterAdd(item, container)

    def manage_afterClone(self, item):
        self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
        getToolByName(self, 'portal_cool').registerCool(self)
        super(CoolDocument, self).manage_afterClone(item)

    def manage_beforeDelete(self, item, container):
        super(CoolDocument, self).manage_beforeDelete(item, container)
        getToolByName(self, 'portal_cool').unregisterCool(self)
&lt;/pre&gt;
&lt;p&gt;This would be the best practice in Zope 2.8. Note the use of &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;super()&lt;/span&gt;&lt;/tt&gt;
to call the base class, which is often omitted because people &amp;quot;know&amp;quot;
that SimpleItem for instance doesn't do anything in these methods.&lt;/p&gt;
&lt;p&gt;If you run this code in Zope 2.9, you will get deprecation warnings,
telling you that:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Calling Products.CoolProduct.CoolDocument.CoolDocument.manage_afterAdd
is deprecated when using Five, instead use event subscribers or mark
the class with &amp;lt;five:deprecatedManageAddDelete/&amp;gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="using-five-deprecatedmanageadddelete"&gt;
&lt;h1&gt;&lt;a name="using-five-deprecatedmanageadddelete"&gt;Using five:deprecatedManageAddDelete&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;The simplest thing you can do to deal with the deprecation warnings, and
have correct behavior, is to add in your products a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;configure.zcml&lt;/span&gt;&lt;/tt&gt;
file containing:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;configure
    xmlns=&amp;quot;http://namespaces.zope.org/zope&amp;quot;
    xmlns:five=&amp;quot;http://namespaces.zope.org/five&amp;quot;&amp;gt;

  &amp;lt;five:deprecatedManageAddDelete
      class=&amp;quot;Products.CoolProduct.CoolDocument.CoolDocument&amp;quot;/&amp;gt;

&amp;lt;/configure&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This tells Zope that you acknowledge that your class contains deprecated
methods, and ask it to still call them in the proper manner. So Zope
will be sending events when an object is added, for instance, and in
addition call your old &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt; method.&lt;/p&gt;
&lt;p&gt;One subtlety here is that you may have to modify you methods to just do
their work, and not call their super class. This is necessary because
proper events are already dispatched to all relevant classes, and the
work of the super class will be done trough events, you must not redo it
by hand. If you call the super class, you will get a warning, saying for
instance:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
CoolDocument.manage_afterAdd is deprecated and will be removed in
Zope 2.11, you should use an IObjectAddedEvent subscriber instead.
&lt;/pre&gt;
&lt;p&gt;The fact that you must &amp;quot;just do your work&amp;quot; is especially important for
the rare cases where people subclass the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt; of object
managers like folders, and decided to reimplement recursion into the
children themselves. If you do that, then there will be two recursions
going on in parallel, the one done by events, and the one done by your
code. This would be bad.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="using-subscribers"&gt;
&lt;h1&gt;&lt;a name="using-subscribers"&gt;Using subscribers&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;In the long run, and before Zope 2.11 where &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt; and
friends will be removed, you will want to use proper subscribers.&lt;/p&gt;
&lt;p&gt;First, you'll have to write a subscriber that &amp;quot;does the work&amp;quot;, for
instance:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def addedCoolDocument(ob, event):
    &amp;quot;&amp;quot;&amp;quot;A Cool Document was added to a container.&amp;quot;&amp;quot;&amp;quot;
    self.mangled_path = mangle('/'.join(self.getPhysicalPath()))
&lt;/pre&gt;
&lt;p&gt;Note that we're not calling the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;portal_cool&lt;/span&gt;&lt;/tt&gt; tool anymore, because
presumably this tool will also be modified to do its work through
events, and will have a similar subscriber doing the necessary
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;registerCool&lt;/span&gt;&lt;/tt&gt;. Note also that here we don't care about the event, but
in more complex cases we would.&lt;/p&gt;
&lt;p&gt;Now we have to register our subscriber for our object. To do that, we
need to &amp;quot;mark&amp;quot; our object through an interface. We can define in our
product's &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;interfaces.py&lt;/span&gt;&lt;/tt&gt;:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from zope.interface import Interface, Attribute

class ICoolDocument(Interface):
    &amp;quot;&amp;quot;&amp;quot;Cool Document.&amp;quot;&amp;quot;&amp;quot;
    mangled_path = Attribute(&amp;quot;Our mangled path.&amp;quot;)
    ...
&lt;/pre&gt;
&lt;p&gt;Then the class CoolDocument is marked with this interface:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from zope.interface import implements
from Products.CoolProduct.interfaces import ICoolDocument
class CoolDocument(...):
    implements(ICoolDocument)
    ...
&lt;/pre&gt;
&lt;p&gt;Finally we must link the event and the interface to the subscriber using
zcml, so in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;configure.zcml&lt;/span&gt;&lt;/tt&gt; we'll add:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
...
  &amp;lt;subscriber
      for=&amp;quot;Products.CoolProduct.interfaces.ICoolDocument
           zope.app.container.interfaces.IObjectAddedEvent&amp;quot;
      handler=&amp;quot;Products.CoolProduct.CoolDocument.addedCoolDocument&amp;quot;
      /&amp;gt;
...
&lt;/pre&gt;
&lt;p&gt;And that's it, everything is plugged. Note that IObjectAddedEvent takes
care of both &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterClone&lt;/span&gt;&lt;/tt&gt;, as it's sent
whenever a new object is placed into a container. However this won't
take care of moves and renamings, we'll see below how to do that.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="event-dispatching"&gt;
&lt;h1&gt;&lt;a name="event-dispatching"&gt;Event dispatching&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;When an IObjectEvent (from which all the events we're talking here
derive) is initially sent, it concerns one object. For instance, a
specific object is removed. The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;event.object&lt;/span&gt;&lt;/tt&gt; attribute is this
object.&lt;/p&gt;
&lt;p&gt;To be able to know about removals, we could just subscribe to the
appropriate event using a standard event subscriber. In that case, we'd
have to filter &amp;quot;by hand&amp;quot; to check if the object removed is of the type
we're interested in, which would be a chore. In addition, any subobjects
of the removed object wouldn't know what happens to them, and for
instance they wouldn't have any way of doing some cleanup before they
disappear.&lt;/p&gt;
&lt;p&gt;To solve these two problems, Zope 3 has an additional mechanism by which
any IObjectEvent is redispatched using multi-adapters of the form &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;(ob,&lt;/span&gt;
&lt;span class="pre"&gt;event)&lt;/span&gt;&lt;/tt&gt;, so that a subscriber can be specific about the type of object
it's interested in. Furthermore, this is done recursively for all
sublocations &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ob&lt;/span&gt;&lt;/tt&gt; of the initial object. The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;event&lt;/span&gt;&lt;/tt&gt; won't change
though, and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;event.object&lt;/span&gt;&lt;/tt&gt; will still be the original object for which
the event was initially sent (this corresponds to &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;self&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;item&lt;/span&gt;&lt;/tt&gt;
in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_afterAdd&lt;/span&gt;&lt;/tt&gt; method -- &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;self&lt;/span&gt;&lt;/tt&gt; is &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;ob&lt;/span&gt;&lt;/tt&gt;, and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;item&lt;/span&gt;&lt;/tt&gt; is
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;event.object&lt;/span&gt;&lt;/tt&gt;).&lt;/p&gt;
&lt;p&gt;Understanding the hierarchy of events is important to see how to
subscribe to them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;IObjectEvent is the most general. Any event focused on an object
derives from this.&lt;/li&gt;
&lt;li&gt;IObjectMovedEvent is sent when an object changes location or is
renamed. It is quite general, as it also encompasses the case where
there's no old location (addition) or no new location (removal).&lt;/li&gt;
&lt;li&gt;IObjectAddedEvent and IObjectRemovedEvent both derive from
IObjectMovedEvent.&lt;/li&gt;
&lt;li&gt;IObjectCopiedEvent is sent just after an object copy is made, but
this doesn't mean the object has been put into its new container yet,
so it doesn't have a location.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are only a few basic use cases about what one wants to do with
respect to events (but you might want to read the full story in
Five/tests/event.txt).&lt;/p&gt;
&lt;p&gt;The first use case is the one where the object has to be aware of its
path, like in the CoolDocument example above. That's strictly a Zope 2
concern, as Zope 3 has others ways to deal with this.&lt;/p&gt;
&lt;p&gt;In Zope 2 an object has a new path through creation, copy or move
(rename is a kind of move). The events sent during these three
operations are varied: creation sends IObjectAddedEvent, copy sends
IObjectCopiedEvent then IObjectAddedEvent, and move sends
IObjectMovedEvent.&lt;/p&gt;
&lt;p&gt;So to react to new paths, we have to subscribe to IObjectMovedEvent, but
this will also get us any IObjectRemovedEvent, which we'll have to
filter out by hand (this is unfortunate, and due to the way the Zope 3
interface hierarchy is organized). So to fix the CoolDocument
configuration we have to add:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def movedCoolDocument(ob, event):
    &amp;quot;&amp;quot;&amp;quot;A Cool Document was moved.&amp;quot;&amp;quot;&amp;quot;
    if not IObjectRemovedEvent.providedBy(event):
        addedCoolDocument(ob, event)
&lt;/pre&gt;
&lt;p&gt;And replace the subscriber with:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
...
  &amp;lt;subscriber
      for=&amp;quot;Products.CoolProduct.interfaces.ICoolDocument
           zope.app.container.interfaces.IObjectMovedEvent&amp;quot;
      handler=&amp;quot;Products.CoolProduct.CoolDocument.movedCoolDocument&amp;quot;
      /&amp;gt;
...
&lt;/pre&gt;
&lt;p&gt;The second use case is when the object has to do some cleanup when it is
removed from its parent. This used to be in &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;manage_beforeDelete&lt;/span&gt;&lt;/tt&gt;, now
we can do the work in a &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;removedCoolDocument&lt;/span&gt;&lt;/tt&gt; method and just
subscribe to IObjectRemovedEvent. But wait, this won't take into account
moves... So in the same vein as above, we would have to write:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
def movedCoolDocument(ob, event):
    &amp;quot;&amp;quot;&amp;quot;A Cool Document was moved.&amp;quot;&amp;quot;&amp;quot;
    if not IObjectRemovedEvent.providedBy(event):
        addedCoolDocument(ob, event)
    if not IObjectAddedEvent.providedBy(event):
        removedCoolDocument(ob, event)
&lt;/pre&gt;
&lt;p&gt;The third use case is when your object has to stay registered with some
tool, for instance indexed in a catalog, or as above registered with
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;portal_cool&lt;/span&gt;&lt;/tt&gt;. Here we have to know the old object's path to
unregister it, so we have to be called &lt;em&gt;before&lt;/em&gt; it is removed. We'll use
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IObjectWillBe...&lt;/span&gt;&lt;/tt&gt; events, that are sent before the actual operations take place:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from OFS.interfaces import IObjectWillBeAddedEvent
def beforeMoveCoolDocument(ob, event):
    &amp;quot;&amp;quot;&amp;quot;A Cool Document will be moved.&amp;quot;&amp;quot;&amp;quot;
    if not IObjectWillBeAddedEvent.providedBy(event):
        getToolByName(ob, 'portal_cool').unregisterCool(ob)

def movedCoolDocument(ob, event):
    &amp;quot;&amp;quot;&amp;quot;A Cool Document was moved.&amp;quot;&amp;quot;&amp;quot;
    if not IObjectRemovedEvent.providedBy(event):
        getToolByName(ob, 'portal_cool').registerCool(ob)
    ...
&lt;/pre&gt;
&lt;p&gt;And use an additional subscriber:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
...
  &amp;lt;subscriber
      for=&amp;quot;Products.CoolProduct.interfaces.ICoolDocument
           OFS.interfaces.IObjectWillBeMovedEvent&amp;quot;
      handler=&amp;quot;Products.CoolProduct.CoolDocument.beforeMoveCoolDocument&amp;quot;
      /&amp;gt;
...
&lt;/pre&gt;
&lt;p&gt;This has to be done if the tool cannot react by itself to objects being
added and removed, which obviously would be better as it's ultimately
the tool's responsibility and not the object's.&lt;/p&gt;
&lt;p&gt;Note that if having tests like:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
if not IObjectWillBeAddedEvent.providedBy(event):
if not IObjectRemovedEvent.providedBy(event):
&lt;/pre&gt;
&lt;p&gt;seems cumbersome (and backwards), it is also possible to check what kind
of event you're dealing with using:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
if event.oldParent is not None:
if event.newParent is not None:
&lt;/pre&gt;
&lt;p&gt;(However be careful, the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;oldParent&lt;/span&gt;&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;newParent&lt;/span&gt;&lt;/tt&gt; are the old and
new parents &lt;em&gt;of the original object&lt;/em&gt; for which the event was sent, not
of the one to which the event was redispatched using the
multi-subscribers we have registered.)&lt;/p&gt;
&lt;p&gt;The &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;IObjectWillBe...&lt;/span&gt;&lt;/tt&gt; events are specific to Zope 2 (and imported
from &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;OFS.interfaces&lt;/span&gt;&lt;/tt&gt;). Zope 3 doesn't really need them, as object
identity is often enough.&lt;/p&gt;
&lt;/div&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_11_08_events-in-zope-2-9</link>
    <dc:date>2005-11-08</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>zope</dc:subject>
    <dc:subject>zope3</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_10_28_cps-second-bug-day">
    <title>CPS Second Bug Day Wrapup</title>
    <description>&lt;p&gt;
This bug day was announced on short notice, we we still managed to kill 26 bugs (while 9 new were opened). That's good work!
&lt;/p&gt;
&lt;p&gt;
Because there are still 84 active tickets for CPS 3.4 (some are only details of course), we've decided to release a CPS 3.3.7 in the meantime, probably early next week. And we'll keep doing bug days, I propose to have them every Thursday until CPS 3.4 is finally released.
&lt;/p&gt;
</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_10_28_cps-second-bug-day</link>
    <dc:date>2005-10-28</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>cps</dc:subject>
    <dc:subject>zope</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_09_21_cps-3-3-6-released">
    <title>CPS 3.3.6 released</title>
    <description>&lt;p&gt;CPS 3.3.6 has just been released.&lt;/p&gt;
&lt;p&gt;You'll find download links in the "Quick Download" box of &lt;a href="http://www.cps-project.org"&gt;http://www.cps-project.org&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This release contains mainly bugfixes, and some speed improvements. It also bundles for the first time CPSSharedCalendar (successor to CPSCalendar) and CPSMailAccess (sucessor to CPSWebMail), two components heavily based on Five.&lt;/p&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_09_21_cps-3-3-6-released</link>
    <dc:date>2005-09-21</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>cps</dc:subject>
    <dc:subject>five</dc:subject>
    <dc:subject>zope</dc:subject>
    <dc:subject>zope3</dc:subject>

  </item>
  <item rdf:about="http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_09_16_cps-bug-day-wrapup">
    <title>CPS Bug Day Wrapup</title>
    <description>&lt;p&gt;
The CPS bug day this Wednesday was quite productive. A number of actual bugs were fixed, but more importantly a lot of bug triage and sorting happened, to determine what was urgent (going into CPS 3.3.6), what was necessary for the next stable CPS 3.4 release, and what could be postponed to a later development cycle (CPS 3.5). The &lt;a href="http://svn.nuxeo.org/trac/pub/timeline?from=09%2F14%2F05&amp;daysback=0&amp;ticket=on"&gt;Timeline for Wednesday&lt;/a&gt; show 65 bugs resolved (fixed or closed). In addition to these, several hundreds have been retargetted or cleaned up.
&lt;/p&gt;
&lt;p&gt;
I'm now confident that CPS 3.3.6 will get released within a week. Before CPS 3.4 we still have a number of important things to do though, so there may be a need for a CPS 3.3.7 which would serve as some kind of prerelease, we'll really need "in the wild" validating of some important features such as the CMFSetup-based installation and import/export tools.
&lt;/p&gt;
&lt;p&gt;
The schedule for the remaining work before CPS 3.4 is really conditionned by the availability of the developers. We still expect CPS 3.4 to be released around mid-october.
&lt;/p&gt;</description>
    <link>http://blogs.nuxeo.com/sections/blogs/florent_guillaume/2005_09_16_cps-bug-day-wrapup</link>
    <dc:date>2005-09-16</dc:date>
    <dc:creator>fguillaume</dc:creator>
    <dc:contributor>Florent Guillaume</dc:contributor>
    <dc:language>en</dc:language>
    <dc:subject>cps</dc:subject>
    <dc:subject>zope</dc:subject>

  </item>


  <xhtml:script id="js" type="text/javascript" src="http://blogs.nuxeo.com/rss.js" />

</rdf:RDF>
