[Zope-CMF] Re: Add forms and menus

Robert Niederreiter rnix at squarewave.at
Tue Jul 15 04:38:22 EDT 2008


Hi,

Am Dienstag, den 15.07.2008, 08:53 +0100 schrieb Martin Aspeli:
> Hi Robert,
> 
> >>> imo its a bad idea to depend on static zcml configuration for factory
> >>> types. martin did a nice approach in his portlets engine with a name
> >>> traverser when calling a generic adding view. 
> >> I'm not quite sure I follow here. The portlets machinery just looks up 
> >> the add view in a utility that stores its name, and then invokes it. 
> >> There's a custom analog to IAdding called "+portlet" to keep the 
> >> namespace separate.
> > 
> > you post i.e. /++contextportlets++plone.rightcolumn/+/portlets.Login as
> > action for adding a portlet and let your ITraversable implementations
> > perform what to do in plone.app.portlets.browser.traversal.py.
> > 
> > thats imo a nice approach
> 
> Ah, I get you. Actually, the ++contextportlets++plone.rightcolumn bit is 
> a namespace traversal adapter that addresses a particular portlet 
> manager (which is basically an ordered container); + is an IAdding view 
> (actually, an IPortletAdding view) registered for the portlet manager 
> container. portlets.Login is the name of the add view for a particular 
> portlet.
> 
> So, this approach is identical to (and borrowed from) the "old" Zope 
> 3/ZMI approach that you have an add view that is a statically registered 
> view for IAdding. The adding view is *not* generic. Each portlet 
> registers its own add view. We have a formlib-based base class though.

i know, but the fact that portlets have it's own add view has nothing to
do with the fact that the traverser is responsible for the magic.

> 
> Now, I think this is fine for portlets, since it's relatively easy to 
> register this add view (there's a single ZCML directive to register all 
> portlet-related information), and portlets are not like portal types 
> (there's no persistent FTI that can be cloned).
> 
> >>> i took this idea and the
> >>> adding mechanism of devilstick works this way as well and depends on the
> >>> fti too. so a call of foo/add/portal_type returns an add view for
> >>> requested type.
> >> How's that different to foo/+/<factory-name> ?
> > 
> > not that much. i only wanted to say that there might be no need to
> > register a seperate addview/form for every portal type. having the type
> > name it should be possible to get the schema interface of the requested
> > type, so it's possible to provide a generic addview/form.
> 
> Right. That's probably a reasonable default (and is, in effect, what 
> Dexterity does as well, although it registers add views as local adapter 
> factories that "know" their portal_type).
> 
> > this interface lookup, and addview/form instanciation might be done then
> > in a traverser, that's the most 'zopeish' solution imo.
> 
> This is quite an interesting approach, actually. After traversal, what 
> is self.context in the add form? Is it the form, or the 'addview' 
> traverser thing?

depends on what your traverser returns :).

consider such an url and the default formlib behaviour:

foo/+/Folder

'+' is the IAdding implementation, which is actually nothing else than a
'factory', but without creating anything like the old behaviour of the
portal_factory.

now it's possible to register an IPublishTraverse implementation for
this specific IAdding implementation (could also be anything else than
IAdding if you want to get rid of it). this traverser then does the FTI
lookup, the schema interface lookup und creates and returns the addform.

in this step you can modify the context of addform as needed.

here is how its done in devilstick:
http://dev.plone.org/collective/browser/devilstick/devilstick.browser/trunk/devilstick/browser/traversal.py
line 71+

so, to follow your intention, there would be some browserpage altering
the factory.

for this factory then an IPublishTraverse implementation is registered.

inside the traverser you can do something like

context = aq_inner(self.context.context)
form = getMultiAdapter((context, self.request),
                       IMyFancyAddFormWithoutIAdding,
                       name='whatever')
return form.__of__(context)

this ensures the right context in the right acquisition chain.

> 
> >> Having the add view be a view for a view (i.e. the context of the real 
> >> add view is not a content object) is sometimes quite painful.
> > 
> > until someone got the clue :). yes you're right here, constructs like
> > ``aq_inner(self.context.context)`` and similar simply look ugly. but on
> > the other hand, if you kick this construct, you have to provide another
> > mechanism which is responsible to finally add what has to be added. if
> > this is more elegant then...?
> 
> The final 'add' operation can be done by a base class for the view. 
> That's how Yuppie's formlib thing works, and how z3c.form prefers to work.
> 
> self.context.context can be majorly painful, though. For example, look 
> at 
> http://dev.plone.org/plone/browser/plone.app.vocabularies/trunk/plone/app/vocabularies/workflow.py.
> 
> Here, we need to acquire something, but since the context may be the 
> IAdding view, we have to do this everywhere:
> 
>   context = getattr(context, 'context', context)

right, but this is a cosmetic thing. nothing that really should be named
'painful'.

> 
> Yuck!
> 
> Martin
> 

Robert




More information about the Zope-CMF mailing list