[Checkins] SVN: zmi.core/trunk/src/zmi/core/container/ Copy browser code from zope.app.container.
Yusei Tahara
yusei at domen.cx
Sun Jun 7 11:25:08 EDT 2009
Log message for revision 100691:
Copy browser code from zope.app.container.
Changed:
A zmi.core/trunk/src/zmi/core/container/__init__.py
A zmi.core/trunk/src/zmi/core/container/add.pt
A zmi.core/trunk/src/zmi/core/container/adding.py
A zmi.core/trunk/src/zmi/core/container/commontasks.pt
A zmi.core/trunk/src/zmi/core/container/configure.zcml
A zmi.core/trunk/src/zmi/core/container/contents.pt
A zmi.core/trunk/src/zmi/core/container/contents.py
A zmi.core/trunk/src/zmi/core/container/find.pt
A zmi.core/trunk/src/zmi/core/container/find.py
A zmi.core/trunk/src/zmi/core/container/index.pt
A zmi.core/trunk/src/zmi/core/container/tests/
A zmi.core/trunk/src/zmi/core/container/tests/__init__.py
A zmi.core/trunk/src/zmi/core/container/tests/configure.zcml
A zmi.core/trunk/src/zmi/core/container/tests/ftesting.zcml
A zmi.core/trunk/src/zmi/core/container/tests/index.txt
A zmi.core/trunk/src/zmi/core/container/tests/test_adding.py
A zmi.core/trunk/src/zmi/core/container/tests/test_contents.py
A zmi.core/trunk/src/zmi/core/container/tests/test_contents_functional.py
A zmi.core/trunk/src/zmi/core/container/tests/test_directive.py
-=-
Added: zmi.core/trunk/src/zmi/core/container/__init__.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/__init__.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/__init__.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
Added: zmi.core/trunk/src/zmi/core/container/add.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/container/add.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/add.pt 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,62 @@
+<html metal:use-macro="context/@@standard_macros/addingdialog"
+ i18n:domain="zope">
+<body>
+<div metal:fill-slot="body" tal:define="infos view/addingInfo">
+
+ <p tal:condition="not:infos" i18n:translate="">
+ There are no addable content types for this container.
+ </p>
+
+ <form method="post" action="action.html" tal:condition="infos">
+ <table class="TypeListing" cellpadding="3">
+
+ <tal:block define="title view/title | nothing">
+ <caption tal:condition="title" tal:content="title"
+ i18n:translate="">Inserted title</caption>
+ <caption tal:condition="not:title"
+ i18n:translate="">Add Content</caption>
+ </tal:block>
+
+ <tbody>
+
+ <tr tal:repeat="info infos">
+
+ <td class="Selector">
+ <input type="radio" name="type_name"
+ tal:attributes="value info/action;
+ id info/action;
+ checked python:len(infos)==1" />
+ </td>
+
+ <td class="TypeName">
+ <label style="font-weight: bold;"
+ tal:attributes="for info/action">
+ <span tal:replace="info/title" i18n:translate="">Folder</span>
+ </label>
+ <div class="TypeDescription" tal:content="info/description"
+ i18n:translate="">
+ Folders are generic containers for content, including other
+ folders.
+ </div>
+ </td>
+ </tr>
+
+ <tr>
+ <td><br /></td>
+ <td><input type="text" name="id"
+ tal:condition="view/nameAllowed"
+ tal:attributes="value request/id | nothing" />
+ <input type="submit" name="add" value=" Add "
+ i18n:attributes="value add-button" />
+ </td>
+ </tr>
+
+ </tbody>
+
+ </table>
+
+ </form>
+
+</div>
+</body>
+</html>
Added: zmi.core/trunk/src/zmi/core/container/adding.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/adding.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/adding.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,211 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Adding View
+
+The Adding View is used to add new objects to a container. It is sort of a
+factory screen.
+
+$Id: adding.py 87419 2008-06-16 08:42:48Z ccomb $
+"""
+
+__docformat__ = 'restructuredtext'
+
+
+from zope.app.container.constraints import checkFactory
+from zope.app.container.constraints import checkObject
+from zope.app.container.i18n import ZopeMessageFactory as _
+from zope.app.container.interfaces import IAdding
+from zope.app.container.interfaces import IContainerNamesContainer
+from zope.app.container.interfaces import INameChooser
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from zope.app.publisher.browser.menu import getMenu
+from zope.component import getMultiAdapter
+from zope.component import getUtility
+from zope.component import queryAdapter
+from zope.component import queryMultiAdapter
+from zope.component import queryUtility
+from zope.component.interfaces import IFactory
+from zope.event import notify
+from zope.exceptions.interfaces import UserError
+from zope.i18n.interfaces.locales import ICollator
+from zope.i18n.locales.fallbackcollator import FallbackCollator
+from zope.interface import implements
+from zope.lifecycleevent import ObjectCreatedEvent
+from zope.location import LocationProxy
+from zope.publisher.browser import BrowserView
+from zope.publisher.interfaces import IPublishTraverse
+from zope.security.proxy import removeSecurityProxy
+from zope.traversing.browser.absoluteurl import absoluteURL
+import zope.security.checker
+
+class Adding(BrowserView):
+ implements(IAdding, IPublishTraverse)
+
+ def add(self, content):
+ """See zope.app.container.interfaces.IAdding
+ """
+ container = self.context
+ name = self.contentName
+ chooser = INameChooser(container)
+
+ # check precondition
+ checkObject(container, name, content)
+
+ if IContainerNamesContainer.providedBy(container):
+ # The container picks its own names.
+ # We need to ask it to pick one.
+ name = chooser.chooseName(self.contentName or '', content)
+ else:
+ request = self.request
+ name = request.get('add_input_name', name)
+
+ if name is None:
+ name = chooser.chooseName(self.contentName or '', content)
+ elif name == '':
+ name = chooser.chooseName('', content)
+ chooser.checkName(name, content)
+
+ container[name] = content
+ self.contentName = name # Set the added object Name
+ return container[name]
+
+ contentName = None # usually set by Adding traverser
+
+ def nextURL(self):
+ """See zope.app.container.interfaces.IAdding"""
+ return absoluteURL(self.context, self.request) + '/@@contents.html'
+
+ # set in BrowserView.__init__
+ request = None
+ context = None
+
+ def publishTraverse(self, request, name):
+ """See zope.publisher.interfaces.IPublishTraverse"""
+ if '=' in name:
+ view_name, content_name = name.split("=", 1)
+ self.contentName = content_name
+
+ if view_name.startswith('@@'):
+ view_name = view_name[2:]
+ return getMultiAdapter((self, request), name=view_name)
+
+ if name.startswith('@@'):
+ view_name = name[2:]
+ else:
+ view_name = name
+
+ view = queryMultiAdapter((self, request), name=view_name)
+ if view is not None:
+ return view
+
+ factory = queryUtility(IFactory, name)
+ if factory is None:
+ return super(Adding, self).publishTraverse(request, name)
+
+ return factory
+
+ def action(self, type_name='', id=''):
+ if not type_name:
+ raise UserError(_(u"You must select the type of object to add."))
+
+ if type_name.startswith('@@'):
+ type_name = type_name[2:]
+
+ if '/' in type_name:
+ view_name = type_name.split('/', 1)[0]
+ else:
+ view_name = type_name
+
+ if queryMultiAdapter((self, self.request),
+ name=view_name) is not None:
+ url = "%s/%s=%s" % (
+ absoluteURL(self, self.request), type_name, id)
+ self.request.response.redirect(url)
+ return
+
+ if not self.contentName:
+ self.contentName = id
+
+ # TODO: If the factory wrapped by LocationProxy is already a Proxy,
+ # then ProxyFactory does not do the right thing and the
+ # original's checker info gets lost. No factory that was
+ # registered via ZCML and was used via addMenuItem worked
+ # here. (SR)
+ factory = getUtility(IFactory, type_name)
+ if not type(factory) is zope.security.checker.Proxy:
+ factory = LocationProxy(factory, self, type_name)
+ factory = zope.security.checker.ProxyFactory(factory)
+ content = factory()
+
+ # Can't store security proxies.
+ # Note that it is important to do this here, rather than
+ # in add, otherwise, someone might be able to trick add
+ # into unproxying an existing object,
+ content = removeSecurityProxy(content)
+
+ notify(ObjectCreatedEvent(content))
+
+ self.add(content)
+ self.request.response.redirect(self.nextURL())
+
+ def nameAllowed(self):
+ """Return whether names can be input by the user."""
+ return not IContainerNamesContainer.providedBy(self.context)
+
+ menu_id = None
+ index = ViewPageTemplateFile("add.pt")
+
+ def addingInfo(self):
+ """Return menu data.
+
+ This is sorted by title.
+ """
+ container = self.context
+ result = []
+ for menu_id in (self.menu_id, 'zope.app.container.add'):
+ if not menu_id:
+ continue
+ for item in getMenu(menu_id, self, self.request):
+ extra = item.get('extra')
+ if extra:
+ factory = extra.get('factory')
+ if factory:
+ factory = getUtility(IFactory, factory)
+ if not checkFactory(container, None, factory):
+ continue
+ elif item['extra']['factory'] != item['action']:
+ item['has_custom_add_view']=True
+ # translate here to have a localized sorting
+ item['title'] = zope.i18n.translate(item['title'],
+ context=self.request)
+ result.append(item)
+
+ # sort the adding info with a collator instead of a basic unicode sort
+ collator = queryAdapter(self.request.locale, ICollator)
+ if collator is None:
+ collator = FallbackCollator(self.request.locale)
+ result.sort(key = lambda x: collator.key(x['title']))
+ return result
+
+ def isSingleMenuItem(self):
+ "Return whether there is single menu item or not."
+ return len(self.addingInfo()) == 1
+
+ def hasCustomAddView(self):
+ "This should be called only if there is `singleMenuItem` else return 0"
+ if self.isSingleMenuItem():
+ menu_item = self.addingInfo()[0]
+ if 'has_custom_add_view' in menu_item:
+ return True
+ return False
Added: zmi.core/trunk/src/zmi/core/container/commontasks.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/container/commontasks.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/commontasks.pt 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,40 @@
+<tal:block define="addingInfo context/@@+/addingInfo|nothing"
+ condition="addingInfo">
+
+ <tal:block repeat="info addingInfo"
+ define="namesRequired context/@@+/nameAllowed">
+ <div tal:define="oddrow repeat/info/odd;
+ has_custom_add_view python:'has_custom_add_view' in info"
+ tal:attributes="class python:oddrow and 'content even' or 'content odd'"
+ class="even">
+ <a href="#"
+ tal:define="baseurl python:request.getURL(1)"
+ tal:condition="python: not info['action'].startswith('../')
+ and namesRequired and not has_custom_add_view"
+ tal:attributes="
+ href string:${baseurl}/@@contents.html?type_name=${info/action};
+ class info/selected"
+ tal:content="info/title">Folder
+ </a>
+
+ <a href="#"
+ tal:define="baseurl python:request.getURL(1)"
+ tal:condition="python: not info['action'].startswith('../')
+ and (has_custom_add_view or not namesRequired)"
+ tal:attributes="
+ href string:${baseurl}/@@+/action.html?type_name=${info/action};
+ class info/selected"
+ tal:content="info/title">Folder
+ </a>
+
+ <a href="#"
+ tal:define="baseurl python:request.getURL(1)"
+ tal:condition="python: info['action'].startswith('../')"
+ tal:attributes="
+ href python: info['action'][3:];
+ class info/selected"
+ tal:content="info/title">Folder
+ </a>
+ </div>
+ </tal:block>
+</tal:block>
Added: zmi.core/trunk/src/zmi/core/container/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/container/configure.zcml (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/configure.zcml 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,20 @@
+<zope:configure
+ xmlns:zope="http://namespaces.zope.org/zope"
+ xmlns="http://namespaces.zope.org/browser">
+
+ <page
+ for="zope.app.container.interfaces.IReadContainer"
+ name="find.html"
+ permission="zope.ManageContent"
+ class="zmi.core.container.find.Find"
+ template="find.pt"
+ menu="zmi_actions" title="Find" />
+
+ <page
+ for="zope.app.container.interfaces.IWriteContainer"
+ permission="zope.ManageContent"
+ name="commonTasks"
+ class="zmi.core.container.contents.Contents"
+ template="commontasks.pt" />
+
+</zope:configure>
Added: zmi.core/trunk/src/zmi/core/container/contents.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/container/contents.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/contents.pt 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,193 @@
+<html metal:use-macro="context/@@standard_macros/view"
+ i18n:domain="zope">
+<body>
+<div metal:fill-slot="body">
+ <div metal:define-macro="contents">
+
+ <form name="containerContentsForm" method="post" action="."
+ tal:attributes="action request/URL"
+ tal:define="container_contents view/listContentInfo">
+
+ <input type="hidden" name="type_name" value=""
+ tal:attributes="value request/type_name"
+ tal:condition="request/type_name|nothing"
+ />
+ <input type="hidden" name="retitle_id" value=""
+ tal:attributes="value request/retitle_id"
+ tal:condition="request/retitle_id|nothing"
+ />
+
+ <div class="page_error"
+ tal:condition="view/error"
+ tal:content="view/error"
+ i18n:translate="">
+ Error message
+ </div>
+
+ <table id="sortable" class="listing" summary="Content listing"
+ i18n:attributes="summary">
+
+ <thead>
+ <tr>
+ <th><input type="checkbox" onchange="updateCheckboxes(this, 'slaveBox');" /></th>
+ <th i18n:translate="">Name</th>
+ <th i18n:translate="">Title</th>
+ <th i18n:translate="">Size</th>
+ <th i18n:translate="">Created</th>
+ <th i18n:translate="">Modified</th>
+ </tr>
+ </thead>
+
+ <tbody>
+
+ <metal:block tal:condition="view/hasAdding">
+ <tr tal:define="names_required context/@@+/nameAllowed"
+ tal:condition="python:names_required and request.has_key('type_name')">
+ <td></td>
+ <td><input name="new_value" id="focusid" value="" /></td>
+ <td></td>
+ <td></td>
+ <td></td>
+ </tr>
+ </metal:block>
+
+ <metal:block tal:define="supportsRename view/supportsRename"
+ tal:repeat="item container_contents">
+ <tr tal:define="oddrow repeat/item/odd; url item/url;
+ id_quoted item/id/url:quote"
+ tal:attributes="class python:oddrow and 'even' or 'odd'" >
+ <td>
+ <input type="checkbox" class="noborder slaveBox" name="ids:list" id="#"
+ value="#"
+ tal:attributes="value item/id;
+ id item/cb_id;
+ checked request/ids_checked|nothing;"/>
+ </td>
+ <td><a href="#"
+ tal:attributes="href
+ string:${url}/@@SelectedManagementView.html"
+ tal:content="structure item/icon|default">
+ </a
+ ><span tal:condition="item/rename"
+ ><input name="new_value:list"
+ tal:attributes="value item/id"
+ /><input type="hidden" name="rename_ids:list" value=""
+ tal:attributes="value item/rename"
+ /></span
+ ><span tal:condition="not:item/rename">
+ <a href="#"
+ tal:attributes="href
+ string:${url}/@@SelectedManagementView.html"
+ tal:content="item/id"
+ >foo</a
+ ><a href="#"
+ tal:attributes="href
+ string:${request/URL}?rename_ids:list=${id_quoted}"
+ tal:condition="supportsRename"
+ > </a
+ ></span
+ ></td>
+ <td>
+ <input name="new_value" id="focusid"
+ tal:attributes="value item/title|nothing"
+ tal:condition="item/retitle"
+ />
+ <a href="#"
+ tal:attributes="href
+ string:${request/URL}?retitle_id=${id_quoted}"
+ tal:condition="item/retitleable"
+ tal:content="item/title|default"
+ i18n:translate=""
+ > </a>
+ <span
+ tal:condition="item/plaintitle"
+ tal:content="item/title|default"
+ i18n:translate=""
+ > </span>
+ </td>
+
+ <td><span tal:content="item/size/sizeForDisplay|nothing"
+ i18n:translate="">
+ </span></td>
+ <td><span tal:define="created item/created|default"
+ tal:content="created"
+ i18n:translate=""> </span></td>
+ <td><span tal:define="modified item/modified|default"
+ tal:content="modified"
+ i18n:translate=""> </span></td>
+ </tr>
+ </metal:block>
+
+ </tbody>
+ </table>
+
+ <tal:block tal:condition="view/normalButtons">
+
+ <input type="submit" name="container_rename_button" value="Rename"
+ i18n:attributes="value container-rename-button"
+ tal:condition="view/supportsRename"
+ />
+ <input type="submit" name="container_cut_button" value="Cut"
+ i18n:attributes="value container-cut-button"
+ tal:condition="view/supportsCut"
+ />
+ <input type="submit" name="container_copy_button" value="Copy"
+ i18n:attributes="value container-copy-button"
+ tal:condition="view/supportsCopy"
+ />
+ <input type="submit" name="container_paste_button" value="Paste"
+ tal:condition="view/hasClipboardContents"
+ i18n:attributes="value container-paste-button"
+ />
+ <input type="submit" name="container_delete_button" value="Delete"
+ i18n:attributes="value container-delete-button"
+ tal:condition="view/supportsDelete"
+ i18n:domain="zope"
+ />
+
+ <div tal:condition="view/hasAdding" tal:omit-tag="">
+ <div tal:omit-tag=""
+ tal:define="adding nocall:context/@@+;
+ addingInfo adding/addingInfo;
+ has_custom_add_view adding/hasCustomAddView;
+ names_required adding/nameAllowed"
+ tal:condition="adding/isSingleMenuItem">
+ <input type="submit" name="container_add_button" value="Add"
+ i18n:attributes="value add-button"
+ i18n:domain="zope"
+ />
+ <input type="text" name="single_new_value" id="focusid"
+ tal:condition="python:names_required and not has_custom_add_view"
+ i18n:domain="zope"
+ />
+ <input type="hidden" name="single_type_name"
+ value=""
+ tal:attributes="value python:addingInfo[0]['action']"
+ />
+ </div>
+ </div>
+
+ </tal:block>
+
+ <div tal:condition="view/specialButtons">
+ <input type="submit" value="Apply"
+ i18n:attributes="value container-apply-button"
+ />
+ <input type="submit" name="container_cancel_button" value="Cancel"
+ i18n:attributes="value container-cancel-button"
+ />
+ </div>
+
+ </form>
+
+ <script type="text/javascript"><!--
+ if (document.containerContentsForm.new_value)
+ document.containerContentsForm.new_value.focus();
+ //-->
+ </script>
+
+ </div>
+
+</div>
+</body>
+</html>
Added: zmi.core/trunk/src/zmi/core/container/contents.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/contents.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/contents.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,464 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""View Class for the Container's Contents view.
+
+$Id: contents.py 85553 2008-04-21 17:35:27Z lgs $
+"""
+__docformat__ = 'restructuredtext'
+
+import urllib
+
+from zope.component import queryMultiAdapter
+from zope.event import notify
+from zope.exceptions.interfaces import UserError
+from zope.security.interfaces import Unauthorized
+from zope.security import canWrite
+from zope.size.interfaces import ISized
+from zope.traversing.interfaces import TraversalError
+from zope.publisher.browser import BrowserView
+from zope.dublincore.interfaces import IZopeDublinCore
+from zope.dublincore.interfaces import IDCDescriptiveProperties
+from zope.copypastemove.interfaces import IPrincipalClipboard
+from zope.copypastemove.interfaces import IObjectCopier, IObjectMover
+from zope.copypastemove.interfaces import IContainerItemRenamer
+from zope.annotation.interfaces import IAnnotations
+from zope.lifecycleevent import ObjectModifiedEvent, Attributes
+from zope.traversing.api import getName, getPath, joinPath, traverse
+
+from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
+from zope.app.container.i18n import ZopeMessageFactory as _
+
+from zmi.core.container.adding import Adding
+from zope.app.container.interfaces import IContainer, DuplicateIDError
+from zope.app.container.interfaces import IContainerNamesContainer
+
+class Contents(BrowserView):
+
+ __used_for__ = IContainer
+
+ error = ''
+ message = ''
+ normalButtons = False
+ specialButtons = False
+ supportsRename = False
+
+ def listContentInfo(self):
+ request = self.request
+
+ if "container_cancel_button" in request:
+ if "type_name" in request:
+ del request.form['type_name']
+ if "rename_ids" in request and "new_value" in request:
+ del request.form['rename_ids']
+ if "retitle_id" in request and "new_value" in request:
+ del request.form['retitle_id']
+
+ return self._normalListContentsInfo()
+
+ elif "container_rename_button" in request and not request.get("ids"):
+ self.error = _("You didn't specify any ids to rename.")
+ elif "container_add_button" in request:
+ if "single_type_name" in request \
+ and "single_new_value" in request:
+ request.form['type_name'] = request['single_type_name']
+ request.form['new_value'] = request['single_new_value']
+ self.addObject()
+ elif 'single_type_name' in request \
+ and 'single_new_value' not in request:
+ request.form['type_name'] = request['single_type_name']
+ request.form['new_value'] = ""
+ self.addObject()
+ elif "type_name" in request and "new_value" in request:
+ self.addObject()
+ elif "rename_ids" in request and "new_value" in request:
+ self.renameObjects()
+ elif "retitle_id" in request and "new_value" in request:
+ self.changeTitle()
+ elif "container_cut_button" in request:
+ self.cutObjects()
+ elif "container_copy_button" in request:
+ self.copyObjects()
+ elif "container_paste_button" in request:
+ self.pasteObjects()
+ elif "container_delete_button" in request:
+ self.removeObjects()
+ else:
+ return self._normalListContentsInfo()
+
+ if self.error:
+ return self._normalListContentsInfo()
+
+ status = request.response.getStatus()
+ if status not in (302, 303):
+ # Only redirect if nothing else has
+ request.response.redirect(request.URL)
+ return ()
+
+ def normalListContentInfo(self):
+ return self._normalListContentsInfo()
+
+ def _normalListContentsInfo(self):
+ request = self.request
+
+ self.specialButtons = (
+ 'type_name' in request or
+ 'rename_ids' in request or
+ ('container_rename_button' in request
+ and request.get("ids")) or
+ 'retitle_id' in request
+ )
+ self.normalButtons = not self.specialButtons
+
+ info = map(self._extractContentInfo, self.context.items())
+
+ self.supportsCut = info
+ self.supportsCopy = info
+ self.supportsDelete = info
+ self.supportsPaste = self.pasteable()
+ self.supportsRename = (
+ self.supportsCut and
+ not IContainerNamesContainer.providedBy(self.context)
+ )
+
+ return info
+
+
+ def _extractContentInfo(self, item):
+ request = self.request
+
+
+ rename_ids = {}
+ if "container_rename_button" in request:
+ for rename_id in request.get('ids', ()):
+ rename_ids[rename_id] = rename_id
+ elif "rename_ids" in request:
+ for rename_id in request.get('rename_ids', ()):
+ rename_ids[rename_id] = rename_id
+
+
+ retitle_id = request.get('retitle_id')
+
+ id, obj = item
+ info = {}
+ info['id'] = info['cb_id'] = id
+ info['object'] = obj
+
+ info['url'] = urllib.quote(id.encode('utf-8'))
+ info['rename'] = rename_ids.get(id)
+ info['retitle'] = id == retitle_id
+
+
+ zmi_icon = queryMultiAdapter((obj, self.request), name='zmi_icon')
+ if zmi_icon is None:
+ info['icon'] = None
+ else:
+ info['icon'] = zmi_icon()
+
+ dc = IZopeDublinCore(obj, None)
+ if dc is not None:
+ info['retitleable'] = canWrite(dc, 'title')
+ info['plaintitle'] = not info['retitleable']
+
+ title = self.safe_getattr(dc, 'title', None)
+ if title:
+ info['title'] = title
+
+ formatter = self.request.locale.dates.getFormatter(
+ 'dateTime', 'short')
+
+ created = self.safe_getattr(dc, 'created', None)
+ if created is not None:
+ info['created'] = formatter.format(created)
+
+ modified = self.safe_getattr(dc, 'modified', None)
+ if modified is not None:
+ info['modified'] = formatter.format(modified)
+ else:
+ info['retitleable'] = 0
+ info['plaintitle'] = 1
+
+
+ sized_adapter = ISized(obj, None)
+ if sized_adapter is not None:
+ info['size'] = sized_adapter
+ return info
+
+ def safe_getattr(self, obj, attr, default):
+ """Attempts to read the attr, returning default if Unauthorized."""
+ try:
+ return getattr(obj, attr, default)
+ except Unauthorized:
+ return default
+
+ def renameObjects(self):
+ """Given a sequence of tuples of old, new ids we rename"""
+ request = self.request
+ ids = request.get("rename_ids")
+ newids = request.get("new_value")
+
+ renamer = IContainerItemRenamer(self.context)
+ for oldid, newid in zip(ids, newids):
+ if newid != oldid:
+ renamer.renameItem(oldid, newid)
+
+ def changeTitle(self):
+ """Given a sequence of tuples of old, new ids we rename"""
+ request = self.request
+ id = request.get("retitle_id")
+ new = request.get("new_value")
+
+ item = self.context[id]
+ dc = IDCDescriptiveProperties(item)
+ dc.title = new
+ notify(ObjectModifiedEvent(item, Attributes(IZopeDublinCore, 'title')))
+
+ def hasAdding(self):
+ """Returns true if an adding view is available."""
+ adding = queryMultiAdapter((self.context, self.request), name="+")
+ return (adding is not None)
+
+ def addObject(self):
+ request = self.request
+ if IContainerNamesContainer.providedBy(self.context):
+ new = ""
+ else:
+ new = request["new_value"]
+
+ adding = queryMultiAdapter((self.context, self.request), name="+")
+ if adding is None:
+ adding = Adding(self.context, request)
+ else:
+ # Set up context so that the adding can build a url
+ # if the type name names a view.
+ # Note that we can't so this for the "adding is None" case
+ # above, because there is no "+" view.
+ adding.__parent__ = self.context
+ adding.__name__ = '+'
+
+ adding.action(request['type_name'], new)
+
+ def removeObjects(self):
+ """Remove objects specified in a list of object ids"""
+ request = self.request
+ ids = request.get('ids')
+ if not ids:
+ self.error = _("You didn't specify any ids to remove.")
+ return
+
+ container = self.context
+ for id in ids:
+ del container[id]
+
+ def copyObjects(self):
+ """Copy objects specified in a list of object ids"""
+ request = self.request
+ ids = request.get('ids')
+ if not ids:
+ self.error = _("You didn't specify any ids to copy.")
+ return
+
+ container_path = getPath(self.context)
+
+ # For each item, check that it can be copied; if so, save the
+ # path of the object for later copying when a destination has
+ # been selected; if not copyable, provide an error message
+ # explaining that the object can't be copied.
+ items = []
+ for id in ids:
+ ob = self.context[id]
+ copier = IObjectCopier(ob)
+ if not copier.copyable():
+ m = {"name": id}
+ title = getDCTitle(ob)
+ if title:
+ m["title"] = title
+ self.error = _(
+ "Object '${name}' (${title}) cannot be copied",
+ mapping=m)
+ else:
+ self.error = _("Object '${name}' cannot be copied",
+ mapping=m)
+ return
+ items.append(joinPath(container_path, id))
+
+ # store the requested operation in the principal annotations:
+ clipboard = getPrincipalClipboard(self.request)
+ clipboard.clearContents()
+ clipboard.addItems('copy', items)
+
+ def cutObjects(self):
+ """move objects specified in a list of object ids"""
+ request = self.request
+ ids = request.get('ids')
+ if not ids:
+ self.error = _("You didn't specify any ids to cut.")
+ return
+
+ container_path = getPath(self.context)
+
+ # For each item, check that it can be moved; if so, save the
+ # path of the object for later moving when a destination has
+ # been selected; if not movable, provide an error message
+ # explaining that the object can't be moved.
+ items = []
+ for id in ids:
+ ob = self.context[id]
+ mover = IObjectMover(ob)
+ if not mover.moveable():
+ m = {"name": id}
+ title = getDCTitle(ob)
+ if title:
+ m["title"] = title
+ self.error = _(
+ "Object '${name}' (${title}) cannot be moved",
+ mapping=m)
+ else:
+ self.error = _("Object '${name}' cannot be moved",
+ mapping=m)
+ return
+ items.append(joinPath(container_path, id))
+
+ # store the requested operation in the principal annotations:
+ clipboard = getPrincipalClipboard(self.request)
+ clipboard.clearContents()
+ clipboard.addItems('cut', items)
+
+
+ def pasteable(self):
+ """Decide if there is anything to paste
+ """
+ target = self.context
+ clipboard = getPrincipalClipboard(self.request)
+ items = clipboard.getContents()
+ for item in items:
+ try:
+ obj = traverse(target, item['target'])
+ except TraversalError:
+ pass
+ else:
+ if item['action'] == 'cut':
+ mover = IObjectMover(obj)
+ moveableTo = self.safe_getattr(mover, 'moveableTo', None)
+ if moveableTo is None or not moveableTo(target):
+ return False
+ elif item['action'] == 'copy':
+ copier = IObjectCopier(obj)
+ copyableTo = self.safe_getattr(copier, 'copyableTo', None)
+ if copyableTo is None or not copyableTo(target):
+ return False
+ else:
+ raise
+
+ return True
+
+
+ def pasteObjects(self):
+ """Paste ojects in the user clipboard to the container
+ """
+ target = self.context
+ clipboard = getPrincipalClipboard(self.request)
+ items = clipboard.getContents()
+ moved = False
+ not_pasteable_ids = []
+ for item in items:
+ duplicated_id = False
+ try:
+ obj = traverse(target, item['target'])
+ except TraversalError:
+ pass
+ else:
+ if item['action'] == 'cut':
+ mover = IObjectMover(obj)
+ try:
+ mover.moveTo(target)
+ moved = True
+ except DuplicateIDError:
+ duplicated_id = True
+ elif item['action'] == 'copy':
+ copier = IObjectCopier(obj)
+ try:
+ copier.copyTo(target)
+ except DuplicateIDError:
+ duplicated_id = True
+ else:
+ raise
+
+ if duplicated_id:
+ not_pasteable_ids.append(getName(obj))
+
+ if moved:
+ # Clear the clipboard if we do a move, but not if we only do a copy
+ clipboard.clearContents()
+
+ if not_pasteable_ids != []:
+ # Show the ids of objects that can't be pasted because
+ # their ids are already taken.
+ # TODO Can't we add a 'copy_of' or something as a prefix
+ # instead of raising an exception ?
+ raise UserError(
+ _("The given name(s) %s is / are already being used" %(
+ str(not_pasteable_ids))))
+
+ def hasClipboardContents(self):
+ """Interogate the ``PrinicipalAnnotation`` to see if clipboard
+ contents exist."""
+
+ if not self.supportsPaste:
+ return False
+
+ # touch at least one item to in clipboard confirm contents
+ clipboard = getPrincipalClipboard(self.request)
+ items = clipboard.getContents()
+ for item in items:
+ try:
+ traverse(self.context, item['target'])
+ except TraversalError:
+ pass
+ else:
+ return True
+
+ return False
+
+ contents = ViewPageTemplateFile('contents.pt')
+ contentsMacros = contents
+
+ _index = ViewPageTemplateFile('index.pt')
+
+ def index(self):
+ if 'index.html' in self.context:
+ self.request.response.redirect('index.html')
+ return ''
+
+ return self._index()
+
+class JustContents(Contents):
+ """Like Contents, but does't delegate to item named index.html"""
+
+ def index(self):
+ return self._index()
+
+
+def getDCTitle(ob):
+ dc = IDCDescriptiveProperties(ob, None)
+ if dc is None:
+ return None
+ else:
+ return dc.title
+
+
+def getPrincipalClipboard(request):
+ """Return the clipboard based on the request."""
+ user = request.principal
+ annotations = IAnnotations(user)
+ return IPrincipalClipboard(annotations)
Added: zmi.core/trunk/src/zmi/core/container/find.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/container/find.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/find.pt 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,22 @@
+<html metal:use-macro="context/@@standard_macros/dialog"
+ i18n:domain="zope">
+<body>
+<div metal:fill-slot="body" >
+
+ <form action="@@find.html" method="get">
+ <input type="text" name="ids" value="" /><br />
+ <input type="submit" name="find_submit" value=" Find "
+ i18n:attributes="value find-button"/>
+ </form>
+
+ <table tal:condition="request/ids|nothing">
+ <tr tal:repeat="item python:view.findByIds(request['ids'])">
+ <td>
+ <a href="" tal:attributes="href item/url" tal:content="item/id">id</a>
+ </td>
+ </tr>
+ </table>
+
+</div>
+</body>
+</html>
Added: zmi.core/trunk/src/zmi/core/container/find.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/find.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/find.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Find View Class
+
+$Id: find.py 67630 2006-04-27 00:54:03Z jim $
+"""
+__docformat__ = 'restructuredtext'
+
+from zope.traversing.api import getName
+from zope.traversing.browser.absoluteurl import absoluteURL
+from zope.publisher.browser import BrowserView
+
+from zope.app.container.find import SimpleIdFindFilter
+from zope.app.container.interfaces import IFind
+
+# Very simple implementation right now
+class Find(BrowserView):
+
+ def findByIds(self, ids):
+ """Do a find for the `ids` listed in `ids`, which is a string."""
+ finder = IFind(self.context)
+ ids = ids.split()
+ # if we don't have any ids listed, don't search at all
+ if not ids:
+ return []
+ request = self.request
+ result = []
+ for object in finder.find([SimpleIdFindFilter(ids)]):
+ url = absoluteURL(object, request)
+ result.append({ 'id': getName(object), 'url': url})
+ return result
Added: zmi.core/trunk/src/zmi/core/container/index.pt
===================================================================
--- zmi.core/trunk/src/zmi/core/container/index.pt (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/index.pt 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,66 @@
+<html metal:use-macro="context/@@standard_macros/page"
+ i18n:domain="zope">
+<head>
+ <style metal:fill-slot="headers" type="text/css">
+ <!--
+ .ContentIcon {
+ width: 20px;
+ }
+
+ .ContentTitle {
+ text-align: left;
+ }
+ -->
+ </style>
+</head>
+<body>
+<div metal:fill-slot="body">
+
+ <table
+ id="sortable" class="listing" summary="Content listing"
+ cellpadding="2" cellspacing="0"
+ i18n:attributes="summary">
+
+ <thead>
+ <tr>
+ <th> </th>
+ <th i18n:translate="">Name</th>
+ <th i18n:translate="">Title</th>
+ <th i18n:translate="">Created</th>
+ <th i18n:translate="">Modified</th>
+ </tr>
+ </thead>
+
+ <tbody>
+
+ <tr tal:repeat="info view/listContentInfo">
+ <td>
+ <a
+ href="#"
+ tal:attributes="href info/url"
+ tal:content="structure info/icon|default" />
+ </td>
+
+ <td class="ContentTitle">
+ <a href="subfolder_id"
+ tal:attributes="href info/url"
+ tal:content="info/id"
+ i18n:translate=""
+ >ID here</a>
+ </td>
+
+ <td><span tal:content="info/title|default"
+ i18n:translate=""> </span></td>
+ <td><span tal:content="info/created|default"
+ i18n:translate=""> </span></td>
+ <td><span tal:content="info/modified|default"
+ i18n:translate=""> </span></td>
+
+ </tr>
+ </tbody>
+
+ </table>
+
+</div>
+</body>
+</html>
Added: zmi.core/trunk/src/zmi/core/container/tests/__init__.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/__init__.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/__init__.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
Added: zmi.core/trunk/src/zmi/core/container/tests/configure.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/configure.zcml (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/configure.zcml 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,16 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser">
+
+ <class class=".test_contents_functional.ReadOnlyContainer">
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.container.interfaces.IReadContainer"
+ />
+ </class>
+
+ <browser:containerViews
+ for="zope.app.container.interfaces.IReadContainer"
+ contents="zope.ManageContent" />
+
+</configure>
Added: zmi.core/trunk/src/zmi/core/container/tests/ftesting.zcml
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/ftesting.zcml (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/ftesting.zcml 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,55 @@
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ i18n_domain="zope"
+ package="zmi.core.container"
+ >
+ <!-- This file is the equivalent of site.zcml and it is -->
+ <!-- used for functional testing setup -->
+
+ <include package="zope.app.zcmlfiles" />
+ <include package="zmi.core.container.tests" />
+ <include package="zope.app.file"/>
+ <include package="zope.app.authentication" />
+
+ <include package="zope.securitypolicy" file="meta.zcml" />
+ <include package="zope.app.securitypolicy.browser.tests" file="functional.zcml" />
+ <include package="zope.app.securitypolicy" />
+
+ <securityPolicy
+ component="zope.securitypolicy.zopepolicy.ZopeSecurityPolicy" />
+
+ <role id="zope.Anonymous" title="Everybody"
+ description="All users have this role implicitly" />
+ <role id="zope.Manager" title="Site Manager" />
+
+ <!-- Replace the following directive if you don't want public access -->
+ <grant permission="zope.View"
+ role="zope.Anonymous" />
+ <grant permission="zope.app.dublincore.view"
+ role="zope.Anonymous" />
+
+ <grantAll role="zope.Manager" />
+
+ <!-- Principals -->
+
+ <unauthenticatedPrincipal
+ id="zope.anybody"
+ title="Unauthenticated User" />
+
+ <!-- Principal that tests generally run as -->
+ <principal
+ id="zope.mgr"
+ title="Manager"
+ login="mgr"
+ password="mgrpw" />
+
+ <!-- Bootstrap principal used to make local grant to the principal above -->
+ <principal
+ id="zope.globalmgr"
+ title="Manager"
+ login="globalmgr"
+ password="globalmgrpw" />
+
+ <grant role="zope.Manager" principal="zope.globalmgr" />
+
+</configure>
Added: zmi.core/trunk/src/zmi/core/container/tests/index.txt
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/index.txt (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/index.txt 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,18 @@
+The containerViews directive lets us associate some standard forms
+for containers with an interface. There's an "index.html" view that
+provides a listing of the contained objects without provinding any way
+to manage them (though it allows us to visit them by clicking on
+links).
+
+We can get this view from the root folder easily::
+
+ >>> response = http(r"""
+ ... GET / HTTP/1.1
+ ... """)
+
+And we can check that there isn't a form (where the management
+operations would have buttons)::
+
+ >>> body = response.getBody().lower()
+ >>> "<form" in body
+ False
Property changes on: zmi.core/trunk/src/zmi/core/container/tests/index.txt
___________________________________________________________________
Added: svn:eol-style
+ native
Added: zmi.core/trunk/src/zmi/core/container/tests/test_adding.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/test_adding.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/test_adding.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,586 @@
+##############################################################################
+#
+# Copyright (c) 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Adding implementation tests
+
+$Id: test_adding.py 96149 2009-02-05 17:24:17Z tseaver $
+"""
+import unittest
+
+import zope.interface
+import zope.security.checker
+from zope.component.interfaces import IFactory
+from zope.component.interfaces import ComponentLookupError
+from zope.interface import implements, Interface, directlyProvides
+from zope.publisher.browser import TestRequest
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.publisher.browser import BrowserView
+from zope.security.interfaces import ForbiddenAttribute
+from zope.testing.doctestunit import DocTestSuite
+from zope.exceptions.interfaces import UserError
+from zope.traversing.api import getParent
+from zope.traversing.browser import AbsoluteURL
+from zope.traversing.browser.absoluteurl import absoluteURL
+from zope.traversing.browser.interfaces import IAbsoluteURL
+from zope.traversing.interfaces import IContainmentRoot
+
+from zope.app.testing import ztapi
+from zope.app.testing.placelesssetup import PlacelessSetup, setUp, tearDown
+from zope.app.publisher.interfaces.browser import AddMenu
+from zope.app.publisher.interfaces.browser import IMenuItemType, IBrowserMenu
+from zope.app.publisher.browser.menu import BrowserMenuItem, BrowserMenu
+from zope.app.container.interfaces import IAdding
+from zope.app.container.interfaces import IObjectAddedEvent
+from zope.app.container.interfaces import IContainerNamesContainer
+from zope.app.container.interfaces import INameChooser
+from zope.app.container.interfaces import IContainer
+from zope.app.container.contained import contained
+from zmi.core.container.adding import Adding
+from zope.app.container.sample import SampleContainer
+
+
+class Root(object):
+ implements(IContainmentRoot)
+
+class Container(SampleContainer):
+ pass
+
+class CreationView(BrowserView):
+
+ def action(self):
+ return 'been there, done that'
+
+
+class Content(object):
+ pass
+
+class Factory(object):
+
+ implements(IFactory)
+
+ title = ''
+ description = ''
+
+ def getInterfaces(self):
+ return ()
+
+ def __call__(self):
+ return Content()
+
+
+class AbsoluteURL(BrowserView):
+
+ def __str__(self):
+ if IContainmentRoot.providedBy(self.context):
+ return ''
+ name = self.context.__name__
+ url = absoluteURL(getParent(self.context), self.request)
+ url += '/' + name
+ return url
+
+ __call__ = __str__
+
+def defineMenuItem(menuItemType, for_, action, title=u'', extra=None):
+ newclass = type(title, (BrowserMenuItem,),
+ {'title':title, 'action':action,
+ '_for': for_, 'extra':extra})
+ zope.interface.classImplements(newclass, menuItemType)
+ ztapi.provideAdapter((for_, IBrowserRequest), menuItemType, newclass, title)
+
+def registerAddMenu():
+ ztapi.provideUtility(IMenuItemType, AddMenu, 'zope.app.container.add')
+ ztapi.provideUtility(IBrowserMenu,
+ BrowserMenu('zope.app.container.add', u'', u''),
+ 'zope.app.container.add')
+
+
+class Test(PlacelessSetup, unittest.TestCase):
+
+ def setUp(self):
+ super(Test, self).setUp()
+
+ def test(self):
+ container = Container()
+ request = TestRequest()
+ adding = Adding(container, request)
+ ztapi.browserView(IAdding, "Thing", CreationView)
+ self.assertEqual(adding.contentName, None)
+ view = adding.publishTraverse(request, 'Thing=foo')
+ self.assertEqual(view.action(), 'been there, done that')
+ self.assertEqual(adding.contentName, 'foo')
+
+ o = object()
+ result = adding.add(o)
+
+ # Check the state of the container and result
+ self.assertEqual(container["foo"], o)
+ self.assertEqual(result, o)
+
+ def testNoNameGiven(self):
+ container = Container()
+ request = TestRequest()
+ adding = Adding(container, request)
+ ztapi.browserView(IAdding, "Thing", CreationView)
+
+ self.assertEqual(adding.contentName, None)
+ view = adding.publishTraverse(request, 'Thing=')
+ self.assertEqual(adding.contentName, '')
+
+ def testAction(self):
+ # make a private factory
+ ztapi.provideUtility(IFactory, Factory(), 'fooprivate')
+
+ factory = Factory()
+ factory.__Security_checker__ = zope.security.checker.NamesChecker(
+ ['__call__'])
+ ztapi.provideUtility(IFactory, factory, 'foo')
+
+ container = Container()
+ adding = Adding(container, TestRequest())
+ adding.nextURL = lambda: '.'
+ adding.nameAllowed = lambda: True
+
+ # we can't use a private factory:
+ self.assertRaises(ForbiddenAttribute,
+ adding.action, type_name='fooprivate', id='bar')
+
+ # typical add - id is provided by user
+ adding.action(type_name='foo', id='bar')
+ self.assert_('bar' in container)
+
+ # missing type_name
+ self.assertRaises(UserError, adding.action, id='bar')
+
+ # missing id
+ self.assertRaises(KeyError, adding.action, type_name='foo')
+
+ # bad type_name
+ self.assertRaises(ComponentLookupError, adding.action,
+ type_name='***', id='bar')
+
+ # alternative add - id is provided internally instead of from user
+ adding.nameAllowed = lambda: False
+ adding.contentName = 'baz'
+ adding.action(type_name='foo')
+ self.assert_('baz' in container)
+
+ # alternative add w/missing contentName
+ # Note: Passing is None as object name might be okay, if the container
+ # is able to hand out ids itself. Let's not require a content
+ # name to be specified!
+ # For the container, (or really, the chooser, to choose, we have to
+ # marke the container as a ContainerNamesContainer
+ directlyProvides(container, IContainerNamesContainer)
+ adding.contentName = None
+ adding.action(type_name='foo')
+ self.assert_('Content' in container)
+
+
+ def test_action(self):
+ container = Container()
+ container = contained(container, Root(), "container")
+ request = TestRequest()
+ adding = Adding(container, request)
+ adding.__name__ = '+'
+ ztapi.browserView(IAdding, "Thing", CreationView)
+ ztapi.browserView(Interface, "absolute_url", AbsoluteURL)
+ ztapi.browserView(None, '', AbsoluteURL, providing=IAbsoluteURL)
+ self.assertRaises(UserError, adding.action, '', 'foo')
+ adding.action('Thing', 'foo')
+ self.assertEqual(adding.request.response.getHeader('location'),
+ '/container/+/Thing=foo')
+ adding.action('Thing/screen1', 'foo')
+ self.assertEqual(adding.request.response.getHeader('location'),
+ '/container/+/Thing/screen1=foo')
+
+ def test_publishTraverse_factory(self):
+ factory = Factory()
+ ztapi.provideUtility(IFactory, factory, 'foo')
+ container = Container()
+ request = TestRequest()
+ adding = Adding(container, request)
+ self.assert_(adding.publishTraverse(request, 'foo') is factory)
+
+
+def test_constraint_driven_addingInfo():
+ """
+ >>> registerAddMenu()
+
+ >>> class TestMenu(zope.interface.Interface):
+ ... pass
+ >>> zope.interface.directlyProvides(TestMenu, IMenuItemType)
+
+ >>> ztapi.provideUtility(IMenuItemType, TestMenu, 'TestMenu')
+ >>> ztapi.provideUtility(IBrowserMenu, BrowserMenu('TestMenu', u'', u''),
+ ... 'TestMenu')
+
+ >>> defineMenuItem(TestMenu, IAdding, '', 'item1')
+ >>> defineMenuItem(TestMenu, IAdding, '', 'Item2')
+
+ >>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'})
+ >>> defineMenuItem(AddMenu, IAdding, '', 'item4', extra={'factory': 'f2'})
+
+ >>> class F1(object):
+ ... pass
+
+ >>> class F2(object):
+ ... pass
+
+ >>> def pre(container, name, object):
+ ... if not isinstance(object, F1):
+ ... raise zope.interface.Invalid()
+ >>> def prefactory(container, name, factory):
+ ... if factory._callable is not F1:
+ ... raise zope.interface.Invalid()
+ >>> pre.factory = prefactory
+
+
+ >>> class IContainer(zope.interface.Interface):
+ ... def __setitem__(name, object):
+ ... pass
+ ... __setitem__.precondition = pre
+
+
+ >>> class Container(object):
+ ... zope.interface.implements(IContainer)
+
+ >>> from zope.component.factory import Factory
+ >>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
+ >>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
+
+ >>> from zmi.core.container.adding import Adding
+ >>> adding = Adding(Container(), TestRequest())
+ >>> items = adding.addingInfo()
+ >>> len(items)
+ 1
+ >>> items[0]['title']
+ u'item3'
+
+ >>> adding.menu_id = 'TestMenu'
+ >>> items = adding.addingInfo()
+ >>> len(items)
+ 3
+ >>> items[0]['title']
+ u'item1'
+ >>> items[1]['title'] # the collator ordered this one correctly!
+ u'Item2'
+ >>> items[2]['title']
+ u'item3'
+ """
+
+def test_constraint_driven_add():
+ """
+ >>> from zope.app.container.sample import SampleContainer
+ >>> from zmi.core.container.adding import Adding
+
+ >>> class F1(object):
+ ... pass
+
+ >>> class F2(object):
+ ... pass
+
+ >>> def pre(container, name, object):
+ ... "a mock item constraint "
+ ... if not isinstance(object, F1):
+ ... raise zope.interface.Invalid('not a valid child')
+
+ >>> class ITestContainer(zope.interface.Interface):
+ ... def __setitem__(name, object):
+ ... pass
+ ... __setitem__.precondition = pre
+
+ >>> class Container(SampleContainer):
+ ... zope.interface.implements(ITestContainer)
+
+ >>> adding = Adding(Container(), TestRequest())
+ >>> c = adding.add(F1())
+
+ This test should fail, because the container only
+ accepts instances of F1
+
+ >>> adding.add(F2())
+ Traceback (most recent call last):
+ ...
+ Invalid: not a valid child
+
+ >>> class ValidContainer(SampleContainer):
+ ... zope.interface.implements(ITestContainer)
+
+ >>> def constr(container):
+ ... "a mock container constraint"
+ ... if not isinstance(container, ValidContainer):
+ ... raise zope.interface.Invalid('not a valid container')
+ ... return True
+
+ >>> class I2(zope.interface.Interface):
+ ... __parent__ = zope.schema.Field(constraint = constr)
+
+ >>> zope.interface.classImplements(F1, I2)
+
+ This adding now fails, because the Container is not a valid
+ parent for F1
+
+ >>> c = adding.add(F1())
+ Traceback (most recent call last):
+ ...
+ Invalid: not a valid container
+
+ >>> adding = Adding(ValidContainer(), TestRequest())
+ >>> c = adding.add(F1())
+
+ """
+
+
+def test_nameAllowed():
+ """
+ Test for nameAllowed in adding.py
+
+ >>> from zmi.core.container.adding import Adding
+ >>> from zope.app.container.interfaces import IContainerNamesContainer
+
+ Class implements IContainerNamesContainer
+
+ >>> class FakeContainer(object):
+ ... zope.interface.implements(IContainerNamesContainer)
+
+ nameAllowed returns False if the class imlements
+ IContainerNamesContainer
+
+ >>> adding = Adding(FakeContainer(),TestRequest())
+ >>> adding.nameAllowed()
+ False
+
+ Fake class without IContainerNamesContainer
+
+ >>> class Fake(object):
+ ... pass
+
+ nameAllowed returns True if the class
+ doesn't imlement IContainerNamesContainer
+
+ >>> adding = Adding(Fake(),TestRequest())
+ >>> adding.nameAllowed()
+ True
+
+ """
+
+
+
+def test_chooseName():
+ """If user don't enter name, pick one
+
+ >>> class MyContainer(object):
+ ... args = {}
+ ... zope.interface.implements(INameChooser, IContainer)
+ ... def chooseName(self, name, object):
+ ... self.args["choose"] = name, object
+ ... return 'pickone'
+ ... def checkName(self, name, object):
+ ... self.args["check"] = name, object
+ ... def __setitem__(self, name, object):
+ ... setattr(self, name, object)
+ ... self.name = name
+ ... def __getitem__(self, key):
+ ... return getattr(self, key)
+
+ >>> request = TestRequest()
+ >>> mycontainer = MyContainer()
+ >>> adding = Adding(mycontainer, request)
+ >>> o = object()
+ >>> add_obj = adding.add(o)
+ >>> mycontainer.name
+ 'pickone'
+ >>> add_obj is o
+ True
+
+ Make sure right arguments passed to INameChooser adapter:
+
+ >>> name, obj = mycontainer.args["choose"]
+ >>> name
+ ''
+ >>> obj is o
+ True
+ >>> name, obj = mycontainer.args["check"]
+ >>> name
+ 'pickone'
+ >>> obj is o
+ True
+ """
+
+
+
+def test_SingleMenuItem_and_CustomAddView_NonICNC():
+ """
+ This tests the condition if the content has Custom Add views and
+ the container contains only a single content object
+
+ >>> registerAddMenu()
+ >>> defineMenuItem(AddMenu, IAdding, '', 'item3', extra={'factory': 'f1'})
+
+ >>> class F1(object):
+ ... pass
+
+ >>> class F2(object):
+ ... pass
+
+ >>> def pre(container, name, object):
+ ... if not isinstance(object, F1):
+ ... raise zope.interface.Invalid()
+ >>> def prefactory(container, name, factory):
+ ... if factory._callable is not F1:
+ ... raise zope.interface.Invalid()
+ >>> pre.factory = prefactory
+
+
+ >>> class IContainer(zope.interface.Interface):
+ ... def __setitem__(name, object):
+ ... pass
+ ... __setitem__.precondition = pre
+
+
+ >>> class Container(object):
+ ... zope.interface.implements(IContainer)
+
+ >>> from zope.component.factory import Factory
+ >>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
+ >>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
+
+ >>> from zmi.core.container.adding import Adding
+ >>> adding = Adding(Container(), TestRequest())
+ >>> items = adding.addingInfo()
+ >>> len(items)
+ 1
+
+ isSingleMenuItem returns True if there is only one content class
+ inside the Container
+
+ >>> adding.isSingleMenuItem()
+ True
+
+ hasCustomAddView will return False as the content does not have
+ a custom Add View
+
+ >>> adding.hasCustomAddView()
+ True
+
+ """
+
+def test_SingleMenuItem_and_NoCustomAddView_NonICNC():
+ """
+
+ This function checks the case where there is a single content object
+ and there is non custom add view . Also the container does not
+ implement IContainerNamesContainer
+
+ >>> registerAddMenu()
+ >>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''})
+ >>> class F1(object):
+ ... pass
+
+ >>> class F2(object):
+ ... pass
+
+ >>> def pre(container, name, object):
+ ... if not isinstance(object, F1):
+ ... raise zope.interface.Invalid()
+ >>> def prefactory(container, name, factory):
+ ... if factory._callable is not F1:
+ ... raise zope.interface.Invalid()
+ >>> pre.factory = prefactory
+
+
+ >>> class IContainer(zope.interface.Interface):
+ ... def __setitem__(name, object):
+ ... pass
+ ... __setitem__.precondition = pre
+
+
+ >>> class Container(object):
+ ... zope.interface.implements(IContainer)
+
+ >>> from zope.component.factory import Factory
+ >>> ztapi.provideUtility(IFactory, Factory(F1), 'f1')
+ >>> ztapi.provideUtility(IFactory, Factory(F2), 'f2')
+
+ >>> from zmi.core.container.adding import Adding
+ >>> adding = Adding(Container(), TestRequest())
+ >>> items = adding.addingInfo()
+ >>> len(items)
+ 1
+
+ The isSingleMenuItem will return True if there is one single content
+ that can be added inside the Container
+
+ >>> adding.isSingleMenuItem()
+ True
+
+ hasCustomAddView will return False as the content does not have
+ a custom Add View
+
+ >>> adding.hasCustomAddView()
+ False
+
+ """
+
+def test_isSingleMenuItem_with_ICNC():
+ """
+ This test checks for whether there is a single content that can be added
+ and the container uses IContainerNamesContaienr
+
+ >>> registerAddMenu()
+ >>> defineMenuItem(AddMenu, None, '', 'item3', extra={'factory': ''})
+
+ >>> class F1(object):
+ ... pass
+
+ >>> class F2(object):
+ ... pass
+
+ >>> def pre(container, name, object):
+ ... if not isinstance(object, F1):
+ ... raise zope.interface.Invalid()
+ >>> def prefactory(container, name, factory):
+ ... if factory._callable is not F1:
+ ... raise zope.interface.Invalid()
+ >>> pre.factory = prefactory
+
+
+ >>> class IContainer(zope.interface.Interface):
+ ... def __setitem__(name, object):
+ ... pass
+ ... __setitem__.precondition = pre
+
+
+ >>> class Container(object):
+ ... zope.interface.implements(IContainer, IContainerNamesContainer)
+
+ >>> from zmi.core.container.adding import Adding
+ >>> adding = Adding(Container(), TestRequest())
+ >>> items = adding.addingInfo()
+ >>> len(items)
+ 1
+ >>> adding.isSingleMenuItem()
+ True
+ >>> adding.hasCustomAddView()
+ False
+
+ """
+
+def test_suite():
+ return unittest.TestSuite((
+ unittest.makeSuite(Test),
+ DocTestSuite(setUp=setUp, tearDown=tearDown),
+ ))
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Added: zmi.core/trunk/src/zmi/core/container/tests/test_contents.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/test_contents.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/test_contents.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,380 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test Container Contents
+
+$Id: test_contents.py 81660 2007-11-09 19:28:02Z philikon $
+"""
+from unittest import TestCase, TestSuite, main, makeSuite
+
+from zope.interface import Interface, implements
+from zope.security import checker
+from zope.traversing.api import traverse
+
+from zope.component.eventtesting import getEvents
+
+from zope.annotation.interfaces import IAnnotations
+from zope.copypastemove import ContainerItemRenamer
+from zope.copypastemove import ObjectMover, ObjectCopier
+from zope.copypastemove import PrincipalClipboard
+from zope.copypastemove.interfaces import IContainerItemRenamer
+from zope.copypastemove.interfaces import IObjectMover, IObjectCopier
+from zope.copypastemove.interfaces import IPrincipalClipboard
+
+from zope.app.component.testing import PlacefulSetup
+from zope.app.container.contained import contained
+from zope.app.testing import ztapi
+from zope.app.container.interfaces import IContainer, IContained
+
+
+class BaseTestContentsBrowserView(PlacefulSetup):
+ """Base class for testing browser contents.
+
+ Subclasses need to define a method, '_TestView__newContext', that
+ takes no arguments and that returns a new empty test view context.
+
+ Subclasses need to define a method, '_TestView__newView', that
+ takes a context object and that returns a new test view.
+ """
+
+ def setUp(self):
+ PlacefulSetup.setUp(self)
+ PlacefulSetup.buildFolders(self)
+
+ ztapi.provideAdapter(IContained, IObjectCopier, ObjectCopier)
+ ztapi.provideAdapter(IContained, IObjectMover, ObjectMover)
+ ztapi.provideAdapter(IContainer, IContainerItemRenamer,
+ ContainerItemRenamer)
+
+ ztapi.provideAdapter(IAnnotations, IPrincipalClipboard,
+ PrincipalClipboard)
+ ztapi.provideAdapter(Principal, IAnnotations,
+ PrincipalAnnotations)
+
+ def testInfo(self):
+ # Do we get the correct information back from ContainerContents?
+ container = self._TestView__newContext()
+ subcontainer = self._TestView__newContext()
+ container['subcontainer'] = subcontainer
+ document = Document()
+ container['document'] = document
+
+ fc = self._TestView__newView(container)
+ info_list = fc.listContentInfo()
+
+ self.assertEquals(len(info_list), 2)
+
+ ids = map(lambda x: x['id'], info_list)
+ self.assert_('subcontainer' in ids)
+
+ objects = map(lambda x: x['object'], info_list)
+ self.assert_(subcontainer in objects)
+
+ urls = map(lambda x: x['url'], info_list)
+ self.assert_('subcontainer' in urls)
+
+ self.failIf(filter(None, map(lambda x: x['icon'], info_list)))
+
+ def testInfoUnicode(self):
+ # If the id contains non-ASCII characters, url has to be quoted
+ container = self._TestView__newContext()
+ subcontainer = self._TestView__newContext()
+ container[u'f\xf6\xf6'] = subcontainer
+
+ fc = self._TestView__newView(container)
+ info_list = fc.listContentInfo()
+
+ urls = map(lambda x: x['url'], info_list)
+ self.assert_('f%C3%B6%C3%B6' in urls)
+
+ def testInfoWDublinCore(self):
+ container = self._TestView__newContext()
+ document = Document()
+ container['document'] = document
+
+ from datetime import datetime
+ from zope.dublincore.interfaces import IZopeDublinCore
+ class FauxDCAdapter(object):
+ implements(IZopeDublinCore)
+
+ __Security_checker__ = checker.Checker(
+ {"created": "zope.Public",
+ "modified": "zope.Public",
+ "title": "zope.Public",
+ },
+ {"title": "zope.app.dublincore.change"})
+
+ def __init__(self, context):
+ pass
+ title = 'faux title'
+ size = 1024
+ created = datetime(2001, 1, 1, 1, 1, 1)
+ modified = datetime(2002, 2, 2, 2, 2, 2)
+
+ ztapi.provideAdapter(IDocument, IZopeDublinCore, FauxDCAdapter)
+
+ fc = self._TestView__newView(container)
+ info = fc.listContentInfo()[0]
+
+ self.assertEqual(info['id'], 'document')
+ self.assertEqual(info['url'], 'document')
+ self.assertEqual(info['object'], document)
+ self.assertEqual(info['title'], 'faux title')
+ self.assertEqual(info['created'], '01/01/01 01:01')
+ self.assertEqual(info['modified'], '02/02/02 02:02')
+
+ def testRemove(self):
+ container = self._TestView__newContext()
+ subcontainer = self._TestView__newContext()
+ container['subcontainer'] = subcontainer
+ document = Document()
+ container['document'] = document
+ document2 = Document()
+ container['document2'] = document2
+
+ fc = self._TestView__newView(container)
+
+ fc.request.form.update({'ids': ['document2']})
+
+ fc.removeObjects()
+
+ info_list = fc.listContentInfo()
+
+ self.assertEquals(len(info_list), 2)
+
+ ids = map(lambda x: x['id'], info_list)
+ self.assert_('subcontainer' in ids)
+
+ objects = map(lambda x: x['object'], info_list)
+ self.assert_(subcontainer in objects)
+
+ urls = map(lambda x: x['url'], info_list)
+ self.assert_('subcontainer' in urls)
+
+ def testChangeTitle(self):
+ container = self._TestView__newContext()
+ document = Document()
+ container['document'] = document
+
+ from zope.dublincore.interfaces import IDCDescriptiveProperties
+ class FauxDCDescriptiveProperties(object):
+ implements(IDCDescriptiveProperties)
+
+ __Security_checker__ = checker.Checker(
+ {"title": "zope.Public",
+ },
+ {"title": "zope.app.dublincore.change"})
+
+ def __init__(self, context):
+ self.context = context
+
+ def setTitle(self, title):
+ self.context.title = title
+
+ def getTitle(self):
+ return self.context.title
+
+ title = property(getTitle, setTitle)
+
+ ztapi.provideAdapter(IDocument, IDCDescriptiveProperties, FauxDCDescriptiveProperties)
+
+ fc = self._TestView__newView(container)
+
+ dc = IDCDescriptiveProperties(document)
+
+ fc.request.form.update({'retitle_id': 'document', 'new_value': 'new'})
+ fc.changeTitle()
+ events = getEvents()
+ self.assertEquals(dc.title, 'new')
+ self.failIf('title' not in events[-1].descriptions[0].attributes)
+
+
+
+class IDocument(Interface):
+ pass
+
+class Document(object):
+ implements(IDocument)
+
+
+class Principal(object):
+
+ id = 'bob'
+
+class PrincipalAnnotations(dict):
+ implements(IAnnotations)
+ data = {}
+ def __new__(class_, context):
+ try:
+ annotations = class_.data[context.id]
+ except KeyError:
+ annotations = dict.__new__(class_)
+ class_.data[context.id] = annotations
+ return annotations
+ def __init__(self, context):
+ pass
+ def __repr__(self):
+ return "<%s.PrincipalAnnotations object>" % __name__
+
+
+class TestCutCopyPaste(PlacefulSetup, TestCase):
+
+ def setUp(self):
+ PlacefulSetup.setUp(self)
+ PlacefulSetup.buildFolders(self)
+ ztapi.provideAdapter(IContained, IObjectCopier, ObjectCopier)
+ ztapi.provideAdapter(IContained, IObjectMover, ObjectMover)
+ ztapi.provideAdapter(IContainer, IContainerItemRenamer,
+ ContainerItemRenamer)
+
+ ztapi.provideAdapter(IAnnotations, IPrincipalClipboard,
+ PrincipalClipboard)
+ ztapi.provideAdapter(Principal, IAnnotations,
+ PrincipalAnnotations)
+
+ def testRename(self):
+ container = traverse(self.rootFolder, 'folder1')
+ fc = self._TestView__newView(container)
+ ids=['document1', 'document2']
+ for id in ids:
+ document = Document()
+ container[id] = document
+ fc.request.form.update({'rename_ids': ids,
+ 'new_value': ['document1_1', 'document2_2']
+ })
+ fc.renameObjects()
+ self.failIf('document1_1' not in container)
+ self.failIf('document1' in container)
+
+ def testCopyPaste(self):
+ container = traverse(self.rootFolder, 'folder1')
+ fc = self._TestView__newView(container)
+ ids=['document1', 'document2']
+ for id in ids:
+ document = Document()
+ container[id] = document
+
+ fc.request.form['ids'] = ids
+ fc.copyObjects()
+ fc.pasteObjects()
+ self.failIf('document1' not in container)
+ self.failIf('document2' not in container)
+ self.failIf('document1-2' not in container)
+ self.failIf('document2-2' not in container)
+
+ def testCopyFolder(self):
+ container = traverse(self.rootFolder, 'folder1')
+ fc = self._TestView__newView(container)
+ ids = ['folder1_1']
+ fc.request.form['ids'] = ids
+ fc.copyObjects()
+ fc.pasteObjects()
+ self.failIf('folder1_1' not in container)
+ self.failIf('folder1_1-2' not in container)
+
+ def testCopyFolder2(self):
+ container = traverse(self.rootFolder, '/folder1/folder1_1')
+ fc = self._TestView__newView(container)
+ ids = ['folder1_1_1']
+ fc.request.form['ids'] = ids
+ fc.copyObjects()
+ fc.pasteObjects()
+ self.failIf('folder1_1_1' not in container)
+ self.failIf('folder1_1_1-2' not in container)
+
+ def testCopyFolder3(self):
+ container = traverse(self.rootFolder, '/folder1/folder1_1')
+ target = traverse(self.rootFolder, '/folder2/folder2_1')
+ fc = self._TestView__newView(container)
+ tg = self._TestView__newView(target)
+ ids = ['folder1_1_1']
+ fc.request.form['ids'] = ids
+ fc.copyObjects()
+ tg.pasteObjects()
+ self.failIf('folder1_1_1' not in container)
+ self.failIf('folder1_1_1' not in target)
+
+ def testCutPaste(self):
+ container = traverse(self.rootFolder, 'folder1')
+ fc = self._TestView__newView(container)
+ ids=['document1', 'document2']
+ for id in ids:
+ document = Document()
+ container[id] = document
+ fc.request.form['ids'] = ids
+ fc.cutObjects()
+ fc.pasteObjects()
+ self.failIf('document1' not in container)
+ self.failIf('document2' not in container)
+
+ def testCutFolder(self):
+ container = traverse(self.rootFolder, 'folder1')
+ fc = self._TestView__newView(container)
+ ids = ['folder1_1']
+ fc.request.form['ids'] = ids
+ fc.cutObjects()
+ fc.pasteObjects()
+ self.failIf('folder1_1' not in container)
+
+ def testCutFolder2(self):
+ container = traverse(self.rootFolder, '/folder1/folder1_1')
+ fc = self._TestView__newView(container)
+ ids = ['folder1_1_1']
+ fc.request.form['ids'] = ids
+ fc.cutObjects()
+ fc.pasteObjects()
+ self.failIf('folder1_1_1' not in container)
+
+ def testCutFolder3(self):
+ container = traverse(self.rootFolder, '/folder1/folder1_1')
+ target = traverse(self.rootFolder, '/folder2/folder2_1')
+ fc = self._TestView__newView(container)
+ tg = self._TestView__newView(target)
+ ids = ['folder1_1_1']
+ fc.request.form['ids'] = ids
+ fc.cutObjects()
+ tg.pasteObjects()
+ self.failIf('folder1_1_1' in container)
+ self.failIf('folder1_1_1' not in target)
+
+ def _TestView__newView(self, container):
+ from zope.app.container.browser.contents import Contents
+ from zope.publisher.browser import TestRequest
+ request = TestRequest()
+ request.setPrincipal(Principal())
+ return Contents(container, request)
+
+class Test(BaseTestContentsBrowserView, TestCase):
+
+ def _TestView__newContext(self):
+ from zope.app.container.sample import SampleContainer
+ from zope.app.folder import rootFolder
+ root = rootFolder()
+ container = SampleContainer()
+ return contained(container, root, 'sample')
+
+ def _TestView__newView(self, container):
+ from zope.app.container.browser.contents import Contents
+ from zope.publisher.browser import TestRequest
+ request = TestRequest()
+ request.setPrincipal(Principal())
+ return Contents(container, request)
+
+def test_suite():
+ return TestSuite((
+ makeSuite(Test),
+ makeSuite(TestCutCopyPaste),
+ ))
+
+if __name__=='__main__':
+ main(defaultTest='test_suite')
Added: zmi.core/trunk/src/zmi/core/container/tests/test_contents_functional.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/test_contents_functional.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/test_contents_functional.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,380 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Functional tests for the Container's 'Contents' view
+
+$Id: test_contents_functional.py 87310 2008-06-11 09:06:34Z ccomb $
+"""
+
+import unittest
+import os
+
+from persistent import Persistent
+import transaction
+from zope import copypastemove
+from zope.interface import implements, Interface
+from zope.annotation.interfaces import IAttributeAnnotatable
+from zope.dublincore.interfaces import IZopeDublinCore
+
+from zope.app.container.interfaces import IReadContainer, IContained
+from zope.app.testing import ztapi
+from zope.app.testing.functional import BrowserTestCase
+from zope.app.testing.functional import FunctionalDocFileSuite
+from zope.app.testing.functional import ZCMLLayer
+
+ZMICoreContainerLayer = ZCMLLayer(
+ os.path.join(os.path.split(__file__)[0], 'ftesting.zcml'),
+ __name__, 'ZMICoreContainerLayer', allow_teardown=True)
+
+
+class IImmovable(Interface):
+ """Marker interface for immovable objects."""
+
+class IUncopyable(Interface):
+ """Marker interface for uncopyable objects."""
+
+class File(Persistent):
+ implements(IAttributeAnnotatable)
+
+class ImmovableFile(File):
+ implements(IImmovable)
+
+class UncopyableFile(File):
+ implements(IUncopyable)
+
+class ObjectNonCopier(copypastemove.ObjectCopier):
+
+ def copyable(self):
+ return False
+
+class ObjectNonMover(copypastemove.ObjectMover):
+
+ def moveable(self):
+ return False
+
+class ReadOnlyContainer(Persistent):
+ implements(IReadContainer, IContained)
+ __parent__ = __name__ = None
+
+ def __init__(self): self.data = {}
+ def keys(self): return self.data.keys()
+ def __getitem__(self, key): return self.data[key]
+ def get(self, key, default=None): return self.data.get(key, default)
+ def __iter__(self): return iter(self.data)
+ def values(self): return self.data.values()
+ def __len__(self): return len(self.data)
+ def items(self): return self.data.items()
+ def __contains__(self, key): return key in self.data
+ def has_key(self, key): return self.data.has_key(key)
+
+
+class Test(BrowserTestCase):
+
+ def test_inplace_add(self):
+ root = self.getRootFolder()
+ self.assert_('foo' not in root)
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'type_name': u'zope.app.content.File'})
+ body = ' '.join(response.getBody().split())
+ self.assert_(body.find('type="hidden" name="type_name"') >= 0)
+ self.assert_(body.find('input name="new_value"') >= 0)
+ self.assert_(body.find('type="submit" name="container_cancel_button"')
+ >= 0)
+ self.assert_(body.find('type="submit" name="container_rename_button"')
+ < 0)
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'type_name': u'zope.app.content.File',
+ 'new_value': 'foo'})
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+
+ root._p_jar.sync()
+ self.assert_('foo' in root)
+
+ def test_inplace_rename_multiple(self):
+ root = self.getRootFolder()
+ root['foo'] = File()
+ self.assert_('foo' in root)
+ transaction.commit()
+
+ # Check that we don't change mode if there are no items selected
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'container_rename_button': u''})
+ body = ' '.join(response.getBody().split())
+ self.assert_(body.find('input name="new_value:list"') < 0)
+ self.assert_(body.find('type="submit" name="container_cancel_button"')
+ < 0)
+ self.assert_(body.find('type="submit" name="container_rename_button"')
+ >= 0)
+ self.assert_(body.find('div class="page_error"')
+ >= 0)
+
+
+ # Check normal multiple select
+
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'container_rename_button': u'',
+ 'ids': ['foo']})
+ body = ' '.join(response.getBody().split())
+ self.assert_(body.find('input name="new_value:list"') >= 0)
+ self.assert_(body.find('type="submit" name="container_cancel_button"')
+ >= 0)
+ self.assert_(body.find('type="submit" name="container_rename_button"')
+ < 0)
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'rename_ids': ['foo'],
+ 'new_value': ['bar']})
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+
+ root._p_jar.sync()
+ self.assert_('foo' not in root)
+ self.assert_('bar' in root)
+
+
+ def test_inplace_rename_single(self):
+ root = self.getRootFolder()
+ root['foo'] = File()
+ self.assert_('foo' in root)
+ transaction.commit()
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'rename_ids': ['foo']})
+ body = ' '.join(response.getBody().split())
+ self.assert_(body.find('input name="new_value:list"') >= 0)
+ self.assert_(body.find('type="submit" name="container_cancel_button"')
+ >= 0)
+ self.assert_(body.find('type="submit" name="container_rename_button"')
+ < 0)
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'rename_ids': ['foo'],
+ 'new_value': ['bar']})
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+
+ root._p_jar.sync()
+ self.assert_('foo' not in root)
+ self.assert_('bar' in root)
+
+ def test_inplace_change_title(self):
+ root = self.getRootFolder()
+ root['foo'] = File()
+ transaction.commit()
+ self.assert_('foo' in root)
+ dc = IZopeDublinCore(root['foo'])
+ self.assert_(dc.title == '')
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'retitle_id': u'foo'})
+ body = ' '.join(response.getBody().split())
+ self.assert_(body.find('type="hidden" name="retitle_id"') >= 0)
+ self.assert_(body.find('input name="new_value"') >= 0)
+ self.assert_(body.find('type="submit" name="container_cancel_button"')
+ >= 0)
+ self.assert_(body.find('type="submit" name="container_rename_button"')
+ < 0)
+
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'retitle_id': u'foo',
+ 'new_value': u'test title'})
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+
+ root._p_jar.sync()
+ self.assert_('foo' in root)
+ dc = IZopeDublinCore(root['foo'])
+ self.assert_(dc.title == 'test title')
+
+
+ def test_pasteable_for_deleted_clipboard_item(self):
+ """Tests Paste button visibility when copied item is deleted."""
+
+ root = self.getRootFolder()
+ root['foo'] = File() # item to be copied/deleted
+ root['bar'] = File() # ensures that there's always an item in
+ # the collection view
+ transaction.commit()
+
+ # confirm foo in contents, Copy button visible, Paste not visible
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(response.getBody().find(
+ '<a href="foo/@@SelectedManagementView.html">foo</a>') != -1)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_copy_button"') != -1)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_paste_button"') == -1)
+
+ # copy foo - confirm Paste visible
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
+ 'ids' : ('foo',),
+ 'container_copy_button' : '' })
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_paste_button"') != -1)
+
+ # delete foo -> nothing valid to paste -> Paste should not be visible
+ del root['foo']
+ transaction.commit()
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_paste_button"') == -1)
+
+
+ def test_paste_for_deleted_clipboard_item(self):
+ """Tests paste operation when one of two copied items is deleted."""
+
+ root = self.getRootFolder()
+ root['foo'] = File()
+ root['bar'] = File()
+ transaction.commit()
+
+ # confirm foo/bar in contents, Copy button visible, Paste not visible
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(response.getBody().find(
+ '<a href="foo/@@SelectedManagementView.html">foo</a>') != -1)
+ self.assert_(response.getBody().find(
+ '<a href="bar/@@SelectedManagementView.html">bar</a>') != -1)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_copy_button"') != -1)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_paste_button"') == -1)
+
+ # copy foo and bar - confirm Paste visible
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
+ 'ids' : ('foo', 'bar'),
+ 'container_copy_button' : '' })
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_paste_button"') != -1)
+
+ # delete only foo -> bar still available -> Paste should be visible
+ del root['foo']
+ transaction.commit()
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ self.assert_(response.getBody().find(
+ '<input type="submit" name="container_paste_button"') != -1)
+
+ # paste clipboard contents - only bar should be copied
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
+ 'container_paste_button' : '' })
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+ root._p_jar.sync()
+ self.assertEqual(tuple(root.keys()), ('bar', 'bar-2'))
+
+ def test_readonly_display(self):
+ root = self.getRootFolder()
+ root['foo'] = ReadOnlyContainer()
+ transaction.commit()
+ response = self.publish('/foo/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+
+ def test_uncopyable_object(self):
+ ztapi.provideAdapter(IUncopyable,
+ copypastemove.interfaces.IObjectCopier,
+ ObjectNonCopier)
+ root = self.getRootFolder()
+ root['uncopyable'] = UncopyableFile()
+ transaction.commit()
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'ids': [u'uncopyable'],
+ 'container_copy_button': u'Copy'})
+ self.assertEqual(response.getStatus(), 200)
+ body = response.getBody()
+ self.assert_("cannot be copied" in body)
+
+ def test_unmoveable_object(self):
+ ztapi.provideAdapter(IImmovable,
+ copypastemove.interfaces.IObjectMover,
+ ObjectNonMover)
+ root = self.getRootFolder()
+ root['immovable'] = ImmovableFile()
+ transaction.commit()
+ response = self.publish('/@@contents.html',
+ basic='mgr:mgrpw',
+ form={'ids': [u'immovable'],
+ 'container_cut_button': u'Cut'})
+ self.assertEqual(response.getStatus(), 200)
+ body = response.getBody()
+ self.assert_("cannot be moved" in body)
+
+ def test_copy_then_delete_with_unicode_name(self):
+ """Tests unicode on object copied then deleted (#238579)."""
+
+ # create a file with an accentuated unicode name
+ root = self.getRootFolder()
+ root[u'voil\xe0'] = File()
+ transaction.commit()
+
+ # copy the object
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw', form={
+ 'ids' : (u'voil\xe0',),
+ 'container_copy_button' : '' })
+ self.assertEqual(response.getStatus(), 302)
+ self.assertEqual(response.getHeader('Location'),
+ 'http://localhost/@@contents.html')
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+
+ # delete the object
+ del root[u'voil\xe0']
+ transaction.commit()
+ response = self.publish('/@@contents.html', basic='mgr:mgrpw')
+ self.assertEqual(response.getStatus(), 200)
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ Test.layer = ZMICoreContainerLayer
+ suite.addTest(unittest.makeSuite(Test))
+ index = FunctionalDocFileSuite("index.txt")
+ index.layer = ZMICoreContainerLayer
+ suite.addTest(index)
+ return suite
+
+if __name__=='__main__':
+ unittest.main(defaultTest='test_suite')
Added: zmi.core/trunk/src/zmi/core/container/tests/test_directive.py
===================================================================
--- zmi.core/trunk/src/zmi/core/container/tests/test_directive.py (rev 0)
+++ zmi.core/trunk/src/zmi/core/container/tests/test_directive.py 2009-06-07 15:25:08 UTC (rev 100691)
@@ -0,0 +1,294 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""'containerView' directive test
+
+$Id: test_directive.py 67630 2006-04-27 00:54:03Z jim $
+"""
+import re
+import pprint
+import cStringIO
+
+import unittest
+from zope.interface import Interface
+from zope.publisher.interfaces.browser import IBrowserRequest
+from zope.testing.doctestunit import DocTestSuite
+from zope.app.container.browser.metaconfigure import containerViews
+
+atre = re.compile(' at [0-9a-fA-Fx]+')
+
+class Context(object):
+ actions = ()
+ info = ''
+
+ def action(self, discriminator, callable, args):
+ self.actions += ((discriminator, callable, args), )
+ self.info = 'info'
+
+ def __repr__(self):
+ stream = cStringIO.StringIO()
+ pprinter = pprint.PrettyPrinter(stream=stream, width=60)
+ pprinter.pprint(self.actions)
+ r = stream.getvalue()
+ return (''.join(atre.split(r))).strip()
+
+class I(Interface):
+ pass
+
+
+class ITestLayer(IBrowserRequest):
+ pass
+
+
+def test_containerViews():
+ """
+ >>> from zope.app.publisher.browser.menumeta import menus
+ >>> from zope.interface.interface import InterfaceClass
+ >>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus')
+ >>> menus.zmi_views = zmi_views
+ >>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus')
+ >>> menus.zmi_actions = zmi_actions
+
+ >>> context = Context()
+ >>> containerViews(context, for_=I, contents='zope.ManageContent',
+ ... add='zope.ManageContent', index='zope.View')
+ >>> context
+ ((('adapter',
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.app.menus.zmi_views>,
+ u'Contents'),
+ <function handler>,
+ ('registerAdapter',
+ <zope.app.publisher.browser.menumeta.MenuItemFactory object>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.app.menus.zmi_views>,
+ u'Contents',
+ '')),
+ (None,
+ <function provideInterface>,
+ ('', <InterfaceClass zope.app.menus.zmi_views>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (('view',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>,
+ 'contents.html',
+ <InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <function handler>,
+ ('registerAdapter',
+ <class 'zope.app.publisher.browser.viewmeta.Contents'>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.interface.Interface>,
+ 'contents.html',
+ 'info')),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (('view',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>,
+ 'index.html',
+ <InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <function handler>,
+ ('registerAdapter',
+ <class 'zope.app.publisher.browser.viewmeta.Contents'>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.interface.Interface>,
+ 'index.html',
+ 'info')),
+ (('adapter',
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.app.menus.zmi_actions>,
+ u'Add'),
+ <function handler>,
+ ('registerAdapter',
+ <zope.app.publisher.browser.menumeta.MenuItemFactory object>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.app.menus.zmi_actions>,
+ u'Add',
+ 'info')),
+ (None,
+ <function provideInterface>,
+ ('', <InterfaceClass zope.app.menus.zmi_actions>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (None,
+ <function provideInterface>,
+ ('', <InterfaceClass zope.interface.Interface>)),
+ (('view',
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ '+',
+ <InterfaceClass zope.interface.Interface>),
+ <function handler>,
+ ('registerAdapter',
+ <class 'zope.app.publisher.browser.viewmeta.+'>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zope.publisher.interfaces.browser.IDefaultBrowserLayer>),
+ <InterfaceClass zope.interface.Interface>,
+ '+',
+ 'info')))
+ """
+
+def test_containerViews_layer():
+ """
+ >>> from zope.app.publisher.browser.menumeta import menus
+ >>> from zope.interface.interface import InterfaceClass
+ >>> zmi_views = InterfaceClass('zmi_views', __module__='zope.app.menus')
+ >>> menus.zmi_views = zmi_views
+ >>> zmi_actions = InterfaceClass('zmi_actions', __module__='zope.app.menus')
+ >>> menus.zmi_actions = zmi_actions
+
+ >>> context = Context()
+ >>> containerViews(context, for_=I, contents='zope.ManageContent',
+ ... add='zope.ManageContent', index='zope.View', layer=ITestLayer)
+ >>> context
+ ((('adapter',
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.app.menus.zmi_views>,
+ u'Contents'),
+ <function handler>,
+ ('registerAdapter',
+ <zope.app.publisher.browser.menumeta.MenuItemFactory object>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.app.menus.zmi_views>,
+ u'Contents',
+ '')),
+ (None,
+ <function provideInterface>,
+ ('', <InterfaceClass zope.app.menus.zmi_views>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (('view',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>,
+ 'contents.html',
+ <InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <function handler>,
+ ('registerAdapter',
+ <class 'zope.app.publisher.browser.viewmeta.Contents'>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.interface.Interface>,
+ 'contents.html',
+ 'info')),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (('view',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>,
+ 'index.html',
+ <InterfaceClass zope.publisher.interfaces.browser.IBrowserRequest>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <function handler>,
+ ('registerAdapter',
+ <class 'zope.app.publisher.browser.viewmeta.Contents'>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.interface.Interface>,
+ 'index.html',
+ 'info')),
+ (('adapter',
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.app.menus.zmi_actions>,
+ u'Add'),
+ <function handler>,
+ ('registerAdapter',
+ <zope.app.publisher.browser.menumeta.MenuItemFactory object>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.app.menus.zmi_actions>,
+ u'Add',
+ 'info')),
+ (None,
+ <function provideInterface>,
+ ('', <InterfaceClass zope.app.menus.zmi_actions>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>)),
+ (None,
+ <function provideInterface>,
+ ('',
+ <InterfaceClass zmi.core.container.tests.test_directive.I>)),
+ (None,
+ <function provideInterface>,
+ ('', <InterfaceClass zope.interface.Interface>)),
+ (('view',
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ '+',
+ <InterfaceClass zope.interface.Interface>),
+ <function handler>,
+ ('registerAdapter',
+ <class 'zope.app.publisher.browser.viewmeta.+'>,
+ (<InterfaceClass zmi.core.container.tests.test_directive.I>,
+ <InterfaceClass zmi.core.container.tests.test_directive.ITestLayer>),
+ <InterfaceClass zope.interface.Interface>,
+ '+',
+ 'info')))
+ """
+
+
+def test_suite():
+ return unittest.TestSuite((
+ DocTestSuite(),
+ ))
+
+if __name__ == '__main__':
+ unittest.main()
More information about the Checkins
mailing list