Controlling creation

  By unfortunate design, CMF and Archetypes are not very good at dealing with object creation. When users use the 'add item' menu to add a content item, they are taken to what looks like an "add form", complete with "Save" and "Cancel" buttons, but in actual fact, they are editing an object that has already been created. If the user presses "Cancel" or navigates away from the edit form, they will leave behind an empty object.
  
To get around this problem, Plone ships with 'portal_factory', a clever abomination of a hack (thanks Geoff) that creates the object in a temporary folder and only moves it to the real folder when it is first saved.

Turning on 'portal_factory' is easy. In Install.py, we have::

    # Enable portal_factory
    factory = getToolByName(self, 'portal_factory')
    types = factory.getFactoryTypes().keys()
    if 'RichDocument' not in types:
        types.append('RichDocument')
        factory.manage_setPortalFactoryTypes(listOfTypeIds = types)
        
Notice that we don't bother with 'ImageAttachment' or 'FileAttachment'. 'portal_factory' only matters to types created by users through the web. The upload controls in the images and attachments manager widgetes are mini add-forms in themselves, and will guarantee the consistency of the objects created inside the RichDocument.

Unfortunately, 'portal_factory' does not deal very well with a folderish item (such as a RichDocument) being populated (with image and file attachments) before it has been created. Therefore, the scripts 'widget_imagesmanager_upload' and 'widget_attachmentsmanager_upload' both call 'portal_factory.doCreate()' to instantiate the objects as soon as an image or attachment is uploaded. Hopefully this is not too much of a problem, since the most common case for aborting the creation of an object is that the object was created in error.

Generating short names from titles

 New in Plone 2.1 is the rename-after-creation hook. By default, content types do not show the 'id' field ("Short name"). Instead, when they are saved, they will be renamed to a standardised short name generated from the title.
 
Turning this feature on is very easy. Just put in your class::

 _at_rename_after_creation = True
 
If you need more fine-grained control over how titles are generated, you can re-define the '_renameAfterCreation()' method from 'Archetypes/BaseObject.py'::

    security.declarePrivate('_renameAfterCreation')
    def _renameAfterCreation(self, check_auto_id=False):
        """Renames an object like its normalized title.
        """
        plone_tool = getToolByName(self, 'plone_utils', None)
        if plone_tool is None or not shasattr(plone_tool, 'normalizeString'):
            # Plone tool is not available or too old
            # XXX log?
            return None

        title = self.Title()
        if not title:
            # Can't work w/o a title
            return False

        old_id = self.getId()
        if check_auto_id and not self._isIDAutoGenerated(old_id):
            # No auto generated id
            return False

        new_id = plone_tool.normalizeString(title)
        invalid_id = False
        check_id = getattr(self, 'check_id', None)
        if check_id is not None:
            invalid_id = check_id(new_id, required=1)
        else:
            # If check_id is not available just look for conflicting ids
            parent = aq_parent(aq_inner(self))
            invalid_id = new_id in parent.objectIds()

        if not invalid_id:
            # Can't rename without a subtransaction commit when using
            # portal_factory!
            get_transaction().commit(1)
            self.setId(new_id)
            return new_id

        return False

The method 'plone_utils.normalizeString()' is a standard way of ensuring that a string is usable as an id (e.g. for an object, a CSS class or an anchor name). Notice also the check for '_isIDAutoGenerated()' and the call to 'check_id()'. These ensure that only auto-generated ids get renamed, and that the new id is valid and unique, before proceeding.

RichDocument uses the default title-to-id generation to be consistent with the standard Document/Page type. Another common use case, however, is to sequentially number items in a folder. The "Poi":http://plone.org/products/poi issue tracker uses this to sequentially number issues in a tracker::

    def _renameAfterCreation(self, check_auto_id=False):
        parent = self.aq_inner.aq_parent
        maxId = 0
        for id in parent.objectIds():
            try:
                intId = int(id)
                maxId = max(maxId, intId)
            except (TypeError, ValueError):
                pass
        newId = str(maxId + 1)
        # Can't rename without a subtransaction commit when using
        # portal_factory!
        get_transaction().commit(1)
        self.setId(newId)

Because this does not need to do as many checks, it is also much simpler.