Traversal Behavior
==================

BaseObject overrides ``__bobo_traverse__`` to expose subobjects
created by ``PortalTransforms`` during the transformation of
content. However, overriding traversal can be tricky, and very hard to
get right.

These tests pretend to make sure that this functionality behaves
correctly across the many use cases that must co-exist.

First, we are going to setup an environment so we can test that stuff
is acquired or not acquired at the right times.

  >>> from plone.app.testing import TEST_USER_ID, setRoles
  >>> setRoles(layer['portal'], TEST_USER_ID, ['Manager'])
  >>> from plone.app.testing import TEST_USER_NAME as default_user
  >>> from plone.app.testing import TEST_USER_PASSWORD as default_password
  >>> from Products.ATContentTypes.tests.atcttestcase import test_home
  >>> portal = layer['portal']
  >>> portal_name = portal.getId()

  >>> from Products.CMFCore.utils import getToolByName

  >>> portal.invokeFactory('Document', 'test_document',
  ...             title='Root Document')
  'test_document'

  >>> portal.invokeFactory('Folder', 'simple_folder')
  'simple_folder'

XML-RPC
-------

XML-RPC is basically a ``POST`` with content-type text/xml. It should
be allowed to acquire content from higher-level hierarchies:

  >>> from Testing.ZopeTestCase.zopedoctest.functional import http
  >>> from Testing.ZopeTestCase.sandbox import AppZapper
  >>> AppZapper().set(layer['app'])

  >>> print http(r"""
  ... POST /%s HTTP/1.0
  ... Authorization: Basic %s:%s
  ... Content-Type: text/xml
  ...
  ... <?xml version='1.0'?>
  ... <methodCall>
  ... <methodName>title_or_id</methodName>
  ... <params>
  ... </params>
  ...
  ... </methodCall>
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.0 200 OK
  ...Plone site...

  >>> print portal.test_document.getPortalTypeName()
  Document

  >>> print portal.test_document.title_or_id()
  Root Document

  >>> print http(r"""
  ... POST /%s HTTP/1.0
  ... Authorization: Basic %s:%s
  ... Content-Type: text/xml
  ...
  ... <?xml version='1.0'?>
  ... <methodCall>
  ... <methodName>test_document.title_or_id</methodName>
  ... <params>
  ... </params>
  ...
  ... </methodCall>
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.0 200 OK
  ...Root Document...

  >>> 'test_document' in portal.simple_folder
  False

  >>> print http(r"""
  ... POST /%s HTTP/1.0
  ... Authorization: Basic %s:%s
  ... Content-Type: text/xml
  ...
  ... <?xml version='1.0'?>
  ... <methodCall>
  ... <methodName>simple_folder.test_document.title_or_id</methodName>
  ... <params>
  ... </params>
  ...
  ... </methodCall>
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.0 200 OK
  ...Root Document...

Browser
-------

For testing Browser access, we are going to just try using the ``GET``
method instead.

  >>> print portal.title_or_id()
  Plone site

  >>> print http(r"""
  ... GET /%s/title_or_id HTTP/1.0
  ... Authorization: Basic %s:%s
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.0 200 OK
  ...Plone site...

  >>> print portal.test_document.getPortalTypeName()
  Document

  >>> print portal.test_document.title_or_id()
  Root Document

  >>> print http(r"""
  ... GET /%s/test_document/title_or_id HTTP/1.0
  ... Authorization: Basic %s:%s
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.0 200 OK
  ...Root Document...

  >>> 'test_document' in portal.simple_folder
  False

  >>> print http(r"""
  ... GET /%s/simple_folder/test_document/title_or_id HTTP/1.0
  ... Authorization: Basic %s:%s
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.0 200 OK
  ...Root Document...

Lets make sure view lookup takes precedence over acquired views.

We need to do some site magic, or our we end up with the SimpleView class
being persisted in the local component registry. We really shouldn't do
ZCML registrations in function tests.

  >>> from zope.site.hooks import getSite, setSite
  >>> site = getSite()

  >>> setSite(None)
  >>> from Zope2.App import zcml

  >>> import Products.Five
  >>> zcml.load_config('meta.zcml', Products.Five)
  >>> zcml.load_string('''<configure xmlns="http://namespaces.zope.org/browser">
  ... <page
  ...     name="document_view"
  ...     for="*"
  ...     permission="zope.Public"
  ...     class="Products.Archetypes.tests.utils.SimpleView"
  ...     />
  ... </configure>''')
  >>> setSite(site)

WebDAV
------

Now for the tricky part. WebDAV requests are *not* allowed to acquire
content, because that would completely break creation of content
through WebDAV.

  >>> print http(r"""
  ... PROPFIND /%s/test_document HTTP/1.1
  ... Authorization: Basic %s:%s
  ... Content-Type: text/xml; charset="utf-8"
  ... Depth: 0
  ...
  ... <?xml version="1.0" encoding="utf-8"?>
  ...   <DAV:propfind xmlns:DAV="DAV:"
  ...      xmlns:zope="http://www.zope.org/propsets/default">
  ...      <DAV:prop><zope:title/></DAV:prop>
  ...   </DAV:propfind>
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.1 207 Multi-Status
  ...Root Document...

  >>> print http(r"""
  ... PROPFIND /%s/simple_folder/test_document HTTP/1.1
  ... Authorization: Basic %s:%s
  ... Content-Type: text/xml; charset="utf-8"
  ... Depth: 0
  ...
  ... <?xml version="1.0" encoding="utf-8"?>
  ...   <DAV:propfind xmlns:DAV="DAV:"
  ...      xmlns:zope="http://www.zope.org/propsets/default">
  ...      <DAV:prop><zope:title/></DAV:prop>
  ...   </DAV:propfind>
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.1 404 Not Found
  ...

Should be possible to create objects via PUT that would otherwise be
acquired.

Create a CTR predicate to map any content to ``Document``:

  >>> ctr = getToolByName(portal, 'content_type_registry')
  >>> p_id = 'at_dav_test'
  >>> p_type = 'name_regex'
  >>> ctr.addPredicate(p_id, p_type)
  >>> class foo: pass
  >>> p_dict = foo()
  >>> p_dict.pattern = '.*'
  >>> ctr.updatePredicate(p_id, p_dict, 'Document')
  >>> ctr.reorderPredicate(p_id, 0)

  >>> print http(r"""
  ... PUT /%s/simple_folder/test_document HTTP/1.1
  ... Authorization: Basic %s:%s
  ... Content-Type: text/plain; charset="utf-8"
  ... Depth: 0
  ...
  ... Simple Folder Document Content
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.1 201 Created
  ...

  >>> folder = portal.simple_folder
  >>> print folder.test_document.getPortalTypeName()
  Document

  >>> print folder.test_document.title_or_id()
  test_document

  >>> print folder.test_document.getText()
  <p>Simple Folder Document Content</p>
  <BLANKLINE>

  >>> print folder.test_document.title_or_id()
  test_document

  >>> print folder.test_document.getText()
  <p>Simple Folder Document Content</p>

Make sure it's possible to create an item named ``index_html`` into a
AT-based folder.

  >>> folder = portal.simple_folder
  >>> 'index_html' in folder
  False

  >>> print folder.index_html
  <FSPageTemplate at index_html>

  >>> print http(r"""
  ... PUT /%s/simple_folder/index_html HTTP/1.1
  ... Authorization: Basic %s:%s
  ... Content-Type: text/plain; charset="utf-8"
  ... Depth: 0
  ...
  ... Simple Folder Index
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.1 201 Created
  ...

  >>> print folder.index_html.title_or_id()
  index_html

  >>> print folder.index_html.getText()
  <p>Simple Folder Index</p>


Finally, cleanup the CTR predicate to not affect other tests:

  >>> ctr.removePredicate(p_id)

Creating folders should work the same way. And the newly created folder
should be of the same kind as the parent:

  >>> print http(r"""
  ... MKCOL /%s/simple_folder/simple_folder HTTP/1.1
  ... Authorization: Basic %s:%s
  ... """ % (portal_name, default_user, default_password))
  HTTP/1.1 201 Created
  ...

  >>> folder = portal.simple_folder.simple_folder
  >>> print folder.getPortalTypeName()
  Folder

  >>> AppZapper().clear()
