========================
Normalizing name chooser
========================

plone.app.content provides a namechooser for IFolderish objects that can
pick a normalized name based on the object's id, title, portal type or class, 
and can provide uniqueness.

Let's create some dummy content.

    >>> from plone.app.content import container, item
    >>> from zope.interface import implements, Interface, alsoProvides
    >>> from zope import schema
    
    >>> class IMyContainer(Interface):
    ...     title = schema.TextLine(title=u"My title")
    ...     description = schema.TextLine(title=u"My other title")

    >>> class MyContainer(container.Container):
    ...     implements(IMyContainer)
    ...     portal_type = "My container"
    ...     title = IMyContainer['title']
    ...     description = IMyContainer['description']

    >>> class IMyType(Interface):
    ...     title = schema.TextLine(title=u"My title")
    ...     description = schema.TextLine(title=u"My other title")
    
    >>> class MyType(item.Item):
    ...     implements(IMyType)
    ...     portal_type = "My portal type"
    ...     title = IMyType['title']
    ...     description = IMyType['description']

    >>> container = MyContainer("my-container")

Then wire up the name chooser (this is normally done in this package's
configure.zcml file).

    >>> from zope.component import adapts, provideAdapter, provideUtility
    >>> from Products.CMFCore.interfaces import IFolderish
    >>> from plone.app.content.namechooser import NormalizingNameChooser
    >>> provideAdapter(adapts=(IFolderish,), factory=NormalizingNameChooser)

We also need to wire up some adapters from plone.i18n that are used to
normalise URLs.

    >>> from zope.publisher.interfaces.http import IHTTPRequest
    >>> from plone.i18n.normalizer import urlnormalizer
    >>> from plone.i18n.normalizer.adapters import UserPreferredURLNormalizer
    >>> provideUtility(component=urlnormalizer)
    >>> provideAdapter(factory=UserPreferredURLNormalizer, adapts=(IHTTPRequest,))

Choosing names based on id
---------------------------

By default, the namechooser will choose a name based on the id attribute of
an object, if it has one.

    >>> from zope.container.interfaces import INameChooser
    >>> chooser = INameChooser(container)

    >>> item = MyType("my-item")
    >>> item.id
    'my-item'
    
    >>> name = chooser.chooseName(None, item)
    >>> name
    'my-item'
    >>> chooser.checkName(name, object)
    True

If we add it to the container and try again, we'll get a name that's made
unique.

    >>> container[name] = item

    >>> item = MyType("my-item") # a distinct object, but with the same id
    >>> name = chooser.chooseName(None, item)
    >>> name
    'my-item-1'
    >>> chooser.checkName(name, object)
    True

The uniqueness applies also if we pass a name in, in which case it will not
be obtained from the id (or portal type or class or title)

    >>> item.id = "another-id"
    >>> chooser.chooseName("my-item", item)
    'my-item-1'

When a filename is used as an id, the extension is preserved.

    >>> item = MyType("file.txt")
    >>> name = chooser.chooseName(None, item)
    >>> name
    'file.txt'
    >>> chooser.checkName(name, object)
    True
    >>> container[name] = item
    >>> item = MyType("file.txt") # a distinct object, but with the same id
    >>> name = chooser.chooseName(None, item)
    >>> name
    'file-1.txt'
    >>> chooser.checkName(name, object)
    True

If the chooser is used with a container that implements the
IObjectManager interface from OFS, the checkValidId method
of that interface will be used to check for validity of the
chosen name. This catches various edge cases.

    >>> from OFS.ObjectManager import ObjectManager
    >>> om = ObjectManager()
    >>> om.title = 'foo'
    >>> alsoProvides(om, IFolderish)
    >>> chooser2 = INameChooser(om)
    >>> chooser2.chooseName('title', item)
    'title-1'


Choosing names based on type
----------------------------

If we did not have an id, the namechooser would use the portal_type, falling
back on the class name.

    >>> delattr(item, 'id')
    >>> chooser.chooseName(None, item)
    'my-portal-type'
    
    >>> delattr(MyType, 'portal_type')
    >>> chooser.chooseName(None, item)
    'mytype'

Title-based name chooser
------------------------

An object can also gain a name based on its title. To do so, the object
must implement or be adaptable to INameFromTitle.

    >>> from plone.app.content.interfaces import INameFromTitle
    
    >>> class TitleAdapter(object):
    ...     implements(INameFromTitle)
    ...     adapts(IMyType)
    ...     def __init__(self, context):
    ...         self.context = context
    ...     @property
    ...     def title(self):
    ...         return self.context.title
    >>> provideAdapter(TitleAdapter)
    

    >>> item = MyType("some-id")
    >>> item.title = u"My funky item"
    >>> chooser.chooseName(None, item)
    'my-funky-item'