========================
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 implementer, Interface, alsoProvides
    >>> from zope import schema

    >>> class IMyContainer(Interface):
    ...     title = schema.TextLine(title=u"My title")
    ...     description = schema.TextLine(title=u"My other title")

    >>> @implementer(IMyContainer)
    ... class MyContainer(container.Container):
    ...     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")

    >>> @implementer(IMyType)
    ... class MyType(item.Item):
    ...     portal_type = "My portal type"
    ...     title = IMyType['title']
    ...     description = IMyType['description']

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

Allow anyone to access the contents information on the container.
This allows to check for existing content with the same id.

    >>> container.manage_permission('Access contents information', ['Anonymous'], acquire=1)

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

    >>> from zope.component import adapter, 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.
We need to force an ImportError for the check_id function in Plone.

    >>> from OFS.ObjectManager import ObjectManager
    >>> om = ObjectManager()
    >>> om.title = 'foo'
    >>> alsoProvides(om, IFolderish)
    >>> chooser2 = INameChooser(om)
    >>> from Products.CMFPlone import utils
    >>> orig_check_id = utils.check_id
    >>> def dummy_check_id(*args, **kwargs):
    ...     raise ImportError
    >>> utils.check_id = dummy_check_id
    >>> chooser2.chooseName('title', item)
    'title-1'
    >>> utils.check_id = orig_check_id


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

    >>> @implementer(INameFromTitle)
    ... @adapter(IMyType)
    ... class TitleAdapter(object):
    ...     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'
