Current job: Enter phase 2

August 23, 2010

I’ve now spent 10 months in my current job, and I owe to myself to stop today for a quick restrospective before moving to another phase.

The first period (phase 1) helped me understand the context and what can be done to bring value. Fortunately, a lot can be done, so the challenge is interesting. Also, I was lucky to quickly understand and accept that, in the current context, change could only happen progressively.

Saying that nothing interesting has happened so far with the projects I am in charge of, would not be true. But the whole process is too slow (or sometimes chaotic), for unnecessary reasons.

My phase 2 is going to involve more initiatives to pick up the pace on the projects and get things moving. Wish me good luck or my next related post will be about asking if someone has a job opportunity for me ūüėČ

As for other news, I restarted developing for personal projects with my favourite CMS (which is preparing a new release). I wish all success to Plone 4 and the community. If you need a CMS for a critical/complex site project, Plone is undoubtly in the top 2 or 3. Also, we have a great community which opens to various technologies and development practices besides Plone such as Zope Toolkit, ZODB, Deliverance, deco.gs, and BFG, to only name a few.


Scratching my own documentation itch

March 21, 2009

A week ago, I finished updating the Archetypes Developer Manual for Plone 3. In case you have not seen it, the new version is interesting for any developer who has to develop or update an Archetypes-based product ; it tries to present our current coding practices (GenericSetup, ZCA techniques…) The manual is accompanied by a new example package. It is also useful, directly, for the “what does what/how to” when using the ZopeSkel “archetype” template with paster to bootstrap your product package.

What’s next ?

I started another manual to give an overview of the Zope Component Architecture techniques and tools we use for Plone development. Part of the initial work is refactoring/merging existing howtos and tutorials written by the other documentation contributors, and updating them. Hopefully, a first cut of this manual will be reviewed and published in a few weeks.

Sprinting at Pycon

Next week, I am going to Pycon in Chicago and will have 2 days to participate to the post-conference sprints. My current plan is to just dedicate my time to documentation. Topics that I may start contributing on are “Dexterity content types” and “WSGI setup for Plone (3/4) + using middlewares to augment your deployment”. There may be other topics to give priority to, and I will discuss with others to find out. And of course, I will have some time to make progress on the ZCA manual.

Looking forward to meet other “Python Web” community members there!


Promoting Python and Plone in Africa

November 7, 2008

It seems that only South Africa had an event listed as part of the World Plone Day set of local events. As an african, I am of course interested by this fact, and I would have expected some Plone presence in another region. Hopefully, we can fix that for next year’s edition !

I am promoting an effort called Python African Tour which aims at sending volunteers, within the next couple of years, in the different regions of Africa, based on sponsoring, to train beginner developers there on Python and its related technologies. It’s a way to introduce newbies there to a programming language that helps the developer get his job done, as well as all the community practices that help us improve our daily work. It’s also a way to get new developers join the Python community.

The first country the tour will visit is Morocco, in December, from the 18th to the 22nd. Among the possible countries to plan in 2009 are Nigeria and South Africa. Obviously our plans will depend on discussions with local contacts we get, and sponsoring possibilities.

For Morocco, Amine Soulaymani, a developer living in Morocco, and Daniel Nouri have volunteered to participate as instructors for the students at Ecole Mohammedia d’Ingenieurs, ¬≠the school that will host the Python training session.
In addition to the training session during the first 2 days, we plan to have 3 days of community activities: an unconference-style open event with demos and talks related to Python, followed by a sprint that will be hosted in the offices of Nextma, a solution provider doing Python. Talks and sprint activities should cover Plone, with the participation of a PloneGov / CommunesPlone team joining us from Belgium, WSGI / Repoze and ­OpenERP with contributors from Nextma.

On a side note, I have proposed a talk with Roberto Allende for next year’s Pycon to present our ideas and actions to help spread Python in both South America and Africa.

If you want to contribute in any way, ¬≠¬≠contact us through the project’s mailing list.


Not at the Plone conference

October 7, 2008

Unfortunately, I was unable to go to DC for the Plone conference.

I want to apologize to the people who were expecting to follow the tutorial on z3c.form, which had to be canceled.

I am thinking about submitting an improved version of that tutorial proposal for Pycon 2009, and I hope to be there and meet some of you.

Have fun, and let the people know it ! Zope is back (in various forms ;)), and we are going toward more successes in the coming years.


More on using the EditForm

September 17, 2008

I now better understand this after reading z3c.form’s doctests: If the context object that you want to edit fulfills the interface used for your EditForm, the code for defining the form class could be as small as this (followed by the usual configure.zcml snippet):

class ContactEditForm(z3c.form.form.EditForm): """ Contact Edit Form """ fields = z3c.form.field.Fields(IContactData) # For wrapping the form presentation within Plone's layout ContactEditFormView = layout.wrap_form(ContactEditForm, label="Edit Form")

If it does not, you get an error as the framework finds that the object does not match the schema or there is not an adapter to it.

The solution in such a case is to define an adapter to the interface, and override the getContent() method of the form to give the adapted object to the EditForm so it can do its job on it, i.e. update it.

Here is an example with an ATDocument used as the context.

class ContactAdapter(object): zope.component.adapts(IATDocument) zope.interface.implements(IContactData) def __init__(self, document): self.document = document @apply def lastname(): def get(self): return getTheLastname(self.document) def set(self, value): setTheLastname(self.document, value) return property(get, set)
class ContactEditForm(z3c.form.form.EditForm): """ Contact Edit Form """ fields = z3c.form.field.Fields(IContactData) def getContent(self): '''See interfaces.IForm''' return IContactData(self.context)

This approach is valid for cases where you don’t want to introduce a new content type. Hopefully, there will be a time when introducing a new type would be easier, not require too much things to change, and not add complexity to your Plone site with the risk of maintenance issues. (I’m thinking plone.dexterity here.) Then, things should be straightforward.

You can find the detailed code of my z3c.form usage examples in the
example.z3cform package in the Collective.

Update: Do not forget to register the Adapter in the configure.zcml. (Thx to Gilles for informing me that I forgot to mention this bit.)

<adapter factory=‚ÄĚ.adapter.ContactAdapter‚ÄĚ />

Example code for getting started with z3c.form forms

September 13, 2008

I have been working on an example package to show how to get started with z3c.form-based forms in Plone, and this helps me prepare myself for the tutorial session I will lead at the Plone conference.

I just updated the code, to end my day at the Paris Bobun Sprint, by adding a technique you can use – based on a feature of the framework – when you want to provide your own specific error message on a field’s validation (a common need, when you are improving a form’s usability for the users). I will explain this feature in detail in a next post.

My tentative TODO list for the next days/weeks is:

  • Write tests
  • An example of custom widget
  • An example of data converter (maybe)
  • An example of using AjaX (jQuery, KSS) to improve the forms’s usability

You can check the code in the collective (https://svn.plone.org/svn/collective/example.z3cform/trunk/) and send me feedback, suggestions, or questions.

Enjoy !


Updating your form code to latest version of plone.z3cform

July 26, 2008

I am glad to see more work is happening for using z3c.form in CMF and Plone, and Daniel Nouri updated us this week with the latest changes. I just updated my buildout to use the new packages and got plone.z3cform 0.4 and plone.app.z3cform 0.3.2.

With the small API changes that happened, you can see below that there is less to write to expose the form in the Plone site (our ContactForm example from my
previous post):

# my.example/my/example/browser.py import datetime from zope import schema import zope.component import z3c.form import plone.app.z3cform from plone.app.z3cform import layout from plone.i18n.normalizer.interfaces import IIDNormalizer from my.example import interfaces from my.example.contact import MyContact class ContactForm(z3c.form.form.Form): """ Contact Form """ fields = z3c.form.field.Fields(interfaces.IContactData) message_field = z3c.form.field.Fields(schema.TextLine(__name__ = 'message', title=u"Message", required=False) ) fields += message_field ignoreContext = True @z3c.form.button.buttonAndHandler(u'Send', name='send') def handle_send(self, action): data, errors = self.extractData() if errors: self.status = z3c.form.form.EditForm.formErrorsMessage return # Add the contact data, if not in yet id = data['firstname'] + data['lastname'] id = zope.component.queryUtility(IIDNormalizer).normalize(id) if id not in self.context.objectIds(): self._name = id contact = MyContact(self._name, **data) self.context[self._name] = contact # Complete the code so it sends the message to the site admin... message = data['message'] print "Message from %s %s (%s):\n%s" % (data['firstname'], data['lastname'], data['email'], data['message']) self.request.RESPONSE.redirect(self.context.absolute_url()) # New way to provide the wrapping View ContactFormView = layout.wrap_form(ContactForm, label="Contact Form")

The related ZCML code does not change.
By the way, on my TODO list, is moving from ZCML to grokcore.component registration ūüėČ


A variation on my previous z3c.form example

July 19, 2008

Another example of form logic, adding to the one discussed in my previous post.

Here is the idea: If you need a more flexible solution for your use case, you can inherit directly from z3c.form.form.Form.
Let’s say you want the form to be used for both adding contact information (making sure later that this only works when the user is authenticated) and sending a mail message to the site administrator. A site feedback form that will at the same time be used to populate your database of contacts.

Your form class could be defined as follows (I’m skipping the imports part):

# my.example/my/example/browser.py class ContactForm(z3c.form.form.Form): """ Contact Form """ fields = z3c.form.field.Fields(interfaces.IContactData) message_field = z3c.form.field.Fields(schema.TextLine(__name__ = 'message', title=u"Message", required=False) ) fields += message_field ignoreContext = True @z3c.form.button.buttonAndHandler(u'Send', name='send') def handle_send(self, action): data, errors = self.extractData() if errors: self.status = z3c.form.form.EditForm.formErrorsMessage return # Add the contact data, if not in yet id = data['firstname'] + data['lastname'] id = zope.component.queryUtility(IIDNormalizer).normalize(id) if id not in self.context.objectIds(): self._name = id contact = MyContact(self._name, **data) self.context[self._name] = contact # Do something else, e.g. send the message to the site admin. # Complete the code as needed... message = data['message'] print "Message from %s %s (%s):\n%s" % (data['firstname'], data['lastname'], data['email'], data['message']) # Redirect self.request.RESPONSE.redirect(self.context.absolute_url())

The main points of the used pattern:

  • You define the fields for the form ; new fields can be added to the already defined list (based on the IContactData schema in interfaces.py) using the “+” operator.
  • You define your specific form button(s) and handler method(s). In the case of a standard AddForm, there is already a handle_add() defined for the “add” button for you.
  • Also, you need to set the form attribute ‘ignoreContext’ to True, so that the form has the same behaviour as an AddForm, i.e. it does not have to get/set data on attributes on the context. Note that by default, an AddForm has this attribute set to True, and an EditForm has it set to False.

Now, the final touch with the wrapping view…

class ContactFormView(base.FormWrapper): form = ContactForm label= "Contact Form"

… and its configuration:

<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:five="http://namespaces.zope.org/five" i18n_domain="my.example" > <browser:page for="Products.CMFPlone.Portal.PloneSite" name="contact_form" class=".browser.ContactFormView" permission="zope2.View" /> </configure>

Using z3c.form for our forms in Plone

July 16, 2008

I’ve been developing complex forms for a Plone project these days, and I get the job done with z3c.form, the form framework that was first used in the Plone world by the Singing & Dancing project.
Here is the first episode of a small serie to show interesting tips & tricks and patterns that I’ve been learning in the process.

To get started, install your buildout using fake eggs and the required dependencies (plone.z3cform, z3c.form…) You might want to follow the howto contributed by Daniel Nouri on plone.org.

(Note: The code snippets are simplified for easier reading.)

First things first !

z3c.form’s first concept, as you guess, is the “form”, which basically has the list of fields defined for it using an attribute called ‘fields’. From the form, it is also possible to access the list of field widgets using the ‘widgets’ attribute.
The framework is smartly engineered using Zope Component Architecture, so you have “separation of concerns” in every bit, and works with zope.schema for the fields definition and validation.

Note that, at least with the core, you can define a basic Form or a specific Add/Edit/DisplayForm, and other cases such as Group (a group of fields part of a Form) and Subform.

To define the list of fields for a form class, we must provide a schema (for example, IContactData), which I like to think of as the “data model” specification.

# my.example/my/example/interfaces.py from zope.interface import Interface, Invalid, invariant from zope import schema class IContactData(Interface): """Contact data interface """ firstname = schema.TextLine(title=u"Firstname", required=True) lastname =schema.TextLine(title=u"Lastname", required=True) email =schema.TextLine(title=u"Email", required=False) @invariant def email_format(obj): if obj.email.find('@') == -1: raise Invalid(u"Not a valid email")

Defining a storage… if you need to

Since, we generally need to store something, let’s choose the data storage. Though you could choose to do that later.
There are many options (including RDB-based), but the immediate one for us is using the ZODB.
The most simple way to do that, in most real-world apps, is by defining an object class that brings persistency, traversal, security and all the goodies we get in Zope for “free”, i.e. by inheriting from OFS.SimpleItem.SimpleItem (or OFS.Folder.Folder if we want containment) and defining the attributes it needs.

# my.example/my/example/contact.py from zope import interface from zope.schema.fieldproperty import FieldProperty import OFS from my.example.interfaces import IContactData class MyContact(OFS.SimpleItem.SimpleItem): """Contact model class, with ZODB-based storage. """ interface.implements(IContactData) firstname = FieldProperty(IContactData['firstname']) lastname = FieldProperty(IContactData['lastname']) email = FieldProperty(IContactData['email']) def __init__(self, id, **kw): self.id = id for key, value in kw.items(): setattr(self, key, value) super(MyContact, self).__init__(id) @property def title(self): return "%s %s" % (self.firstname, self.lastname)

What I like in this pattern: It’s simple, it’s pythonic ! And it does the job for most cases where we don’t need a full-featured Plone content type.
Of course, if we need to manage a full-featured content type, we can inherit from plone.app.content base classes and bring the required Plone mechanisms on the table.

For those who are new to zope.schema, the Field Property mechanism, is very handy too. It does the job of providing data validation based on the information found in the schema.

Defining an “add” form for our Contact objects

Now the really new stuff starts !

An AddForm (and EditForm) could be used by a Content Manager to maintain a list of contacts in a folder within the site. You do that by inheriting from z3c.form.form.AddForm and providing your create(), add() and nextURL() methods.

# my.example/my/example/browser.py import datetime from zope import schema import zope.component import z3c.form from plone.z3cform import base from plone.i18n.normalizer.interfaces import IIDNormalizer from my.example import interfaces from my.example.contact import MyContact class ContactAddForm(z3c.form.form.AddForm): """ Contact Add Form """ fields = z3c.form.field.Fields(interfaces.IContactData) def create(self, data): id = data['firstname'] + data['lastname'] id = zope.component.queryUtility(IIDNormalizer).normalize(id) self._name = id contact = MyContact(self._name, **data) return contact def add(self, obj): # Add the object within context, and persist it ! context = self.context context[self._name] = obj def nextURL(self): return "%s/%s" % (self.context.absolute_url(), self._name)

There is one last thing to do, to make sure that our form can be rendered via Plone’s presentation machinery like any other page ; we define a special View by inheriting from the FormWrapper class provided by the plone.z3cform package, as follows :

# my.example/my/example/browser.py class ContactAddFormView(base.FormWrapper): form = ContactAddForm label= "Contact Add Form"

And don’t forget to add the configuration for this in the right ZCML file, something along the lines of:

<configure xmlns="http://namespaces.zope.org/zope" xmlns:browser="http://namespaces.zope.org/browser" xmlns:five="http://namespaces.zope.org/five" i18n_domain="my.example" > <browser:page for="Products.CMFPlone.Portal.PloneSite" name="contact_add_form" class=".browser.ContactAddFormView" permission="cmf.AddPortalContent" /> </configure>

Update: Of course, we render our form after restarting Zope, via the URL http://plonesite/@@contact_add_form.

Of course, in a real case you would use another context for the container, by affecting the right interface to the ‘for’ attribute in the configure.zcml, instead of the ‘Products.CMFPlone.Portal.PloneSite’ value.


Updating an old AT-based product for Plone 3 – Step 1: Using GenericSetup

March 3, 2008

I am spending some time to update the Archetypes Developer Manual on plone.org/documentation, so I can move forward in contributing on other stuff in the Doc Team tasks list.

If you are also in the situation of updating some old AT-based product for Plone 3, this might be useful to you.

The current version of our manual, in the part discussing a sample AT product called InstantMessage, is missing:

  • GenericSetup profile for setting types and subskins. This has been missing for a long time, so I decided to update it immediately.
  • ZCML-based registration for things such as FS Directory Views or class permissions and factory, which the current version of CMF permits.

I started updating the “InstantMessage” package’s code in this branch.

What was needed: Mainly replacing the old ‘Extensions/Install.py’ code by the needed GenericSetup profile XML files:

  • for content types:
  • profiles/default/types.xml
  • profiles/default/types/InstantMessage.xml
  • profiles/default/factorytool.xml
  • for the sub-skin: profiles/default/skins.xml

Then, you need a piece of code for loading the GenericSetup profile. You do that, nowadays, by adding a configure.zcml file at the root of the package with the following code:

<configure xmlns="http://namespaces.zope.org/zope"

       xmlns:genericsetup="http://namespaces.zope.org/genericsetup"

       i18n_domain="instantmessage" >

<genericsetup:registerProfile

       name="InstantMessage"

       title="InstantMessage"

       directory="profiles/default"

       description="Extension profile for InstantMessage sample AT content type."

       provides="Products.GenericSetup.interfaces.EXTENSION"

      />

</configure>

I think I can now update the manual to reflect the new setup code – pointing to a couple of GS-related tutorials that already exist on plone.org, and merge the updated branch of the code with the trunk so people can get this new version.

Next step: Introduce other ZCML-based registrations in the CMF/AT context. This update could wait for the “versions” feature support on plone.org so we add these details without loosing the text related to the old way of doing.