Having thought about this a bit more ...

I don't really see why you need a traverser *unless* you're trying to have a single add form implementation that covers multiple types.
i.e. if you have one content type, i.e. a folder, but you want to use
exactly this type with different workflows, names, icons, then this
makes indeed sence.

here you might simply add another fti, and its done. adding such a type
is then either invoked like @@add/Folder or @@add/AnotherFtiForFolder,
but both return the same form.

Right, I get the need now.

You may of course have that, and maybe it's helpful to let people write that, but I think most people would prefer to write "plain" add views that use the standard z3c.form patterns.
its possible anyway, isn't it? the advantage is that there's one way how
adding works in general. and the discussion is still about implementing
a generic adding mechanism in CMF. as i pointed in a previous post,
there should be the possibility to do customization. so as convention it
might be done this way.

* lookup fti for ``portal_type``

* have a look if theres a custom view set.

* if so, do lookup with this name

* if not, try lookup with ``portal_type`` as name

* finally do general lookup if others failed.

thats also why i tried to introduce IFormFactory, because the traverser
might not need to know too much. but thats maybe a bit too far...?

That feels like a lot of hoops, but maybe not.

I wouldn't necessarily try to create a generic add form that is a fallback for all types. Even if CMFDefault chooses to generalise its forms, that's not likely to work for other implementations. Rather, then, each CMFDefault FTI may choose to use the same form registration.

For something like Dexterity, where we explicitly want to support "generic" content with a schema that varies according to runtime configuration, this is more of an issue. But even there, the intention is that whilst the framework has a few hooks like this so that it works with content that's more malleable, it doesn't force you to use unconventional patterns if you do something yourself on the filesystem.
the goal should be the various IFormFactory hooks, so you might not need
to change the way you write addforms in general, but to provide a
specific IFormFactory implementation for a specific framework.
(dexterity, devilstick, archetypes, whatever).

as an alternative the magic could be done in the traverser directly, but
then there must be different traversers for each framework and different
'add' browserpages where those traversers could be bound to. this would
then look like this for invoking:


which of them to call in the add dropdown must be stored then i the fti.

I think we should at least make this *possible*, in that I don't think we should hardcode the dependency on an ../@@add/<FTI> name. That should be the result of a TALES expression that may by convention look like:


or whatever (here I'm assuming we add a convenience variable for portal_type to the TALES expression context so that people don't have to remember to change the portal type if they copy and paste the expression between types).

In the case above, you end up having to register your form as a particular adapter rather than a browser view. That's fairly unnatural, and also doesn't necessarily deal with things like security settings. It makes the add view quite different to write than the edit view, too.
all the forms can be registered as browserpages anyway (and should!).
there's no difference if you call it via the browser, or get it inside
the traverser with getMultiAdapter and return this one. even the
security settings are considered then imo.

So, let me try to summarise what I think we're saying here:

 - My type has a form like:

class MyAddForm(CMFBaseAddForm):
    fields = form.Fields(IMyType)
    portal_type = 'My type'

- The base form knows to look at self.factory_name to look up the factory when it does the create() call.

 - The base add form implements ICMFAddForm

- I register the form as a normal <browser:page />, with the convention that the name is the same as the factory name

- The FTI has an 'addview' property, which by convention is set to string:${folder/absolute_url}/@@add/${portal_type}

 - The @@add view looks like

class AddView(BrowserView):

    def publishTraverse(self, request, name):
        portal_types = getToolByName(self.context, 'portal_types')
        fti = getattr(portal_types, name)
        factory = fti.factory
        addview = getMultiAdapter((self.context, request), ICMFAddForm,
        addview.portal_type = name
        return addview

A few things to note about this:

- The traverser doesn't call the view, it just returns it (the publisher will call it when it needs to)

- We don't look up a default, unnamed add form view. This doesn't make any sense unless we really can generalise all forms; frameworks like Dexterity may have a way to do this and thus may be able to have their own versions of @@add, but I don't think this something we should do at the CMF level.

- This doesn't require any more registrations than the simple add form browser view.

- If I don't want to use this idiom, I could change that TALES expression to something like string:${folder/absolute_url}/@@add-my-stuff

I quite like this approach now. ;-)


Zope-CMF maillist  -  Zope-CMF@lists.zope.org

See http://collector.zope.org/CMF for bug reports and feature requests

Reply via email to