Update Five to version 1.1. The biggest change in Five 1.1 is a

restructuring of the source code package layout and i18n through
Zope 3 message catalogs by default (fallback to older translation
services is possible).

Updates to Five 1.2 and eventually 1.3 will follow as those versions
are released. Zope 2.9 will be released with Five 1.3 (which will have
Zope 3.2 support)
parent 2c82b50d
...@@ -2,6 +2,126 @@ ...@@ -2,6 +2,126 @@
Five Changes Five Changes
============ ============
Five 1.1 (2005-10-04)
=====================
Features
--------
* When Zope was not in debug mode, an error in a ZCML file would cause Five to
stop loading ZCML completely, making all subsequent products "dead". The
effect would typically be that objects appeared to have no views at all.
Now a ZCML error will only stop the ZCML loading for that product, but the
rest of the products will load as usual. A traceback will still be printed
in the event log.
In debug mode the behaviour has not changed; a ZCML error will stop Zope
startup completely, and print a traceback if running in foreground mode.
Restructuring
-------------
* The deprecated FivePageTemplateFile was removed, and the erroneous use of
this by EditView was changed.
Bugfixes
--------
* Repaired 'forms.txt' test which expected an error page when passing
'handle_errors' as False; it now expects an Unauthorized traceback.
Note that this test fails on Zope 2.8.1, which incorrectly ignored
'handle_errors'.
* FiveTraversable should only do a view lookup and not call the traverse
method of its superclass.
* Fixed manage_beforeDelete triggering for classes using five:sendEvents.
* The redefinePermission directive was falsely registered under the
``zope`` namespace, not the ``meta`` namespace as it is in Zope 3.
* Some parts of add.pt and edit.pt were not translated correctly or not
translated at all. The fix depends on TAL changes in Zope 2.8.1 and changes
in Zope X3-3.0.1 (shipped with 2.8.1). Form i18n is still broken with older
Zope versions.
* 'zope' domain translations are now set up by default. Form i18n needs them.
* Added backwards compatibility for some moved classes (AddForm, EditForm,
ContentAdding)
* The ZPT variable 'container' makes little sense in Zope3/Five, but is now
always set to be the same as 'here' which is normal Zope2 behaviour.
It is in Five 1.0.x set to be the same as 'view' which breaks some templates.
* In some hard to replicate cases, using the "modules" variable in ZPT cause
an AuthenticationError. Using the secure module importer fixes this.
* If you used some parts of Zope 3 (for example the mail delivery) Five 1.1
transaction backport would conflict with Zope 3s transaction module.
This is now solved.
Five 1.1b (2005-07-13)
======================
Features
--------
* Zope 3-style i18n support has been provided. Apart from being able
to register translations through ZCML, Five now lets Zope 2 ZPTs
automatically use Zope 3 translation domains. Fallback to an
old-style translation service (e.g. Localizer or PTS) is supported.
This also includes the detection of preferred languages. See
``doc/i18n.txt`` for more information.
* Added support for Zope 3 -> Zope 2 interface bridging. This
functionality will be part of Zope 2.9, with Five you can already
use it in Zope 2.7/2.8. Since Zope 2 interfaces are rarely used and
their Zope 3 equivalents are more meaningful (for the Component
Architecture), the preferred way of dealing with interface migration
is to write Zope 3 interfaces and bridge them to Zope 2 ones as
needed. To bridge, use the ``Interface.bridge.fromZ3Interface()``
function.
* Support for the standard <factory />, <modulealias /> and <hook />
ZCML directives was added.
* The default browser view name for all objects is now 'index.html',
just as it is in Zope 3. This means that a view by that name will
be looked up if no specific view name is given in the URL.
Restructuring
-------------
* Restructured the Five source code to be easier to navigate in.
Three subpackages were created, Five.browser, Five.form and
Five.skin.
* The former test product, ``FiveTest``, was converted into separate
modules that provide the mock objects for the corresponding tests
and are located right next to them. Common test helpers have been
moved to the Five.testing package. Overall, the testing framework
was much simplified and the individual tests clean up after
themselves much like they do in Zope 3.
* Relocated Zope core interfaces. Future Zope versions will ship with their
own z3 interfaces. Five now patches the older Zope versions to make sure
you can always find the interfaces in 'AccessControl.interfaces',
'Acquisition.interfaces', 'App.interfaces', 'OFS.interfaces' and
'webdav.interfaces'. Please don't use the aliases in 'Five.interfaces' or
'Five.bbb.*interfaces' - they are only provided for backwards
compatibility.
* Zope 2.8 HTTPRequest is no longer patched. It has the required methods.
Bugfixes
--------
* The ZPT variable 'container' did not always contain the parent object
of the context.
* The deprecated get_transaction method is no longer used in Zope 2.8.
Five 1.0.2 (2005-07-12) Five 1.0.2 (2005-07-12)
======================= =======================
......
...@@ -29,6 +29,10 @@ Five contributors ...@@ -29,6 +29,10 @@ Five contributors
- Yvo Schubbe (y.2005-@wcm-solutions.de) - Yvo Schubbe (y.2005-@wcm-solutions.de)
- Malcolm Cleaton (malcolm@jamkit.com)
- Tarek Ziad (tziade@nuxeo.com)
Thank you Thank you
--------- ---------
......
====
TODO
====
- more extensive testing whether event system works with things like Zope 2
folders etc
- ensuring that the event-sending behavior is as close to Zope 3's as
possible. A lot of edge cases with different behavior likely remain,
and things like IObjectModifiedEvents are not sent yet for folders.
- allow the multiple use of five:sendEvents
- allow Zope2 boilerplate context.registerClass be configured through zcml
- Figure out where add-view redirects should go.
- Instructions on using add views.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Initialize the Five product """Initialize the Five product
$Id: __init__.py 12915 2005-05-31 10:23:19Z philikon $ $Id: __init__.py 12884 2005-05-30 13:10:41Z philikon $
""" """
import Acquisition import Acquisition
from Globals import INSTANCE_HOME from Globals import INSTANCE_HOME
...@@ -23,7 +23,7 @@ import zcml ...@@ -23,7 +23,7 @@ import zcml
# public API provided by Five # public API provided by Five
# usage: from Products.Five import <something> # usage: from Products.Five import <something>
from browser import BrowserView from browser import BrowserView
from skin import StandardMacros from skin.standardmacros import StandardMacros
def initialize(context): def initialize(context):
zcml.load_site() zcml.load_site()
############################################################################## ##############################################################################
# #
# Copyright (c) 2002 Zope Corporation and Contributors. # Copyright (c) 2004, 2005 Zope Corporation and Contributors.
# All Rights Reserved. # All Rights Reserved.
# #
# This software is subject to the provisions of the Zope Public License, # This software is subject to the provisions of the Zope Public License,
...@@ -11,251 +11,16 @@ ...@@ -11,251 +11,16 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Adding View """ Z2 -> Z3 bridge utilities.
The Adding View is used to add new objects to a container. It is sort of a $Id: adding.py 15515 2005-08-02 17:42:08Z yuppie $
factory screen.
""" """
__docformat__ = 'restructuredtext'
from warnings import warn # BBB: This file will be removed in future versions of Five.
from zope.interface import implements from browser.adding import ContentAdding
from zope.publisher.interfaces import IPublishTraverse
from zope.component.interfaces import IFactory
from zope.app.exception.interfaces import UserError import warnings
from zope.app.container.interfaces import IAdding, INameChooser warnings.warn("\nThe Products.Five.adding module has been renamed to "
from zope.app.container.interfaces import IContainerNamesContainer "Products.Five.browser.adding \n"
from zope.app.container.constraints import checkFactory, checkObject "and will be disabled starting in Five 1.2.\n",
DeprecationWarning, stacklevel=2)
from zope.app import zapi
from zope.app.event.objectevent import ObjectCreatedEvent
from zope.event import notify
from zExceptions import BadRequest
from Products.Five import BrowserView
from Products.Five.traversable import Traversable
from Products.Five.pagetemplatefile import ZopeTwoPageTemplateFile
from Acquisition import Implicit
from OFS.SimpleItem import SimpleItem
class BasicAdding(Implicit, 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 it's 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)
else:
# Invoke the name chooser even when we have a
# name. It'll do useful things with it like converting
# the incoming unicode to an ASCII string.
name = chooser.chooseName(name, container)
content.id = name
container._setObject(name, content)
self.contentName = name # Set the added object Name
return container._getOb(name)
contentName = None # usually set by Adding traverser
def nextURL(self):
"""See zope.app.container.interfaces.IAdding"""
# XXX this is definitely not right for all or even most uses
# of Five, but can be overridden by an AddView subclass, using
# the class attribute of a zcml:addform directive
return (str(zapi.getView(self.context, "absolute_url", self.request))
+ '/manage_main')
# set in BrowserView.__init__
request = None
context = None
def renderAddButton(self):
warn("The renderAddButton method is deprecated, use nameAllowed",
DeprecationWarning, 2)
def publishTraverse(self, request, name):
"""See zope.app.container.interfaces.IAdding"""
if '=' in name:
view_name, content_name = name.split("=", 1)
self.contentName = content_name
if view_name.startswith('@@'):
view_name = view_name[2:]
return zapi.getView(self, view_name, request)
if name.startswith('@@'):
view_name = name[2:]
else:
view_name = name
view = zapi.queryView(self, view_name, request)
if view is not None:
return view
factory = zapi.queryUtility(IFactory, name)
if factory is None:
return super(BasicAdding, self).publishTraverse(request, name)
return factory
def action(self, type_name='', id=''):
if not type_name:
raise UserError("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 zapi.queryView(self, view_name, self.request) is not None:
url = "%s/%s=%s" % (
zapi.getView(self, "absolute_url", self.request),
type_name, id)
self.request.response.redirect(url)
return
if not self.contentName:
self.contentName = id
factory = zapi.getUtility(IFactory, type_name)
content = factory()
notify(ObjectCreatedEvent(content))
self.add(content)
self.request.response.redirect(self.nextURL())
def namesAccepted(self):
return not IContainerNamesContainer.providedBy(self.context)
def nameAllowed(self):
"""Return whether names can be input by the user."""
return not IContainerNamesContainer.providedBy(self.context)
class Adding(BasicAdding):
menu_id = None
index = ZopeTwoPageTemplateFile("adding.pt")
def addingInfo(self):
"""Return menu data.
This is sorted by title.
"""
container = self.context
menu_service = zapi.getService("BrowserMenu")
result = []
for menu_id in (self.menu_id, 'zope.app.container.add'):
if not menu_id:
continue
for item in menu_service.getMenu(menu_id, self, self.request):
extra = item.get('extra')
if extra:
factory = extra.get('factory')
if factory:
factory = zapi.getUtility(IFactory, factory)
if not checkFactory(container, None, factory):
continue
elif item['extra']['factory'] != item['action']:
item['has_custom_add_view']=True
result.append(item)
result.sort(lambda a, b: cmp(a['title'], b['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
class ContentAdding(Adding, Traversable, SimpleItem):
menu_id = "add_content"
class ObjectManagerNameChooser:
"""A name chooser for a Zope object manager.
"""
implements(INameChooser)
def __init__(self, context):
self.context = context
def checkName(self, name, object):
# ObjectManager can only deal with ASCII names. Specially
# ObjectManager._checkId can only deal with strings.
try:
name = name.encode('ascii')
except UnicodeDecodeError:
raise UserError, "Id must contain only ASCII characters."
try:
self.context._checkId(name, allow_dup=False)
except BadRequest, e:
msg = ' '.join(e.args) or "Id is in use or invalid"
raise UserError, msg
def chooseName(self, name, object):
if not name:
name = object.__class__.__name__
else:
try:
name = name.encode('ascii')
except UnicodeDecodeError:
raise UserError, "Id must contain only ASCII characters."
dot = name.rfind('.')
if dot >= 0:
suffix = name[dot:]
name = name[:dot]
else:
suffix = ''
n = name + suffix
i = 0
while True:
i += 1
try:
self.context._getOb(n)
except AttributeError:
break
n = name + '-' + str(i) + suffix
# Make sure the name is valid. We may have started with
# something bad.
self.checkName(n, object)
return n
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
""" Z2 -> Z3 bridge utilities. """ Z2 -> Z3 bridge utilities.
$Id: bridge.py 12915 2005-05-31 10:23:19Z philikon $ $Id: bridge.py 14504 2005-07-11 15:49:59Z yuppie $
""" """
from Interface._InterfaceClass import Interface as Z2_InterfaceClass from Interface._InterfaceClass import Interface as Z2_InterfaceClass
from Interface import Interface as Z2_Interface from Interface import Interface as Z2_Interface
...@@ -39,9 +39,7 @@ def fromZ2Interface(z2i): ...@@ -39,9 +39,7 @@ def fromZ2Interface(z2i):
return _bridges[z2i] return _bridges[z2i]
name = z2i.getName() name = z2i.getName()
bases = [ fromZ2Interface(x) for x in z2i.getBases() ] bases = [ fromZ2Interface(x) for x in z2i.getBases() ]
attrs = {} attrs = {}
for k, v in z2i.namesAndDescriptions(): for k, v in z2i.namesAndDescriptions():
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Utils to be reused """Utils to be reused
$Id: ReuseUtils.py 12912 2005-05-31 09:58:59Z philikon $ $Id: ReuseUtils.py 12907 2005-05-31 06:26:15Z philikon $
""" """
from new import function from new import function
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Trusted expression """Trusted expression
$Id: TrustedExpression.py 12912 2005-05-31 09:58:59Z philikon $ $Id: TrustedExpression.py 17373 2005-09-08 14:48:27Z regebro $
""" """
from sys import modules from sys import modules
...@@ -22,17 +22,12 @@ from Products.PageTemplates.PythonExpr import PythonExpr ...@@ -22,17 +22,12 @@ from Products.PageTemplates.PythonExpr import PythonExpr
from Products.PageTemplates.Expressions import \ from Products.PageTemplates.Expressions import \
SubPathExpr, PathExpr, \ SubPathExpr, PathExpr, \
StringExpr, \ StringExpr, \
getEngine, installHandlers getEngine, installHandlers,\
SecureModuleImporter
from ReuseUtils import rebindFunction from ReuseUtils import rebindFunction
ModuleImporter = SecureModuleImporter
class _ModuleImporter:
def __getitem__(self, module):
__import__(module)
return modules[module]
ModuleImporter = _ModuleImporter()
def trustedTraverse(ob, path, ignored,): def trustedTraverse(ob, path, ignored,):
if not path: return self if not path: return self
...@@ -110,3 +105,4 @@ getEngine = rebindFunction(getEngine, ...@@ -110,3 +105,4 @@ getEngine = rebindFunction(getEngine,
) )
...@@ -11,36 +11,22 @@ ...@@ -11,36 +11,22 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Dummy objects and views for the security tests """Provide basic browser functionality
$Id: dummy.py 12915 2005-05-31 10:23:19Z philikon $ $Id: __init__.py 18841 2005-10-23 09:57:38Z philikon $
""" """
from zope.interface import Interface, implements import Acquisition
from Products.Five import BrowserView
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
class IDummy(Interface): class BrowserView(Acquisition.Explicit):
"""Just a marker interface""" security = ClassSecurityInfo()
class DummyView(BrowserView):
"""A dummy view"""
def foo(self):
"""A foo"""
return 'A foo view'
class Dummy1: def __init__(self, context, request):
implements(IDummy) self.context = context
def foo(self): pass self.request = request
def bar(self): pass
def baz(self): pass # XXX do not create any methods on the subclass called index_html,
def keg(self): pass # as this makes Zope 2 traverse into that first!
def wot(self): pass
class Dummy2(Dummy1): InitializeClass(BrowserView)
security = ClassSecurityInfo()
security.declarePublic('foo')
security.declareProtected('View management screens', 'bar')
security.declarePrivate('baz')
security.declareProtected('View management screens', 'keg')
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Absolute URL
$Id: absoluteurl.py 13254 2005-06-09 21:40:41Z philikon $
"""
from Acquisition import aq_inner, aq_parent
from OFS.interfaces import ITraversable
from zope.interface import implements
from zope.app import zapi
from zope.app.traversing.browser.interfaces import IAbsoluteURL
from Products.Five.browser import BrowserView
class AbsoluteURL(BrowserView):
"""An adapter for Zope3-style absolute_url using Zope2 methods
(original: zope.app.traversing.browser.absoluteurl)
"""
implements(IAbsoluteURL)
def __init__(self, context, request):
self.context, self.request = context, request
def __str__(self):
context = aq_inner(self.context)
return context.absolute_url()
__call__ = __str__
def breadcrumbs(self):
context = aq_inner(self.context)
container = aq_parent(context)
request = self.request
name = context.getId()
if container is None or self._isVirtualHostRoot() \
or not ITraversable.providedBy(container):
return (
{'name': name, 'url': context.absolute_url()},)
view = zapi.getViewProviding(container, IAbsoluteURL, request)
base = tuple(view.breadcrumbs())
base += (
{'name': name, 'url': ("%s/%s" % (base[-1]['url'], name))},)
return base
def _isVirtualHostRoot(self):
virtualrootpath = self.request.get('VirtualRootPhysicalPath', None)
if virtualrootpath is None:
return False
context = aq_inner(self.context)
return context.restrictedTraverse(virtualrootpath) == context
class SiteAbsoluteURL(AbsoluteURL):
"""An adapter for Zope3-style absolute_url using Zope2 methods
This one is just used to stop breadcrumbs from crumbing up
to the Zope root.
(original: zope.app.traversing.browser.absoluteurl)
"""
def breadcrumbs(self):
context = self.context
request = self.request
return ({'name': context.getId(),
'url': context.absolute_url()
},)
<html metal:use-macro="here/five_template/macros/master"> <html metal:use-macro="context/@@standard_macros/page">
<body> <body>
<div metal:fill-slot="main"> <div metal:fill-slot="main">
<p>+ screen not yet supported by Five</p> <p>+ screen not yet supported by Five</p>
......
##############################################################################
#
# Copyright (c) 2002-2005 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.
"""
__docformat__ = 'restructuredtext'
from warnings import warn
from zope.interface import implements
from zope.publisher.interfaces import IPublishTraverse
from zope.component.interfaces import IFactory
from zope.app.exception.interfaces import UserError
from zope.app.container.interfaces import IAdding, INameChooser
from zope.app.container.interfaces import IContainerNamesContainer
from zope.app.container.constraints import checkFactory, checkObject
from zope.app import zapi
from zope.app.event.objectevent import ObjectCreatedEvent
from zope.event import notify
from zExceptions import BadRequest
from Products.Five import BrowserView
from Products.Five.traversable import Traversable
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
from Acquisition import Implicit
from OFS.SimpleItem import SimpleItem
class BasicAdding(Implicit, 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 it's 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)
else:
# Invoke the name chooser even when we have a
# name. It'll do useful things with it like converting
# the incoming unicode to an ASCII string.
name = chooser.chooseName(name, container)
content.id = name
container._setObject(name, content)
self.contentName = name # Set the added object Name
return container._getOb(name)
contentName = None # usually set by Adding traverser
def nextURL(self):
"""See zope.app.container.interfaces.IAdding"""
# XXX this is definitely not right for all or even most uses
# of Five, but can be overridden by an AddView subclass, using
# the class attribute of a zcml:addform directive
return (str(zapi.getView(self.context, "absolute_url", self.request))
+ '/manage_main')
# set in BrowserView.__init__
request = None
context = None
def renderAddButton(self):
warn("The renderAddButton method is deprecated, use nameAllowed",
DeprecationWarning, 2)
def publishTraverse(self, request, name):
"""See zope.app.container.interfaces.IAdding"""
if '=' in name:
view_name, content_name = name.split("=", 1)
self.contentName = content_name
if view_name.startswith('@@'):
view_name = view_name[2:]
return zapi.getView(self, view_name, request)
if name.startswith('@@'):
view_name = name[2:]
else:
view_name = name
view = zapi.queryView(self, view_name, request)
if view is not None:
return view
factory = zapi.queryUtility(IFactory, name)
if factory is None:
return super(BasicAdding, self).publishTraverse(request, name)
return factory
def action(self, type_name='', id=''):
if not type_name:
raise UserError("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 zapi.queryView(self, view_name, self.request) is not None:
url = "%s/%s=%s" % (
zapi.getView(self, "absolute_url", self.request),
type_name, id)
self.request.response.redirect(url)
return
if not self.contentName:
self.contentName = id
factory = zapi.getUtility(IFactory, type_name)
content = factory()
notify(ObjectCreatedEvent(content))
self.add(content)
self.request.response.redirect(self.nextURL())
def namesAccepted(self):
return not IContainerNamesContainer.providedBy(self.context)
def nameAllowed(self):
"""Return whether names can be input by the user."""
return not IContainerNamesContainer.providedBy(self.context)
class Adding(BasicAdding):
menu_id = None
index = ZopeTwoPageTemplateFile("adding.pt")
def addingInfo(self):
"""Return menu data.
This is sorted by title.
"""
container = self.context
menu_service = zapi.getService("BrowserMenu")
result = []
for menu_id in (self.menu_id, 'zope.app.container.add'):
if not menu_id:
continue
for item in menu_service.getMenu(menu_id, self, self.request):
extra = item.get('extra')
if extra:
factory = extra.get('factory')
if factory:
factory = zapi.getUtility(IFactory, factory)
if not checkFactory(container, None, factory):
continue
elif item['extra']['factory'] != item['action']:
item['has_custom_add_view']=True
result.append(item)
result.sort(lambda a, b: cmp(a['title'], b['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
class ContentAdding(Adding, Traversable, SimpleItem):
menu_id = "add_content"
class ObjectManagerNameChooser:
"""A name chooser for a Zope object manager.
"""
implements(INameChooser)
def __init__(self, context):
self.context = context
def checkName(self, name, object):
# ObjectManager can only deal with ASCII names. Specially
# ObjectManager._checkId can only deal with strings.
try:
name = name.encode('ascii')
except UnicodeDecodeError:
raise UserError, "Id must contain only ASCII characters."
try:
self.context._checkId(name, allow_dup=False)
except BadRequest, e:
msg = ' '.join(e.args) or "Id is in use or invalid"
raise UserError, msg
def chooseName(self, name, object):
if not name:
name = object.__class__.__name__
else:
try:
name = name.encode('ascii')
except UnicodeDecodeError:
raise UserError, "Id must contain only ASCII characters."
dot = name.rfind('.')
if dot >= 0:
suffix = name[dot:]
name = name[:dot]
else:
suffix = ''
n = name + suffix
i = 0
while True:
i += 1
try:
self.context._getOb(n)
except AttributeError:
break
n = name + '-' + str(i) + suffix
# Make sure the name is valid. We may have started with
# something bad.
self.checkName(n, object)
return n
...@@ -12,40 +12,19 @@ ...@@ -12,40 +12,19 @@
component="zope.app.publisher.browser.globalbrowsermenuservice.globalBrowserMenuService" component="zope.app.publisher.browser.globalbrowsermenuservice.globalBrowserMenuService"
/> />
<browser:defaultView name="index.html" />
<browser:page <browser:page
for="*" for="*"
name="absolute_url" name="absolute_url"
class=".browser.AbsoluteURL" class=".absoluteurl.AbsoluteURL"
permission="zope.Public" permission="zope.Public"
allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL" allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL"
/> />
<browser:page
for="*"
template="five_template.pt"
name="five_template"
permission="zope.Public"
/>
<browser:page
for="*"
name="standard_macros"
permission="zope2.View"
class=".skin.StandardMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
<browser:page
for="*"
name="form_macros"
permission="zope2.View"
class=".skin.FormMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
<view <view
for="*" for="*"
factory=".browser.AbsoluteURL" factory=".absoluteurl.AbsoluteURL"
type="zope.publisher.interfaces.http.IHTTPRequest" type="zope.publisher.interfaces.http.IHTTPRequest"
permission="zope.Public" permission="zope.Public"
provides="zope.app.traversing.browser.interfaces.IAbsoluteURL" provides="zope.app.traversing.browser.interfaces.IAbsoluteURL"
...@@ -54,21 +33,21 @@ ...@@ -54,21 +33,21 @@
<browser:page <browser:page
for="zope.app.traversing.interfaces.IContainmentRoot" for="zope.app.traversing.interfaces.IContainmentRoot"
name="absolute_url" name="absolute_url"
class=".browser.SiteAbsoluteURL" class=".absoluteurl.SiteAbsoluteURL"
permission="zope.Public" permission="zope.Public"
allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL" allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL"
/> />
<view <view
for="zope.app.traversing.interfaces.IContainmentRoot" for="zope.app.traversing.interfaces.IContainmentRoot"
factory=".browser.SiteAbsoluteURL" factory=".absoluteurl.SiteAbsoluteURL"
type="zope.publisher.interfaces.http.IHTTPRequest" type="zope.publisher.interfaces.http.IHTTPRequest"
permission="zope.Public" permission="zope.Public"
provides="zope.app.traversing.browser.interfaces.IAbsoluteURL" provides="zope.app.traversing.browser.interfaces.IAbsoluteURL"
/> />
<browser:view <browser:view
for=".interfaces.IObjectManager" for="OFS.interfaces.IObjectManager"
name="+" name="+"
class=".adding.ContentAdding" class=".adding.ContentAdding"
permission="zope2.ViewManagementScreens" permission="zope2.ViewManagementScreens"
...@@ -79,4 +58,19 @@ ...@@ -79,4 +58,19 @@
</browser:view> </browser:view>
<adapter
for="OFS.interfaces.IObjectManager"
factory=".adding.ObjectManagerNameChooser"
provides="zope.app.container.interfaces.INameChooser"
/>
<!-- Menu access -->
<browser:page
for="*"
name="view_get_menu"
permission="zope.Public"
class=".menu.MenuAccessView"
allowed_interface="zope.app.publisher.interfaces.browser.IMenuAccessView"
/>
</configure> </configure>
##############################################################################
#
# Copyright (c) 2005 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.
#
##############################################################################
"""Some menu code
$Id: menu.py 14512 2005-07-11 18:40:51Z philikon $
"""
from zope.interface import implements
from zope.app import zapi
from zope.app.publisher.interfaces.browser import IMenuAccessView
from zope.app.servicenames import BrowserMenu
from Products.Five import BrowserView
class MenuAccessView(BrowserView):
implements(IMenuAccessView)
def __getitem__(self, menu_id):
browser_menu_service = zapi.getService(BrowserMenu)
return browser_menu_service.getMenu(menu_id, self.context, self.request)
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<meta:directives namespace="http://namespaces.zope.org/browser">
<meta:directive
name="layer"
schema="zope.app.publisher.browser.metadirectives.ILayerDirective"
handler="zope.app.publisher.browser.metaconfigure.layer"
/>
<meta:directive
name="skin"
schema="zope.app.publisher.browser.metadirectives.ISkinDirective"
handler="zope.app.publisher.browser.metaconfigure.skin"
/>
<meta:directive
name="defaultSkin"
schema="zope.app.publisher.browser.metadirectives.IDefaultSkinDirective"
handler="zope.app.publisher.browser.metaconfigure.defaultSkin"
/>
<meta:directive
name="defaultView"
schema="zope.app.publisher.browser.metadirectives.IDefaultViewDirective"
handler=".metaconfigure.defaultView"
/>
<meta:directive
name="page"
schema="zope.app.publisher.browser.metadirectives.IPageDirective"
handler=".metaconfigure.page"
/>
<meta:complexDirective
name="pages"
schema="zope.app.publisher.browser.metadirectives.IPagesDirective"
handler=".metaconfigure.pages"
>
<meta:subdirective
name="page"
schema="zope.app.publisher.browser.metadirectives.IPagesPageSubdirective"
/>
</meta:complexDirective>
<meta:directive
name="resource"
schema="zope.app.publisher.browser.metadirectives.IResourceDirective"
handler=".metaconfigure.resource"
/>
<meta:directive
name="resourceDirectory"
schema="zope.app.publisher.browser.metadirectives.IResourceDirectoryDirective"
handler=".metaconfigure.resourceDirectory"
/>
<meta:directive
name="menu"
schema="zope.app.publisher.browser.metadirectives.IMenuDirective"
handler="zope.app.publisher.browser.globalbrowsermenuservice.menuDirective"
/>
<meta:directive
name="menuItem"
schema="zope.app.publisher.browser.metadirectives.IMenuItemDirective"
handler="zope.app.publisher.browser.globalbrowsermenuservice.menuItemDirective"
/>
<meta:complexDirective
name="menuItems"
schema="zope.app.publisher.browser.metadirectives.IMenuItemsDirective"
handler="zope.app.publisher.browser.globalbrowsermenuservice.menuItemsDirective"
>
<meta:subdirective
name="menuItem"
schema="zope.app.publisher.browser.metadirectives.IMenuItemSubdirective"
/>
</meta:complexDirective>
<meta:complexDirective
name="view"
schema="zope.app.publisher.browser.metadirectives.IViewDirective"
handler=".metaconfigure.view"
>
<meta:subdirective
name="page"
schema="zope.app.publisher.browser.metadirectives.IViewPageSubdirective"
/>
<meta:subdirective
name="defaultPage"
schema="zope.app.publisher.browser.metadirectives.IViewDefaultPageSubdirective"
/>
</meta:complexDirective>
</meta:directives>
</configure>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
Directives to emulate the 'http://namespaces.zope.org/browser' Directives to emulate the 'http://namespaces.zope.org/browser'
namespace in ZCML known from zope.app. namespace in ZCML known from zope.app.
$Id: browserconfigure.py 12915 2005-05-31 10:23:19Z philikon $ $Id: metaconfigure.py 13257 2005-06-09 21:56:39Z philikon $
""" """
import os import os
...@@ -27,21 +27,21 @@ from zope.component.servicenames import Presentation ...@@ -27,21 +27,21 @@ from zope.component.servicenames import Presentation
from zope.publisher.interfaces.browser import IBrowserRequest from zope.publisher.interfaces.browser import IBrowserRequest
from zope.app.publisher.browser.viewmeta import pages as zope_app_pages from zope.app.publisher.browser.viewmeta import pages as zope_app_pages
from zope.app.publisher.browser.viewmeta import view as zope_app_view from zope.app.publisher.browser.viewmeta import view as zope_app_view
from zope.app.publisher.browser.viewmeta import providesCallable
from zope.app.publisher.browser.globalbrowsermenuservice import\ from zope.app.publisher.browser.globalbrowsermenuservice import\
menuItemDirective menuItemDirective
from zope.app.component.metaconfigure import handler from zope.app.component.metaconfigure import handler
from zope.app.component.interface import provideInterface from zope.app.component.interface import provideInterface
from zope.app.form.browser.metaconfigure import BaseFormDirective
from zope.app.container.interfaces import IAdding from zope.app.container.interfaces import IAdding
from resource import FileResourceFactory, ImageResourceFactory from Products.Five.browser import BrowserView
from resource import PageTemplateResourceFactory from Products.Five.browser.resource import FileResourceFactory, ImageResourceFactory
from resource import DirectoryResourceFactory from Products.Five.browser.resource import PageTemplateResourceFactory
from browser import BrowserView, EditView, AddView from Products.Five.browser.resource import DirectoryResourceFactory
from metaclass import makeClass from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
from security import getSecurityInfo, protectClass, protectName,\ from Products.Five.metaclass import makeClass
initializeClass from Products.Five.security import getSecurityInfo, protectClass, \
from pagetemplatefile import ZopeTwoPageTemplateFile protectName, initializeClass
import ExtensionClass import ExtensionClass
...@@ -425,178 +425,6 @@ def resourceDirectory(_context, name, directory, layer='default', ...@@ -425,178 +425,6 @@ def resourceDirectory(_context, name, directory, layer='default',
args = (new_class,) args = (new_class,)
) )
#
# Form generation from schema
#
def EditViewFactory(name, schema, label, permission, layer,
template, default_template, bases, for_, fields,
fulledit_path=None, fulledit_label=None, menu=u''):
s = getGlobalService(Presentation)
class_ = makeClassForTemplate(template, used_for=schema, bases=bases)
class_.schema = schema
class_.label = label
class_.fieldNames = fields
class_.fulledit_path = fulledit_path
if fulledit_path and (fulledit_label is None):
fulledit_label = "Full edit"
class_.fulledit_label = fulledit_label
class_.generated_form = ZopeTwoPageTemplateFile(default_template)
s.provideView(for_, name, IBrowserRequest, class_, layer)
protectClass(class_, permission)
initializeClass(class_)
class FiveFormDirective(BaseFormDirective):
def _processWidgets(self):
if self._widgets:
customWidgetsObject = makeClass('CustomWidgetsMixin', (ExtensionClass.Base,), self._widgets)
self.bases = self.bases + (customWidgetsObject,)
class EditFormDirective(FiveFormDirective):
view = EditView
default_template = 'edit.pt'
title = 'Edit'
def _handle_menu(self):
if self.menu:
menuItemDirective(
self._context, self.menu, self.for_ or self.schema,
'@@' + self.name, self.title, permission=self.permission)
def __call__(self):
self._processWidgets()
self._handle_menu()
self._context.action(
discriminator=self._discriminator(),
callable=EditViewFactory,
args=self._args(),
kw={'menu': self.menu},
)
def AddViewFactory(name, schema, label, permission, layer,
template, default_template, bases, for_,
fields, content_factory, arguments,
keyword_arguments, set_before_add, set_after_add,
menu=u''):
s = getGlobalService(Presentation)
class_ = makeClassForTemplate(template, used_for=schema, bases=bases)
class_.schema = schema
class_.label = label
class_.fieldNames = fields
class_._factory = content_factory
class_._arguments = arguments
class_._keyword_arguments = keyword_arguments
class_._set_before_add = set_before_add
class_._set_after_add = set_after_add
class_.generated_form = ZopeTwoPageTemplateFile(default_template)
s.provideView(for_, name, IBrowserRequest, class_, layer)
protectClass(class_, permission)
initializeClass(class_)
class AddFormDirective(FiveFormDirective):
view = AddView
default_template = 'add.pt'
for_ = IAdding
# default add form information
description = None
content_factory = None
arguments = None
keyword_arguments = None
set_before_add = None
set_after_add = None
def _handle_menu(self):
if self.menu or self.title:
if (not self.menu) or (not self.title):
raise ValueError("If either menu or title are specified, "
"they must both be specified")
# Add forms are really for IAdding components, so do not use
# for=self.schema.
menuItemDirective(
self._context, self.menu, self.for_, '@@' + self.name,
self.title, permission=self.permission,
description=self.description)
def _handle_arguments(self, leftover=None):
schema = self.schema
fields = self.fields
arguments = self.arguments
keyword_arguments = self.keyword_arguments
set_before_add = self.set_before_add
set_after_add = self.set_after_add
if leftover is None:
leftover = fields
if arguments:
missing = [n for n in arguments if n not in fields]
if missing:
raise ValueError("Some arguments are not included in the form",
missing)
optional = [n for n in arguments if not schema[n].required]
if optional:
raise ValueError("Some arguments are optional, use"
" keyword_arguments for them",
optional)
leftover = [n for n in leftover if n not in arguments]
if keyword_arguments:
missing = [n for n in keyword_arguments if n not in fields]
if missing:
raise ValueError(
"Some keyword_arguments are not included in the form",
missing)
leftover = [n for n in leftover if n not in keyword_arguments]
if set_before_add:
missing = [n for n in set_before_add if n not in fields]
if missing:
raise ValueError(
"Some set_before_add are not included in the form",
missing)
leftover = [n for n in leftover if n not in set_before_add]
if set_after_add:
missing = [n for n in set_after_add if n not in fields]
if missing:
raise ValueError(
"Some set_after_add are not included in the form",
missing)
leftover = [n for n in leftover if n not in set_after_add]
self.set_after_add += leftover
else:
self.set_after_add = leftover
def __call__(self):
self._processWidgets()
self._handle_menu()
self._handle_arguments()
self._context.action(
discriminator=self._discriminator(),
callable=AddViewFactory,
args=self._args()+(self.content_factory, self.arguments,
self.keyword_arguments,
self.set_before_add, self.set_after_add),
kw={'menu': self.menu},
)
# #
# mixin classes / class factories # mixin classes / class factories
# #
...@@ -627,14 +455,14 @@ class ViewMixinForTemplates(BrowserView): ...@@ -627,14 +455,14 @@ class ViewMixinForTemplates(BrowserView):
def __call__(self, *args, **kw): def __call__(self, *args, **kw):
return self.index(self, *args, **kw) return self.index(self, *args, **kw)
def makeClassForTemplate(src, template=None, used_for=None, def makeClassForTemplate(filename, globals=None, used_for=None,
bases=(), cdict=None): bases=(), cdict=None):
# XXX needs to deal with security from the bases? # XXX needs to deal with security from the bases?
if cdict is None: if cdict is None:
cdict = {} cdict = {}
cdict.update({'index': ZopeTwoPageTemplateFile(src, template)}) cdict.update({'index': ZopeTwoPageTemplateFile(filename, globals)})
bases += (ViewMixinForTemplates,) bases += (ViewMixinForTemplates,)
class_ = makeClass("SimpleViewClass from %s" % src, bases, cdict) class_ = makeClass("SimpleViewClass from %s" % filename, bases, cdict)
if used_for is not None: if used_for is not None:
class_.__used_for__ = used_for class_.__used_for__ = used_for
......
...@@ -13,21 +13,20 @@ ...@@ -13,21 +13,20 @@
############################################################################## ##############################################################################
"""A 'PageTemplateFile' without security restrictions. """A 'PageTemplateFile' without security restrictions.
$Id: pagetemplatefile.py 12915 2005-05-31 10:23:19Z philikon $ $Id: pagetemplatefile.py 15193 2005-07-27 13:27:04Z regebro $
""" """
import os, sys import os, sys
# Zope 2
from Globals import package_home from Globals import package_home
from AccessControl import getSecurityManager
from Shared.DC.Scripts.Bindings import Unauthorized, UnauthorizedBinding
from Products.PageTemplates.PageTemplateFile import PageTemplateFile from Products.PageTemplates.PageTemplateFile import PageTemplateFile
# Zope 3
from zope.app.pagetemplate.viewpagetemplatefile import ViewMapper from zope.app.pagetemplate.viewpagetemplatefile import ViewMapper
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
# Five from Products.Five.browser.ReuseUtils import rebindFunction
from ReuseUtils import rebindFunction from Products.Five.browser.TrustedExpression import getEngine, ModuleImporter
from TrustedExpression import getEngine, ModuleImporter
class ZopeTwoPageTemplateFile(PageTemplateFile): class ZopeTwoPageTemplateFile(PageTemplateFile):
"""A strange hybrid between Zope 2 and Zope 3 page template. """A strange hybrid between Zope 2 and Zope 3 page template.
...@@ -65,24 +64,23 @@ class ZopeTwoPageTemplateFile(PageTemplateFile): ...@@ -65,24 +64,23 @@ class ZopeTwoPageTemplateFile(PageTemplateFile):
getEngine=getEngine) getEngine=getEngine)
def _pt_getContext(self): def _pt_getContext(self):
view = self._getContext()
try: try:
root = self.getPhysicalRoot() root = self.getPhysicalRoot()
here = view.context view = self._getContext()
except AttributeError: except AttributeError:
# self has no attribute getPhysicalRoot. # self has no attribute getPhysicalRoot. This typically happens
# This typically happens when the template has # when the template has no proper acquisition context.
# no proper acquisition context. That means it has no view, # That also means it has no view. /regebro
# since that's the normal context for a template in Five. /regebro
root = self.context.getPhysicalRoot() root = self.context.getPhysicalRoot()
here = self.context
view = None view = None
here = self.context.aq_inner
request = getattr(root, 'REQUEST', None) request = getattr(root, 'REQUEST', None)
c = {'template': self, c = {'template': self,
'here': here, 'here': here,
'context': here, 'context': here,
'container': self._getContainer(), 'container': here,
'nothing': None, 'nothing': None,
'options': {}, 'options': {},
'root': root, 'root': root,
...@@ -97,14 +95,3 @@ class ZopeTwoPageTemplateFile(PageTemplateFile): ...@@ -97,14 +95,3 @@ class ZopeTwoPageTemplateFile(PageTemplateFile):
pt_getContext = rebindFunction(_pt_getContext, pt_getContext = rebindFunction(_pt_getContext,
SecureModuleImporter=ModuleImporter) SecureModuleImporter=ModuleImporter)
# this is not in use right now, but would be how to integrate Zope 3 page
# templates instead of Zope 2 page templates
class FivePageTemplateFile(ViewPageTemplateFile):
def pt_getContext(self, instance, request, **_kw):
# instance is a View component
namespace = super(FivePageTemplateFile, self).pt_getContext(
instance, request, **_kw)
namespace['here'] = namespace['context']
return namespace
...@@ -13,15 +13,15 @@ ...@@ -13,15 +13,15 @@
############################################################################## ##############################################################################
"""Provide basic resource functionality """Provide basic resource functionality
$Id: browser.py 5259 2004-06-23 15:59:52Z philikon $ $Id: resource.py 13268 2005-06-10 14:18:23Z philikon $
""" """
import os import os
import urllib import urllib
from Acquisition import Explicit, aq_inner, aq_parent from Acquisition import Explicit
from ComputedAttribute import ComputedAttribute from ComputedAttribute import ComputedAttribute
from browser import BrowserView
from OFS.Traversable import Traversable as OFSTraversable from OFS.Traversable import Traversable as OFSTraversable
from zope.exceptions import NotFoundError from zope.exceptions import NotFoundError
from zope.interface import implements from zope.interface import implements
from zope.component.interfaces import IResource from zope.component.interfaces import IResource
...@@ -33,6 +33,8 @@ from zope.app.publisher.fileresource import File, Image ...@@ -33,6 +33,8 @@ from zope.app.publisher.fileresource import File, Image
from zope.app.publisher.pagetemplateresource import PageTemplate from zope.app.publisher.pagetemplateresource import PageTemplate
from zope.app.publisher.browser.resources import empty from zope.app.publisher.browser.resources import empty
from Products.Five.browser import BrowserView
_marker = [] _marker = []
class Resource(Explicit): class Resource(Explicit):
...@@ -57,7 +59,7 @@ class PageTemplateResource(BrowserView, Resource): ...@@ -57,7 +59,7 @@ class PageTemplateResource(BrowserView, Resource):
#implements(IBrowserPublisher) #implements(IBrowserPublisher)
def __browser_default__(self, request): def __browser_default__(self, request):
return self, ('render', ) return self, ('render',)
def render(self): def render(self):
"""Rendered content""" """Rendered content"""
......
...@@ -7,12 +7,11 @@ ObjectManagerNameChooser ...@@ -7,12 +7,11 @@ ObjectManagerNameChooser
First we need to import and setup some prerequisites: First we need to import and setup some prerequisites:
>>> from zope.app.container.interfaces import INameChooser >>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> from Products.Five.tests.products.FiveTest.helpers import \ >>> from Products.Five.browser.adding import ObjectManagerNameChooser
... manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid') >>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
>>> chooser = INameChooser(self.folder) >>> chooser = ObjectManagerNameChooser(self.folder)
Now we can start. ``INameChooser`` defines a ``checkName()`` method Now we can start. ``INameChooser`` defines a ``checkName()`` method
that checks whether a given name is valid in the container or not. that checks whether a given name is valid in the container or not.
......
<p tal:content="context/mymethod">Alpha</p> <p tal:content="context/mymethod">Alpha</p>
<p tal:content="view/eagle">Beta</p> <p tal:content="view/eagle">Beta</p>
<div tal:replace="structure views/flamingo.html">Gamma</div> <div tal:replace="structure context/@@flamingo.html">Gamma</div>
\ No newline at end of file \ No newline at end of file
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
<p i18n:domain="fivetest" i18n:translate="">This is a message</p>
<p i18n:domain="default" i18n:translate="">Object actions</p>
</body>
</html>
##############################################################################
#
# Copyright (c) 2005 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 the Localizer language integration for CPS. This test
requires a full blown CPS installation to run. It is therefore
prefixed with ``cps_`` so it won't be picked up by the test runner.
$Id: cps_test_localizer.py 14400 2005-07-07 17:55:08Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_suite():
from Testing.ZopeTestCase import installProduct, FunctionalDocFileSuite
installProduct('Five')
installProduct('BTreeFolder2')
installProduct('CMFCalendar')
installProduct('CMFCore')
installProduct('CMFDefault')
installProduct('CMFTopic')
installProduct('DCWorkflow')
installProduct('Localizer')
installProduct('MailHost')
installProduct('CPSCore')
installProduct('CPSDefault')
installProduct('CPSDirectory')
installProduct('CPSUserFolder')
installProduct('TranslationService')
installProduct('SiteAccess')
# these products should (and used to be) be optional, but they
# aren't right now.
installProduct('CPSForum')
installProduct('CPSSubscriptions')
installProduct('CPSNewsLetters')
installProduct('CPSSchemas')
installProduct('CPSDocument')
installProduct('PortalTransforms')
installProduct('Epoz')
# optional products, but apparently still needed...
installProduct('CPSRSS')
installProduct('CPSChat')
installProduct('CPSCalendar')
installProduct('CPSCollector')
installProduct('CPSMailBoxer')
return FunctionalDocFileSuite('cps_test_localizer.txt',
package='Products.Five.browser.tests')
if __name__ == '__main__':
framework()
Localizer languages
===================
Before we start, we need to set up a manager user to be able to create
the portal:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
We need to 1) configure the Zope 3 i18n message catalogs, 2) make the
CPS portal traversable, 3) register the Localizer languagees adapter
and 4) register our test page:
>>> configure_zcml = """
... <configure
... xmlns="http://namespaces.zope.org/zope"
... xmlns:browser="http://namespaces.zope.org/browser"
... xmlns:five="http://namespaces.zope.org/five"
... xmlns:i18n="http://namespaces.zope.org/i18n"
... >
... <configure package="Products.Five.tests">
... <i18n:registerTranslations directory="locales" />
... </configure>
...
... <five:traversable class="Products.CPSDefault.Portal.CPSDefaultSite" />
...
... <adapter
... for="zope.publisher.interfaces.http.IHTTPRequest"
... provides="zope.i18n.interfaces.IUserPreferredLanguages"
... factory="Products.Five.i18n.LocalizerLanguages"
... />
...
... <configure package="Products.Five.browser.tests">
... <browser:page
... for="*"
... template="cps_test_localizer.pt"
... name="cps_test_localizer.html"
... permission="zope2.View"
... />
... </configure>
... </configure>
... """
>>> from Products.Five import zcml
>>> zcml.load_string(configure_zcml)
Create a CPS portal. We print an additional line before creating it
because PortalTransforms might print stuff to stdout and doctest
doesn't allow us to ellide a first line.
>>> print "Ignore lines after me"; print http(r"""
... POST /test_folder_1_/manage_addProduct/CPSDefault/manage_addCPSDefaultSite HTTP/1.1
... Authorization: Basic manager:r00t
... Content-Length: 269
... Content-Type: application/x-www-form-urlencoded
...
... id=cps&title=CPS+Portal&description=&manager_id=manager&manager_sn=CPS+manager&manager_givenName=Manager&manager_email=root%40localhost&manager_password=root&manager_password_confirmation=root&langs_list%3Alist=en&langs_list%3Alist=fr&langs_list%3Alist=de&submit=Create""")
Ignore lines after me
...
HTTP/1.1 200 OK
...
Now for some actual testing... Our test page is a simple ZPT
translating two messages from different domains. The first domain is
a Zope 3 style one, the second one comes from Localizer.
Both systems should yield the same default language (English) when no
language is specified whatsoever:
>>> print http(r"""
... GET /test_folder_1_/cps/cps_test_localizer.html HTTP/1.1
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
<p>This is a message</p>
<p>Object actions</p>
</body>
</html>
Both systems should honour the HTTP ``Accept-Language`` header in the
same way:
>>> print http(r"""
... GET /test_folder_1_/cps/cps_test_localizer.html HTTP/1.1
... Accept-Language: de
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
<p>Dies ist eine Nachricht</p>
<p>Objekt Aktionen</p>
</body>
</html>
Both systems should also honour Localizer-specific ways of determining
the language, for example the ``LOCALIZER_LANGUAGE`` cookie:
>>> print http(r"""
... GET /test_folder_1_/cps/cps_test_localizer.html HTTP/1.1
... Accept-Language: de
... Cookie: LOCALIZER_LANGUAGE=en
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a Localizer domain -->
<p>This is a message</p>
<p>Object actions</p>
</body>
</html>
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five">
<five:defaultViewable
class="Products.Five.testing.simplecontent.SimpleContent" />
<browser:defaultView
for="Products.Five.testing.simplecontent.ISimpleContent"
name="eagledefaultview.txt"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
name="eagledefaultview.txt"
class=".pages.SimpleView"
attribute="eagle"
permission="zope2.Public"
/>
<!-- this tests whether five:defaultViewable can be called on a class that
already provides __call__, such as our CallableSimpleContent -->
<five:defaultViewable
class="Products.Five.testing.simplecontent.CallableSimpleContent" />
<!-- this tests whether five:defaultViewable can be called on a class that
already provides index_html, such as our IndexSimpleContent -->
<five:defaultViewable
class="Products.Five.testing.simplecontent.IndexSimpleContent" />
<browser:defaultView
for="Products.Five.testing.simplecontent.IIndexSimpleContent"
name="index_html"
/>
</configure>
<p tal:content="context/mymethod">Replaced</p>
<p tal:content="python:context.mymethod()">Replaced</p>
##############################################################################
#
# ZopeTestCase
#
# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
#
# This version of framework.py will use the SOFTWARE_HOME
# environment variable to locate Zope and the Testing package.
#
# If the tests are run in an INSTANCE_HOME installation of Zope,
# Products.__path__ and sys.path with be adjusted to include the
# instance's Products and lib/python directories respectively.
#
# If you explicitly set INSTANCE_HOME prior to running the tests,
# auto-detection is disabled and the specified path will be used
# instead.
#
# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
# will be adjusted to use it.
#
# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
# is assumed, and you can attach to a running ZEO server (via the
# instance's custom_zodb.py).
#
##############################################################################
#
# The following code should be at the top of every test module:
#
# import os, sys
# if __name__ == '__main__':
# execfile(os.path.join(sys.path[0], 'framework.py'))
#
# ...and the following at the bottom:
#
# if __name__ == '__main__':
# framework()
#
##############################################################################
__version__ = '0.2.3'
# Save start state
#
__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
if __SOFTWARE_HOME.endswith(os.sep):
__SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
if __INSTANCE_HOME.endswith(os.sep):
__INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
# Find and import the Testing package
#
if not sys.modules.has_key('Testing'):
p0 = sys.path[0]
if p0 and __name__ == '__main__':
os.chdir(p0)
p0 = ''
s = __SOFTWARE_HOME
p = d = s and s or os.getcwd()
while d:
if os.path.isdir(os.path.join(p, 'Testing')):
zope_home = os.path.dirname(os.path.dirname(p))
sys.path[:1] = [p0, p, zope_home]
break
p, d = s and ('','') or os.path.split(p)
else:
print 'Unable to locate Testing package.',
print 'You might need to set SOFTWARE_HOME.'
sys.exit(1)
import Testing, unittest
execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
# Include ZopeTestCase support
#
if 1: # Create a new scope
p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
if not os.path.isdir(p):
print 'Unable to locate ZopeTestCase package.',
print 'You might need to install ZopeTestCase.'
sys.exit(1)
ztc_common = 'ztc_common.py'
ztc_common_global = os.path.join(p, ztc_common)
f = 0
if os.path.exists(ztc_common_global):
execfile(ztc_common_global)
f = 1
if os.path.exists(ztc_common):
execfile(ztc_common)
f = 1
if not f:
print 'Unable to locate %s.' % ztc_common
sys.exit(1)
# Debug
#
print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
sys.stdout.flush()
<html i18n:domain="fivetest">
<body>
<p i18n:translate="">This is a message</p>
<p i18n:translate="explicit-msg">This is an explicit message</p>
<p i18n:translate="">These are <span tal:replace="python:4" i18n:name="number" /> messages</p>
<p i18n:translate="explicit-msgs">These are <span tal:replace="python:4" i18n:name="number" /> explicit messages</p>
<table summary="This is an attribute" i18n:attributes="summary">
</table>
<table summary="Explicit summary" title="Explicit title" i18n:attributes="summary explicit-summary; title explicit-title" >
</table>
</body>
</html>
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta"
xmlns:browser="http://namespaces.zope.org/browser"
i18n_domain="fivetest">
<!-- make the zope2.Public permission work -->
<meta:redefinePermission from="zope2.Public" to="zope.Public" />
<!-- browser menu support -->
<browser:menu
id="testmenu"
title="Test menu"
/>
<browser:menuItem
for="OFS.interfaces.IFolder"
menu="testmenu"
title="Test Menu Item"
action="seagull.html"
description="This is a test menu item"
permission="zope2.Public"
/>
<browser:menuItem
for="OFS.interfaces.IFolder"
menu="testmenu"
title="Protected Test Menu Item"
action="seagull.html"
description="This is a protected test menu item"
permission="zope2.ViewManagementScreens"
/>
<browser:menuItems
for="OFS.interfaces.IFolder"
menu="testmenu">
<menuItem
title="Test Menu Item 2"
action="parakeet.html"
description="This is a test menu item"
permission="zope2.Public"
/>
<menuItem
title="Test Menu Item 3"
action="falcon.html"
description="This is a test menu item"
permission="zope2.Public"
/>
<menuItem
title="Protected Test Menu Item 2"
action="falcon.html"
description="This is a protected test menu item"
permission="zope2.ViewManagementScreens"
/>
</browser:menuItems>
<!-- page in a menu -->
<browser:page
for="OFS.interfaces.IFolder"
template="cockatiel.pt"
name="cockatiel_menu_public.html"
permission="zope2.Public"
title="Page in a menu (public)"
menu="testmenu"
/>
<browser:page
for="OFS.interfaces.IFolder"
template="cockatiel.pt"
name="cockatiel_menu_protected.html"
permission="zope2.ViewManagementScreens"
title="Page in a menu (protected)"
menu="testmenu"
/>
</configure>
\ No newline at end of file
<ul> <ul>
<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="item"/>
</ul>
<ul>
<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="python:repeat['item'].index"/> <li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="python:repeat['item'].index"/>
</ul> </ul>
...@@ -3,18 +3,11 @@ ...@@ -3,18 +3,11 @@
<!-- mouse instead of eagle --> <!-- mouse instead of eagle -->
<browser:page <browser:page
for=".interfaces.ISimpleContent" for="Products.Five.testing.simplecontent.ISimpleContent"
class=".browser.SimpleContentView" class=".pages.SimpleView"
attribute="mouse" attribute="mouse"
name="overridden_view" name="overridden_view"
permission="zope2.Public" permission="zope2.Public"
/> />
<!-- OverrideAdapter instead of OriginalAdapter --> </configure>
<adapter \ No newline at end of file
for=".interfaces.IOrigin"
provides=".interfaces.IDestination"
factory=".classes.OverrideAdapter"
/>
</configure>
...@@ -11,17 +11,13 @@ ...@@ -11,17 +11,13 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Browser views for tests. """Test browser pages
$Id: browser.py 12915 2005-05-31 10:23:19Z philikon $ $Id: pages.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from Products.Five import BrowserView from Products.Five import BrowserView
from Products.Five import StandardMacros as BaseMacros
from simplecontent import FieldSimpleContent
from zope.app.form import CustomWidgetFactory
from zope.app.form.browser import ObjectWidget
class SimpleContentView(BrowserView): class SimpleView(BrowserView):
"""More docstring. Please Zope""" """More docstring. Please Zope"""
def eagle(self): def eagle(self):
...@@ -32,7 +28,7 @@ class SimpleContentView(BrowserView): ...@@ -32,7 +28,7 @@ class SimpleContentView(BrowserView):
"""Docstring""" """Docstring"""
return "The mouse has been eaten by the eagle" return "The mouse has been eaten by the eagle"
class FancyContentView(BrowserView): class FancyView(BrowserView):
"""Fancy, fancy stuff""" """Fancy, fancy stuff"""
def view(self): def view(self):
...@@ -69,14 +65,3 @@ class NewStyleClass(object): ...@@ -69,14 +65,3 @@ class NewStyleClass(object):
def method(self): def method(self):
"""Docstring""" """Docstring"""
return return
class StandardMacros(BaseMacros):
macro_pages = ('bird_macros', 'dog_macros')
aliases = {'flying':'birdmacro',
'walking':'dogmacro'}
class ComplexSchemaView:
"""Needs a docstring"""
fish_widget = CustomWidgetFactory(ObjectWidget, FieldSimpleContent)
Test browser pages
==================
Let's register a quite large amount of test pages:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
Let's add a test object that we view most of the pages off of:
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
We also need to create a stub user account and login; otherwise we
wouldn't have all the rights to do traversal etc.:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
Now for some actual testing...
Simple pages
------------
A browser page that is a view class's attribute (method):
>>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> view != None
True
>>> from Products.Five.browser.tests.pages import SimpleView
>>> isinstance(view, SimpleView)
True
>>> view()
'The eagle has landed'
A browser page that is a Page Template.
>>> view = self.folder.unrestrictedTraverse('testoid/owl.html')
>>> view()
'<p>2</p>\n'
A browser page that is a PageTemplate plus a view class:
>>> view = self.folder.unrestrictedTraverse('testoid/falcon.html')
>>> isinstance(view, SimpleView)
True
>>> view()
'<p>The falcon has taken flight</p>\n'
Test pages that have been registered through the cumulative
<browser:pages> directive:
>>> view = self.folder.unrestrictedTraverse('testoid/eagle-page.txt')
>>> isinstance(view, SimpleView)
True
>>> view()
'The eagle has landed'
>>> view = self.folder.unrestrictedTraverse('testoid/mouse-page.txt')
>>> isinstance(view, SimpleView)
True
>>> view()
'The mouse has been eaten by the eagle'
Zope 2 objects always need a docstring in order to be published. Five
adds a docstring automatically if a view method doesn't have it, but
it shouldn't modify existing ones:
>>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> view.eagle.__doc__ == SimpleView.eagle.__doc__
True
Test whether new-style classes are ignored when registering browser
pages with view classes. When traversing for a non-existing view, we
should get an AttributeError:
>>> self.folder.unrestrictedTraverse('testoid/@@new_style_class')
Traceback (most recent call last):
...
AttributeError: @@new_style_class
ZPT-based browser pages
-----------------------
Test access to ``context`` from ZPTs:
>>> view = self.folder.unrestrictedTraverse('testoid/flamingo.html')
>>> print view()
<p>Hello world</p>
<p>Hello world</p>
Test macro access from ZPT pages:
>>> view = self.folder.unrestrictedTraverse('testoid/seagull.html')
>>> view()
'<html><head><title>bird macro</title></head><body>Color: gray</body></html>\n'
Test whether old-style direct traversal still works with a
five:traversable class:
>>> old_view = self.folder.unrestrictedTraverse('testoid/direct')
>>> old_view()
'Direct traversal worked'
test_zpt_things:
>>> view = self.folder.unrestrictedTraverse('testoid/condor.html')
>>> print view()
<p>Hello world</p>
<p>The eagle has landed</p>
<p>Hello world</p>
<p>Hello world</p>
Make sure that tal:repeat works in ZPT browser pages:
>>> view = self.folder.unrestrictedTraverse('testoid/ostrich.html')
>>> print view()
<ul>
<li>Alpha</li>
<li>Beta</li>
<li>Gamma</li>
</ul>
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
</ul>
Test TALES traversal in ZPT pages:
>>> view = self.folder.unrestrictedTraverse('testoid/tales_traversal.html')
>>> print view()
<p>testoid</p>
<p>test_folder_1_</p>
Make sure that global template variables in ZPT pages are correct:
>>> view = self.folder.unrestrictedTraverse('testoid/template_variables.html')
>>> print view()
View is a view: True
Context is testoid: True
Contaxt.aq_parent is test_folder_1_: True
Container is context: True
Here is context: True
Nothing is None: True
Default works: True
Root is the application: True
Template is a template: True
Traverse_subpath exists and is empty: True
Request is a request: True
User is manager: True
Options exist: True
Attrs exist: True
Repeat exists: True
Loop exists: True
Modules exists: True
Make sure that ZPT's aren't a security-less zone. Let's logout and
try to access some protected stuff. Let's not forgot to login again,
of course:
>>> from AccessControl import allow_module
>>> allow_module('smtpd')
>>> self.logout()
>>> view = self.folder.unrestrictedTraverse('testoid/security.html')
>>> print view()
<div>NoneType</div>
<div>smtpd</div>
>>> self.login('manager')
Test pages registered through the <five:pagesFromDirectory /> directive:
>>> view = self.folder.unrestrictedTraverse('testoid/dirpage1')
>>> print view()
<html>
<p>This is page 1</p>
</html>
>>> view = self.folder.unrestrictedTraverse('testoid/dirpage2')
>>> print view()
<html>
<p>This is page 2</p>
</html>
Low-level security
------------------
This tests security on a low level (functional pages test has
high-level security tests). Let's manually look up a protected view:
>>> from Products.Five.traversable import FakeRequest
>>> from zope.app import zapi
>>> request = FakeRequest()
>>> view = zapi.getView(self.folder.testoid, 'eagle.txt', request)
It's protecting the object with the permission, and not the attribute,
so we get ('',) instead of ('eagle',):
>>> getattr(view, '__ac_permissions__')
(('View management screens', ('',)),)
Wrap into an acquisition so that imPermissionRole objects can be
evaluated. __roles__ is a imPermissionRole object:
>>> view = view.__of__(self.folder.testoid)
>>> view_roles = getattr(view, '__roles__', None)
>>> view_roles
('Manager',)
Check to see if view's context properly acquires it's true
parent
>>> from Acquisition import aq_parent, aq_base, aq_inner
>>> context = getattr(view, 'context')
Check the wrapper type
>>> from Acquisition import ImplicitAcquisitionWrapper
>>> type(context) == ImplicitAcquisitionWrapper
True
The acquired parent is the view. This isn't
usually what you want.
>>> aq_parent(context) == view
True
To get what you usually want, do this
>>> context.aq_inner.aq_parent
<Folder at /test_folder_1_>
C methods work the same
>>> aq_parent(aq_inner(context))
<Folder at /test_folder_1_>
High-level security
-------------------
>>> protected_view_names = [
... 'eagle.txt', 'falcon.html', 'owl.html', 'flamingo.html',
... 'condor.html', 'protectededitform.html']
>>>
>>> public_view_names = [
... 'public_attribute_page',
... 'public_template_page',
... 'public_template_class_page',
... 'nodoc-method', 'nodoc-function', 'nodoc-object',
... 'dirpage1', 'dirpage2']
>>> from Products.Five.testing.restricted import checkRestricted
>>> from Products.Five.testing.restricted import checkUnauthorized
As long as we're not authenticated, we should get Unauthorized for
protected views, but we should be able to view the public ones:
>>> self.logout()
>>> for view_name in protected_view_names:
... checkUnauthorized(
... self.folder,
... 'context.restrictedTraverse("testoid/%s")()' % view_name)
>>> for view_name in public_view_names:
... checkRestricted(
... self.folder,
... 'context.restrictedTraverse("testoid/%s")()' % view_name)
>>> self.login('manager')
Being logged in as a manager again, we find that the protected pages
are not accessible to us:
>>> for view_name in protected_view_names:
... checkRestricted(
... self.folder,
... 'context.restrictedTraverse("testoid/%s")()' % view_name)
>>> checkRestricted(
... self.folder,
... 'context.restrictedTraverse("testoid/eagle.method").eagle()')
Other
-----
Make sure that browser pages can be overridden:
>>> zcml.load_string('''
... <includeOverrides
... package="Products.Five.browser.tests"
... file="overrides.zcml" />
... ''')
>>> view = self.folder.unrestrictedTraverse('testoid/overridden_view')
>>> view()
'The mouse has been eaten by the eagle'
Test traversal to resources from within ZPT pages:
>>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
>>> view = self.folder.unrestrictedTraverse('testoid/parakeet.html')
>>> print view()
<html><body><img alt=""
src="http://nohost/test_folder_1_/testoid/++resource++pattern.png" /></body></html>
Clean up
--------
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five">
<!-- make the zope2.Public permission work -->
<meta:redefinePermission from="zope2.Public" to="zope.Public" />
<!-- attribute page -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
attribute="eagle"
name="eagle.txt"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
name="eagle.method"
permission="zope2.ViewManagementScreens"
allowed_attributes="eagle"
/>
<!-- attribute page -->
<browser:pages
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
permission="zope2.ViewManagementScreens"
>
<browser:page
name="eagle-page.txt"
attribute="eagle"
/>
<browser:page
name="mouse-page.txt"
attribute="mouse"
/>
</browser:pages>
<!-- template/class page -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
template="falcon.pt"
name="falcon.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page (with simple python expression) -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="owl.pt"
name="owl.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page which calls on context using python and path
expressions -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="flamingo.pt"
name="flamingo.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template/class page which calls on context, view, views -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
template="condor.pt"
name="condor.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page that defines a macro page -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="birdmacro.pt"
name="bird.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page that uses macro page -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="seagull.pt"
name="seagull.html"
permission="zope2.ViewManagementScreens"
/>
<!-- test TALES -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="ostrich.pt"
name="ostrich.html"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="tales_traversal.pt"
name="tales_traversal.html"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="template_variables.pt"
name="template_variables.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template security -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="security.pt"
name="security.html"
permission="zope2.View"
/>
<!-- a publicly accessible page, attribute, template, template/class -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
attribute="eagle"
name="public_attribute_page"
permission="zope2.Public"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
template="owl.pt"
name="public_template_page"
permission="zope2.Public"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
template="falcon.pt"
name="public_template_class_page"
permission="zope2.Public"
/>
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
template="parakeet.pt"
name="parakeet.html"
permission="zope2.ViewManagementScreens"
/>
<!-- pages from methods/functions/callables that don't have docstrings -->
<browser:pages
for="Products.Five.testing.simplecontent.ISimpleContent"
class="Products.Five.browser.tests.pages.NoDocstringView"
permission="zope2.Public">
<browser:page
name="nodoc-method"
attribute="method"
/>
<browser:page
name="nodoc-function"
attribute="function"
/>
<browser:page
name="nodoc-object"
attribute="object"
/>
</browser:pages>
<!-- five:pagesFromDirectory loads all .pt files in a directory as pages.
This is mainly used to load Zope2 skin templates so they can be used
in five skins and layers. -->
<five:pagesFromDirectory
for="Products.Five.testing.simplecontent.ISimpleContent"
module="Products.Five.browser.tests"
directory="pages"
permission="zope2.Public"
/>
<!-- browser:page directives with new style classes are ignored -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.NewStyleClass"
name="new_style_class"
attribute="method"
permission="zope2.Public"
/>
<!-- Verify that browser:view works, especially when no specific
view attribute is specified -->
<browser:view
name=""
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
permission="zope2.Public"
/>
<!-- XXX this should really be in Five.form.tests -->
<!-- protected edit form for permission check -->
<browser:editform
schema="Products.Five.testing.simplecontent.ISimpleContent"
name="protectededitform.html"
permission="zope2.ViewManagementScreens"
/>
<!-- stuff that we'll override in overrides.zcml -->
<browser:page
for="Products.Five.testing.simplecontent.ISimpleContent"
class=".pages.SimpleView"
attribute="eagle"
name="overridden_view"
permission="zope2.Public"
/>
</configure>
\ No newline at end of file
Functional Browser Pages Test
=============================
This test tests publishing aspects of browser pages. Let's register
some:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
Let's also add one of our stub objects to play with:
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
Docstrings
----------
In Zope 2, objects normally have to have a docstring in order to be
published. This crazy requirement luckily isn't true for Zope 3, so
it should be possible to write docstring-less view classes that are
still published through ZPublisher.
We see that even though the callables have no docstring, they are
published nevertheless:
>>> print http(r"""
... GET /test_folder_1_/testoid/nodoc-function HTTP/1.1
... """)
HTTP/1.1 200 OK
...
No docstring
>>> print http(r"""
... GET /test_folder_1_/testoid/nodoc-method HTTP/1.1
... """)
HTTP/1.1 200 OK
...
No docstring
>>> print http(r"""
... GET /test_folder_1_/testoid/nodoc-object HTTP/1.1
... """)
HTTP/1.1 200 OK
...
No docstring
Security
--------
Browser pages need to be protected with a permission. Let's test
those; we start by adding two users:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('viewer', 'secret', [], [])
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> protected_view_names = [
... 'eagle.txt', 'falcon.html', 'owl.html', 'flamingo.html',
... 'condor.html', 'protectededitform.html']
>>>
>>> public_view_names = [
... 'public_attribute_page',
... 'public_template_page',
... 'public_template_class_page',
... 'nodoc-method', 'nodoc-function', 'nodoc-object',
... 'dirpage1', 'dirpage2']
>>>
>>> ViewManagementScreens = 'View management screens'
As a normal user we shouldn't get to see those pages protected with
the 'View management screens' permission. Thus we expect a 401
Unauthorized:
>>> for view_name in protected_view_names:
... response = self.publish('/test_folder_1_/testoid/%s' % view_name,
... basic='viewer:secret')
... status = response.getStatus()
... self.failUnless(status == 401, (status, 401, view_name))
The same should apply for the user if he has all other permissions
except 'View management screens':
>>> permissions = self.folder.possible_permissions()
>>> permissions.remove(ViewManagementScreens)
>>> self.folder._addRole('Viewer')
>>> self.folder.manage_role('Viewer', permissions)
>>> self.folder.manage_addLocalRoles('viewer', ['Viewer'])
>>> for view_name in protected_view_names:
... response = self.publish('/test_folder_1_/testoid/%s' % view_name,
... basic='viewer:secret')
... status = response.getStatus()
... self.failUnless(status == 401, (status, 401, view_name))
If we grant 'View management screens' now, the protected views should
become viewable:
>>> self.folder.manage_role('Viewer', [ViewManagementScreens])
>>> for view_name in protected_view_names:
... response = self.publish('/test_folder_1_/testoid/%s' % view_name,
... basic='viewer:secret')
... status = response.getStatus()
... self.failUnless(status == 200, (status, 200, view_name))
Managers should always be able to view anything, including proctected
stuff:
>>> for view_name in protected_view_names:
... response = self.publish('/test_folder_1_/testoid/%s' % view_name,
... basic='manager:r00t')
... self.assertEqual(response.getStatus(), 200)
All public views should always be accessible by anyone:
>>> for view_name in public_view_names:
... response = self.publish('/test_folder_1_/testoid/%s' % view_name)
... status = response.getStatus()
... self.failUnless(status == 200, (status, 200, view_name))
Clean up
--------
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
<html><body><img alt="" src="" tal:attributes="src context/++resource++pattern.png" /></body></html>
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
<p i18n:domain="fivetest" i18n:translate="">This is a message</p>
<p i18n:domain="PlacelessTranslationService" i18n:translate="">Reload this catalog</p>
</body>
</html>
##############################################################################
#
# Copyright (c) 2005 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 the PTS language integration.
$Id: pts_test_languages.py 14400 2005-07-07 17:55:08Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_suite():
from Testing.ZopeTestCase import installProduct, FunctionalDocFileSuite
installProduct('Five')
installProduct('PlacelessTranslationService')
return FunctionalDocFileSuite('pts_test_languages.txt',
package='Products.Five.browser.tests')
if __name__ == '__main__':
framework()
PTS languages
=============
Before we start, we need to set up a manager user to be able to create
the portal:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
We need to 1) configure the Zope 3 i18n message catalogs, 3) register
the PTS languagees adapter and 3) register our test page:
>>> configure_zcml = """
... <configure
... xmlns="http://namespaces.zope.org/zope"
... xmlns:browser="http://namespaces.zope.org/browser"
... xmlns:i18n="http://namespaces.zope.org/i18n"
... >
... <configure package="Products.Five.tests">
... <i18n:registerTranslations directory="locales" />
... </configure>
...
... <adapter
... for="zope.publisher.interfaces.http.IHTTPRequest"
... provides="zope.i18n.interfaces.IUserPreferredLanguages"
... factory="Products.Five.i18n.PTSLanguages"
... />
...
... <configure package="Products.Five.browser.tests">
... <browser:page
... for="Products.Five.interfaces.IFolder"
... template="pts_test_languages.pt"
... name="pts_test_languages.html"
... permission="zope2.View"
... />
... </configure>
... </configure>
... """
>>> from Products.Five import zcml
>>> zcml.load_string(configure_zcml)
Finally, we need a traversable folder so that the test page we
registered is found:
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'ftf')
Now for some actual testing... Our test page is a simple ZPT
translating two messages from different domains. The first domain is
a Zope 3 style one, the second one comes from PTS.
Both systems should yield the same default language (English) when no
language is specified whatsoever:
>>> print http(r"""
... GET /test_folder_1_/ftf/pts_test_languages.html HTTP/1.1
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
<p>This is a message</p>
<p>Reload this catalog</p>
</body>
</html>
Both systems should honour the HTTP ``Accept-Language`` header in the
same way:
>>> print http(r"""
... GET /test_folder_1_/ftf/pts_test_languages.html HTTP/1.1
... Accept-Language: de
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
<p>Dies ist eine Nachricht</p>
<p>Diesen Katalog neu einlesen</p>
</body>
</html>
Both systems should also honour Localizer-specific ways of determining
the language, for example the ``pts_language`` cookie...
>>> print http(r"""
... GET /test_folder_1_/ftf/pts_test_languages.html HTTP/1.1
... Accept-Language: de
... Cookie: pts_language=en
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
<p>This is a message</p>
<p>Reload this catalog</p>
</body>
</html>
... and the ``language`` form field...
>>> print http(r"""
... GET /test_folder_1_/ftf/pts_test_languages.html?language=en HTTP/1.1
... Accept-Language: de
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
<p>This is a message</p>
<p>Reload this catalog</p>
</body>
</html>
... and both the ``pts_language`` cookie and the ``language`` form field:
>>> print http(r"""
... GET /test_folder_1_/ftf/pts_test_languages.html?language=de HTTP/1.1
... Accept-Language: en
... Cookie: pts_language=fr
... """)
HTTP/1.1 200 OK
...
<html>
<body>
<!-- fivetest is a Zope 3 style i18n domain, default is a PTS domain -->
<p>Dies ist eine Nachricht</p>
<p>Diesen Katalog neu einlesen</p>
</body>
</html>
Testing resources
=================
Set up the test fixtures:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
>>> import os, glob
>>> _prefix = os.path.dirname(Products.Five.browser.tests.__file__)
>>> dir_resource_names = [os.path.basename(r) for r in (
... glob.glob('%s/*.png' % _prefix) +
... glob.glob('%s/*.pt' % _prefix) +
... glob.glob('%s/[a-z]*.py' % _prefix) +
... glob.glob('%s/*.css' % _prefix))]
Resource types
--------------
>>> from Products.Five.browser.resource import Resource, PageTemplateResource
Template resource
~~~~~~~~~~~~~~~~~
>>> resource = self.folder.unrestrictedTraverse('testoid/++resource++cockatiel.html')
>>> isinstance(resource, Resource)
True
>>> resource()
'http://nohost/test_folder_1_/testoid/++resource++cockatiel.html'
File resource
~~~~~~~~~~~~~
>>> resource = self.folder.unrestrictedTraverse('testoid/++resource++style.css')
>>> isinstance(resource, Resource)
True
>>> resource()
'http://nohost/test_folder_1_/testoid/++resource++style.css'
Image resource
~~~~~~~~~~~~~~
>>> resource = self.folder.unrestrictedTraverse('testoid/++resource++pattern.png')
>>> isinstance(resource, Resource)
True
>>> resource()
'http://nohost/test_folder_1_/testoid/++resource++pattern.png'
Resource directory
~~~~~~~~~~~~~~~~~~
>>> base = 'testoid/++resource++fivetest_resources/%s'
>>> base_url = 'http://nohost/test_folder_1_/' + base
>>> abs_url = self.folder.unrestrictedTraverse(base % '')()
>>> abs_url + '/' == base_url % ''
True
PageTemplateResource's __call__ renders the template
>>> for r in dir_resource_names:
... resource = self.folder.unrestrictedTraverse(base % r)
... self.assert_(isinstance(resource, Resource))
... if not isinstance(resource, PageTemplateResource):
... self.assertEquals(resource(), base_url % r)
Security
--------
>>> from Products.Five.testing.restricted import checkRestricted
>>> from Products.Five.testing.restricted import checkUnauthorized
>>> resource_names = ['cockatiel.html', 'style.css', 'pattern.png']
We should get Unauthorized as long as we're unauthenticated:
>>> for resource in resource_names:
... checkUnauthorized(
... self.folder,
... 'context.restrictedTraverse("testoid/++resource++%s")()' % resource)
>>> base = 'testoid/++resource++fivetest_resources/%s'
>>> for resource in dir_resource_names:
... path = base % resource
... checkUnauthorized(self.folder, 'context.restrictedTraverse("%s")' % path)
Now let's create a manager user account and log in:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
We can now view them all:
>>> for resource in resource_names:
... checkRestricted(
... self.folder,
... 'context.restrictedTraverse("testoid/++resource++%s")()' % resource)
>>> base = 'testoid/++resource++fivetest_resources/%s'
>>> for resource in dir_resource_names:
... path = base % resource
... checkRestricted(self.folder, 'context.restrictedTraverse("%s")' % path)
Clean up
--------
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<!-- a couple simple resources -->
<browser:resource
template="cockatiel.pt"
name="cockatiel.html"
permission="zope2.ViewManagementScreens"
/>
<browser:resource
file="style.css"
name="style.css"
permission="zope2.ViewManagementScreens"
/>
<browser:resource
image="pattern.png"
name="pattern.png"
permission="zope2.ViewManagementScreens"
/>
<browser:resourceDirectory
name="fivetest_resources"
directory="."
permission="zope2.ViewManagementScreens"
/>
</configure>
\ No newline at end of file
Functional Resource Test
========================
Set up the test fixtures:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('resource.zcml', package=Products.Five.browser.tests)
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
>>> import os, glob
>>> _prefix = os.path.dirname(Products.Five.browser.tests.__file__)
>>> dir_resource_names = [os.path.basename(r) for r in (
... glob.glob('%s/*.png' % _prefix) +
... glob.glob('%s/*.pt' % _prefix) +
... glob.glob('%s/[a-z]*.py' % _prefix) +
... glob.glob('%s/*.css' % _prefix))]
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
Image resource
~~~~~~~~~~~~~~
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++pattern.png HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 200 OK
...
File resource
~~~~~~~~~~~~~
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++style.css HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 200 OK
...
Template resource
~~~~~~~~~~~~~~~~~
>>> print http(r'''
... GET /test_folder_1_/testoid/++resource++cockatiel.html HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 200 OK
...
Resource directory
~~~~~~~~~~~~~~~~~~
Page templates aren't guaranteed to render, so exclude them from the test:
>>> base_url = '/test_folder_1_/testoid/++resource++fivetest_resources/%s'
>>> for r in dir_resource_names:
... if r.endswith('.pt'):
... continue
... response = self.publish(base_url % r, basic='manager:r00t')
... self.assertEquals(200, response.getStatus())
Clean up
--------
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
<html metal:use-macro="context/@@bird.html/birdmacro"><metal:block fill-slot="color">gray</metal:block></html>
View is a view: <tal:block
content="python:hasattr(view,'context') and hasattr(view, 'request')" />
Context is testoid: <tal:block content="python:context.id == 'testoid'" />
Contaxt.aq_parent is test_folder_1_: <tal:block
content="python:context.aq_parent.id =='test_folder_1_'" />
Container is context: <tal:block content="python:container is context" />
Here is context: <tal:block content="python:here is context"/>
Nothing is None: <tal:block content="python:nothing is None"/>
Default works: <tal:block replace="non_existent_var|default" />True
Root is the application: <tal:block
replace="python:repr(root).find('Application') != -1" />
Template is a template: <tal:block
replace="python:repr(template.aq_base).startswith('<ZopeTwoPageTemplateFile')" />
Traverse_subpath exists and is empty: <tal:block
replace="python:traverse_subpath == []" />
Request is a request: <tal:block
replace="python:getattr(request, 'RESPONSE', None) is not None" />
User is manager: <tal:block replace="python:str(user) == 'manager'" />
Options exist: <tal:block replace="python:options is not None" />
Attrs exist: <tal:block replace="python:attrs is not None" />
Repeat exists: <tal:block replace="python:repeat is not None" />
Loop exists: <tal:block replace="python:loop is not None" />
Modules exists: <tal:block replace="python:modules is not None" />
\ No newline at end of file
##############################################################################
#
# Copyright (c) 2004, 2005 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 AbsoluteURL
$Id: test_absoluteurl.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_absoluteurl():
"""This tests the absolute url view (IAbsoluteURL or @@absolute_url),
in particular the breadcrumb functionality.
First we make some preparations:
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
A simple traversal will yield us the @@absolute_url view:
>>> view = self.folder.unrestrictedTraverse('testoid/@@absolute_url')
>>> view()
'http://nohost/test_folder_1_/testoid'
IAbsoluteURL also defines a breadcrumbs() method that returns a
simple Python structure:
>>> for crumb in view.breadcrumbs():
... info = crumb.items()
... info.sort()
... info
[('name', ''), ('url', 'http://nohost')]
[('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')]
[('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')]
This test assures and demonstrates that the absolute url stops
traversing through an object's parents when it has reached the
root object. In Zope 3 this is marked with the IContainmentRoot
interface:
>>> from zope.interface import directlyProvides, providedBy
>>> from zope.app.traversing.interfaces import IContainmentRoot
>>> directlyProvides(self.folder, IContainmentRoot)
>>> for crumb in view.breadcrumbs():
... info = crumb.items()
... info.sort()
... info
[('name', 'test_folder_1_'), ('url', 'http://nohost/test_folder_1_')]
[('name', 'testoid'), ('url', 'http://nohost/test_folder_1_/testoid')]
>>> directlyProvides(self.folder,
... providedBy(self.folder) - IContainmentRoot)
The absolute url view is obviously not affected by virtual hosting:
>>> request = self.app.REQUEST
>>> request['PARENTS'] = [self.folder.test_folder_1_]
>>> url = request.setServerURL(
... protocol='http', hostname='foo.bar.com', port='80')
>>> request.setVirtualRoot('')
>>> for crumb in view.breadcrumbs():
... info = crumb.items()
... info.sort()
... info
[('name', 'test_folder_1_'), ('url', 'http://foo.bar.com')]
[('name', 'testoid'), ('url', 'http://foo.bar.com/testoid')]
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
...@@ -13,18 +13,16 @@ ...@@ -13,18 +13,16 @@
############################################################################## ##############################################################################
"""Test adding views """Test adding views
$Id: test_adding.py 12915 2005-05-31 10:23:19Z philikon $ $Id: test_adding.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os, sys import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
import Products.Five.tests.fivetest # starts Zope, loads Five, etc.
def test_suite(): def test_suite():
from Testing.ZopeTestCase import ZopeDocFileSuite from Testing.ZopeTestCase import ZopeDocFileSuite
return ZopeDocFileSuite('adding.txt', return ZopeDocFileSuite('adding.txt',
package="Products.Five.tests") package="Products.Five.browser.tests")
if __name__ == '__main__': if __name__ == '__main__':
framework() framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 Default View functionality
$Id: test_defaultview.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_default_view():
"""
Test default view functionality
Let's register a couple of default views and make our stub classes
default viewable:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('defaultview.zcml', Products.Five.browser.tests)
Now let's add a couple of stub objects:
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> from Products.Five.testing.simplecontent import manage_addCallableSimpleContent
>>> from Products.Five.testing.simplecontent import manage_addIndexSimpleContent
>>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
>>> manage_addCallableSimpleContent(self.folder, 'testcall', 'TestCall')
>>> manage_addIndexSimpleContent(self.folder, 'testindex', 'TestIndex')
As a last act of preparation, we create a manager login:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
Test a simple default view:
>>> print http(r'''
... GET /test_folder_1_/testoid HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 200 OK
...
The eagle has landed
This tests whether an existing ``index_html`` method is still
supported and called:
>>> print http(r'''
... GET /test_folder_1_/testindex HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
Default index_html called
Disabled __call__ overriding for now. Causese more trouble than it
fixes. Thus, no test here:
#>>> print http(r'''
#... GET /test_folder_1_/testcall HTTP/1.1
#... ''')
#HTTP/1.1 200 OK
#...
#Default __call__ called
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import FunctionalDocTestSuite
return FunctionalDocTestSuite()
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Unit tests for the i18n framework
$Id: test_i18n.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_zpt_i18n():
"""
Test i18n functionality in ZPTs
>>> configure_zcml = '''
... <configure
... xmlns="http://namespaces.zope.org/zope"
... xmlns:browser="http://namespaces.zope.org/browser"
... xmlns:i18n="http://namespaces.zope.org/i18n">
... <configure package="Products.Five.tests">
... <i18n:registerTranslations directory="locales" />
... </configure>
... <configure package="Products.Five.browser.tests">
... <browser:page
... for="Products.Five.interfaces.IFolder"
... template="i18n.pt"
... name="i18n.html"
... permission="zope2.View"
... />
... </configure>
... </configure>'''
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_string(configure_zcml)
In order to be able to traverse to the PageTemplate view, we need
a traversable object:
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
We tell Zope to translate the messages by passing the
``Accept-Language`` header which is processed by the
``IUserPreferredLangauges`` adapter:
>>> print http(r'''
... GET /test_folder_1_/testoid/@@i18n.html HTTP/1.1
... Accept-Language: de
... ''')
HTTP/1.1 200 OK
...
<html>
<body>
<p>Dies ist eine Nachricht</p>
<p>Dies ist eine explizite Nachricht</p>
<p>Dies sind 4 Nachrichten</p>
<p>Dies sind 4 explizite Nachrichten</p>
<table summary="Dies ist ein Attribut">
</table>
<table summary="Explizite Zusammenfassung"
title="Expliziter Titel">
</table>
</body>
</html>
...
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import FunctionalDocTestSuite
from zope.testing.doctest import ELLIPSIS
return FunctionalDocTestSuite(optionflags=ELLIPSIS)
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 browser menus
$Id: test_menu.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_menu():
"""
Test menus
Before we can start we need to set up a few things. For menu
configuration, we have to start a new interaction:
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('menu.zcml', package=Products.Five.browser.tests)
>>> from Products.Five.security import newInteraction
>>> newInteraction()
Now for some actual testing... Let's look up the menu we registered:
>>> from Products.Five.traversable import FakeRequest
>>> from zope.app.publisher.browser.globalbrowsermenuservice import \\
... globalBrowserMenuService
>>> request = FakeRequest()
>>> menu = globalBrowserMenuService.getMenu(
... 'testmenu', self.folder, request)
It should have
>>> len(menu)
4
Sort menu items by title so we get a stable testable result:
>>> menu.sort(lambda x, y: cmp(x['title'], y['title']))
>>> from pprint import pprint
>>> pprint(menu)
[{'action': '@@cockatiel_menu_public.html',
'description': '',
'extra': None,
'selected': '',
'title': u'Page in a menu (public)'},
{'action': u'seagull.html',
'description': u'This is a test menu item',
'extra': None,
'selected': '',
'title': u'Test Menu Item'},
{'action': u'parakeet.html',
'description': u'This is a test menu item',
'extra': None,
'selected': '',
'title': u'Test Menu Item 2'},
{'action': u'falcon.html',
'description': u'This is a test menu item',
'extra': None,
'selected': '',
'title': u'Test Menu Item 3'}]
Let's create a manager user account and log in.
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
>>> newInteraction()
>>> menu = globalBrowserMenuService.getMenu(
... 'testmenu', self.folder, request)
We should get the protected menu items now:
>>> len(menu)
7
>>> menu.sort(lambda x, y: cmp(x['title'], y['title']))
>>> pprint(menu)
[{'action': '@@cockatiel_menu_protected.html',
'description': '',
'extra': None,
'selected': '',
'title': u'Page in a menu (protected)'},
{'action': '@@cockatiel_menu_public.html',
'description': '',
'extra': None,
'selected': '',
'title': u'Page in a menu (public)'},
{'action': u'seagull.html',
'description': u'This is a protected test menu item',
'extra': None,
'selected': '',
'title': u'Protected Test Menu Item'},
{'action': u'falcon.html',
'description': u'This is a protected test menu item',
'extra': None,
'selected': '',
'title': u'Protected Test Menu Item 2'},
{'action': u'seagull.html',
'description': u'This is a test menu item',
'extra': None,
'selected': '',
'title': u'Test Menu Item'},
{'action': u'parakeet.html',
'description': u'This is a test menu item',
'extra': None,
'selected': '',
'title': u'Test Menu Item 2'},
{'action': u'falcon.html',
'description': u'This is a test menu item',
'extra': None,
'selected': '',
'title': u'Test Menu Item 3'}]
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 browser pages
$Id: test_pages.py 18840 2005-10-23 09:47:10Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_ViewAcquisitionWrapping():
"""
>>> import Products.Five.browser.tests
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
>>> zcml.load_config('pages.zcml', package=Products.Five.browser.tests)
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
>>> view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
>>> view is not None
True
>>> from Products.Five.browser.tests.pages import SimpleView
>>> isinstance(view, SimpleView)
True
>>> view()
'The eagle has landed'
This sucks, but we know it
>>> from Acquisition import aq_parent, aq_base
>>> aq_parent(view.context) is view
True
This is the right way to get the context parent
>>> view.context.aq_inner.aq_parent is not view
True
>>> view.context.aq_inner.aq_parent is self.folder
True
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
import unittest
from Testing.ZopeTestCase import installProduct, ZopeDocTestSuite
from Testing.ZopeTestCase import ZopeDocFileSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
installProduct('PythonScripts') # for Five.testing.restricted
return unittest.TestSuite((
ZopeDocTestSuite(),
ZopeDocFileSuite('pages.txt',
package='Products.Five.browser.tests'),
FunctionalDocFileSuite('pages_ftest.txt',
package='Products.Five.browser.tests')
))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 default view recursion
$Id: test_recurse.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_recursion():
"""
Test recursion
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
This test makes sure that recursion is avoided for view lookup.
First, we need to set up a stub interface...
>>> from zope.interface import Interface, implements
>>> class IRecurse(Interface):
... pass
...
and a class that is callable and has a view method:
>>> from OFS.Traversable import Traversable
>>> class Recurse(Traversable):
... implements(IRecurse)
... def view(self):
... return self()
... def __call__(self):
... return 'foo'
...
Now we make the class default viewable and register a default view
name for it:
>>> from Products.Five.fiveconfigure import classDefaultViewable
>>> classDefaultViewable(Recurse)
>>> from zope.app import zapi
>>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> pres = zapi.getGlobalService('Presentation')
>>> pres.setDefaultViewName(IRecurse, IBrowserRequest, 'view')
Here comes the actual test:
>>> ob = Recurse()
>>> ob.view()
'foo'
>>> ob()
'foo'
Clean up:
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 browser resources
$Id: test_resource.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_suite():
import unittest
from Testing.ZopeTestCase import installProduct, ZopeDocFileSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
installProduct('PythonScripts') # for Five.testing.restricted
return unittest.TestSuite((
ZopeDocFileSuite('resource.txt',
package='Products.Five.browser.tests'),
FunctionalDocFileSuite('resource_ftest.txt',
package='Products.Five.browser.tests'),
))
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 Five-traversable classes
$Id: test_traversable.py 17580 2005-09-15 16:05:47Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_traversable():
"""
Test the behaviour of Five-traversable classes.
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
``SimpleContent`` is a traversable class by default. Its fallback
traverser should raise NotFound when traversal fails. (Note: If
we return None in __fallback_traverse__, this test passes but for
the wrong reason: None doesn't have a docstring so BaseRequest
raises NotFoundError.)
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
>>> print http(r'''
... GET /test_folder_1_/testoid/doesntexist HTTP/1.1
... ''')
HTTP/1.1 404 Not Found
...
Now let's take class which already has a __bobo_traverse__ method.
Five should correctly use that as a fallback.
>>> configure_zcml = '''
... <configure xmlns="http://namespaces.zope.org/zope"
... xmlns:meta="http://namespaces.zope.org/meta"
... xmlns:browser="http://namespaces.zope.org/browser"
... xmlns:five="http://namespaces.zope.org/five">
...
... <!-- make the zope2.Public permission work -->
... <meta:redefinePermission from="zope2.Public" to="zope.Public" />
...
... <five:traversable
... class="Products.Five.testing.fancycontent.FancyContent"
... />
...
... <browser:page
... for="Products.Five.testing.fancycontent.IFancyContent"
... class="Products.Five.browser.tests.pages.FancyView"
... attribute="view"
... name="fancy"
... permission="zope2.Public"
... />
...
... </configure>'''
>>> zcml.load_string(configure_zcml)
>>> from Products.Five.testing.fancycontent import manage_addFancyContent
>>> info = manage_addFancyContent(self.folder, 'fancy', '')
In the following test we let the original __bobo_traverse__ method
kick in:
>>> print http(r'''
... GET /test_folder_1_/fancy/something-else HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
something-else
Of course we also need to make sure that Zope 3 style view lookup
actually works:
>>> print http(r'''
... GET /test_folder_1_/fancy/fancy HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
Fancy, fancy
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import FunctionalDocTestSuite
return FunctionalDocTestSuite()
if __name__ == '__main__':
framework()
<configure xmlns="http://namespaces.zope.org/zope" <configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five"> xmlns:five="http://namespaces.zope.org/five">
<include file="meta.zcml" /> <include file="meta.zcml" />
<include file="services.zcml" /> <include file="services.zcml" />
<include file="interfaces.zcml" /> <include file="interfaces.zcml" />
<include file="permissions.zcml" /> <include file="permissions.zcml" />
<include file="browser.zcml" />
<include file="i18n.zcml" /> <include file="i18n.zcml" />
<include package=".browser" />
<include package=".form" />
<include package=".skin" />
<include package="zope.app.event" />
<include package="zope.app.traversing" /> <include package="zope.app.traversing" />
<include package="zope.app.form.browser" />
<!-- do 'traditional' traversing by default; needed by ZPT --> <!-- do 'traditional' traversing by default; needed by ZPT -->
<adapter <adapter
...@@ -31,12 +32,6 @@ ...@@ -31,12 +32,6 @@
provides=".interfaces.IBrowserDefault" provides=".interfaces.IBrowserDefault"
/> />
<adapter
for=".interfaces.IObjectManager"
factory=".adding.ObjectManagerNameChooser"
provides="zope.app.container.interfaces.INameChooser"
/>
<!-- this is really lying, but it's to please checkContainer --> <!-- this is really lying, but it's to please checkContainer -->
<five:implements class="OFS.ObjectManager.ObjectManager" <five:implements class="OFS.ObjectManager.ObjectManager"
interface="zope.app.container.interfaces.IContainer" /> interface="zope.app.container.interfaces.IContainer" />
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
Use 'structured monkey patching' to enable zope.app.container event sending for Use 'structured monkey patching' to enable zope.app.container event sending for
Zope 2 objects. Zope 2 objects.
$Id: eventconfigure.py 12915 2005-05-31 10:23:19Z philikon $ $Id: eventconfigure.py 17810 2005-09-24 09:12:59Z efge $
""" """
from Products.Five.fiveconfigure import isFiveMethod from Products.Five.fiveconfigure import isFiveMethod
from zope.event import notify from zope.event import notify
...@@ -25,6 +25,9 @@ from zope.app.container.interfaces import IObjectAddedEvent,\ ...@@ -25,6 +25,9 @@ from zope.app.container.interfaces import IObjectAddedEvent,\
from zope.app.container.contained import ObjectMovedEvent from zope.app.container.contained import ObjectMovedEvent
from zope.app.event.objectevent import ObjectCopiedEvent from zope.app.event.objectevent import ObjectCopiedEvent
# holds classes that were monkeyed with; for clean up
_monkied = []
# ObjectAddedEvent and ObjectRemovedEvent are different in Zope 2 # ObjectAddedEvent and ObjectRemovedEvent are different in Zope 2
class ObjectAddedEvent(ObjectMovedEvent): class ObjectAddedEvent(ObjectMovedEvent):
implements(IObjectAddedEvent) implements(IObjectAddedEvent)
...@@ -76,15 +79,14 @@ manage_afterAdd.__five_method__ = True ...@@ -76,15 +79,14 @@ manage_afterAdd.__five_method__ = True
def manage_beforeDelete(self, item, container): def manage_beforeDelete(self, item, container):
notify(ObjectRemovedEvent(self)) notify(ObjectRemovedEvent(self))
# call original # call original
method = getattr(self, '__five_manage_beforeDelete', None) method = getattr(self, '__five_original_manage_beforeDelete', None)
if method is not None: if method is not None:
self._five_original_manage_beforeDelete(item, container) self.__five_original_manage_beforeDelete(item, container)
manage_beforeDelete.__five_method__ = True manage_beforeDelete.__five_method__ = True
def classSendEvents(class_): def classSendEvents(class_):
"""Make instances of the class send Object*Event. """Make instances of the class send Object*Event."""
"""
# tuck away original methods if necessary # tuck away original methods if necessary
for name in ['manage_afterAdd', 'manage_beforeDelete']: for name in ['manage_afterAdd', 'manage_beforeDelete']:
method = getattr(class_, name, None) method = getattr(class_, name, None)
...@@ -94,6 +96,8 @@ def classSendEvents(class_): ...@@ -94,6 +96,8 @@ def classSendEvents(class_):
class_.manage_afterAdd = manage_afterAdd class_.manage_afterAdd = manage_afterAdd
class_.manage_beforeDelete = manage_beforeDelete class_.manage_beforeDelete = manage_beforeDelete
# remember class for clean up
_monkied.append(class_)
def sendEvents(_context, class_): def sendEvents(_context, class_):
_context.action( _context.action(
...@@ -101,3 +105,19 @@ def sendEvents(_context, class_): ...@@ -101,3 +105,19 @@ def sendEvents(_context, class_):
callable = classSendEvents, callable = classSendEvents,
args=(class_,) args=(class_,)
) )
# clean up code
from Products.Five.fiveconfigure import killMonkey
from zope.testing.cleanup import addCleanUp
def unsendEvents(class_):
"""Restore class's initial state with respect to sending events"""
for name in ['manage_afterAdd', 'manage_beforeDelete']:
killMonkey(class_, name, '__five_original_'+name)
def cleanUp():
for class_ in _monkied:
unsendEvents(class_)
addCleanUp(cleanUp)
del addCleanUp
...@@ -15,19 +15,26 @@ ...@@ -15,19 +15,26 @@
These directives are specific to Five and have no equivalents in Zope 3. These directives are specific to Five and have no equivalents in Zope 3.
$Id: fiveconfigure.py 12915 2005-05-31 10:23:19Z philikon $ $Id: fiveconfigure.py 17361 2005-09-08 12:13:30Z regebro $
""" """
import os import os
import sys
import glob import glob
import warnings import warnings
import App
from zLOG import LOG, ERROR
from zope.interface import classImplements from zope.interface import classImplements
from zope.configuration import xmlconfig from zope.configuration import xmlconfig
from zope.app.component.interface import provideInterface from zope.app.component.interface import provideInterface
from viewable import Viewable from viewable import Viewable
from traversable import Traversable from traversable import Traversable
from bridge import fromZ2Interface from bridge import fromZ2Interface
from browserconfigure import page from browser.metaconfigure import page
debug_mode = App.config.getConfiguration().debug_mode
def findProducts(): def findProducts():
import Products import Products
...@@ -39,28 +46,48 @@ def findProducts(): ...@@ -39,28 +46,48 @@ def findProducts():
products.append(product) products.append(product)
return products return products
def handleBrokenProduct(product):
if debug_mode:
# Just reraise the error and let Zope handle it.
raise
# Not debug mode. Zope should continue to load. Print a log message:
# XXX It would be really cool if we could make this product appear broken
# in the control panel. However, all attempts to do so has failed from my
# side. //regebro
exc = sys.exc_info()
LOG('Five', ERROR, 'Could not import Product %s' % name, error=exc)
def loadProducts(_context): def loadProducts(_context):
products = findProducts() products = findProducts()
# first load meta.zcml files # first load meta.zcml files
for product in products: for product in products:
zcml = os.path.join(os.path.dirname(product.__file__), 'meta.zcml') zcml = os.path.join(os.path.dirname(product.__file__), 'meta.zcml')
if os.path.isfile(zcml): if os.path.isfile(zcml):
xmlconfig.include(_context, zcml, package=product) try:
xmlconfig.include(_context, zcml, package=product)
except: # Yes, really, *any* kind of error.
handleBrokenProduct(product)
# now load their configure.zcml # now load their configure.zcml
for product in products: for product in products:
zcml = os.path.join(os.path.dirname(product.__file__), zcml = os.path.join(os.path.dirname(product.__file__),
'configure.zcml') 'configure.zcml')
if os.path.isfile(zcml): if os.path.isfile(zcml):
xmlconfig.include(_context, zcml, package=product) try:
xmlconfig.include(_context, zcml, package=product)
except: # Yes, really, *any* kind of error.
handleBrokenProduct(product)
def loadProductsOverrides(_context): def loadProductsOverrides(_context):
for product in findProducts(): for product in findProducts():
zcml = os.path.join(os.path.dirname(product.__file__), zcml = os.path.join(os.path.dirname(product.__file__),
'overrides.zcml') 'overrides.zcml')
if os.path.isfile(zcml): if os.path.isfile(zcml):
xmlconfig.includeOverrides(_context, zcml, package=product) try:
xmlconfig.includeOverrides(_context, zcml, package=product)
except: # Yes, really, *any* kind of error.
handleBrokenProduct(product)
def implements(_context, class_, interface): def implements(_context, class_, interface):
for interface in interface: for interface in interface:
...@@ -79,6 +106,7 @@ def implements(_context, class_, interface): ...@@ -79,6 +106,7 @@ def implements(_context, class_, interface):
def isFiveMethod(m): def isFiveMethod(m):
return hasattr(m, '__five_method__') return hasattr(m, '__five_method__')
_traversable_monkies = []
def classTraversable(class_): def classTraversable(class_):
# If a class already has this attribute, it means it is either a # If a class already has this attribute, it means it is either a
# subclass of Traversable or was already processed with this # subclass of Traversable or was already processed with this
...@@ -104,6 +132,8 @@ def classTraversable(class_): ...@@ -104,6 +132,8 @@ def classTraversable(class_):
setattr(class_, '__bobo_traverse__', setattr(class_, '__bobo_traverse__',
Traversable.__bobo_traverse__.im_func) Traversable.__bobo_traverse__.im_func)
setattr(class_, '__five_traversable__', True) setattr(class_, '__five_traversable__', True)
# remember class for clean up
_traversable_monkies.append(class_)
def traversable(_context, class_): def traversable(_context, class_):
_context.action( _context.action(
...@@ -112,6 +142,7 @@ def traversable(_context, class_): ...@@ -112,6 +142,7 @@ def traversable(_context, class_):
args = (class_,) args = (class_,)
) )
_defaultviewable_monkies = []
def classDefaultViewable(class_): def classDefaultViewable(class_):
# If a class already has this attribute, it means it is either a # If a class already has this attribute, it means it is either a
# subclass of DefaultViewable or was already processed with this # subclass of DefaultViewable or was already processed with this
...@@ -136,6 +167,8 @@ def classDefaultViewable(class_): ...@@ -136,6 +167,8 @@ def classDefaultViewable(class_):
setattr(class_, '__browser_default__', setattr(class_, '__browser_default__',
Viewable.__browser_default__.im_func) Viewable.__browser_default__.im_func)
setattr(class_, '__five_viewable__', True) setattr(class_, '__five_viewable__', True)
# remember class for clean up
_defaultviewable_monkies.append(class_)
def defaultViewable(_context, class_): def defaultViewable(_context, class_):
_context.action( _context.action(
...@@ -200,4 +233,44 @@ def pagesFromDirectory(_context, directory, module, for_=None, ...@@ -200,4 +233,44 @@ def pagesFromDirectory(_context, directory, module, for_=None,
page(_context, name=name, permission=permission, page(_context, name=name, permission=permission,
layer=layer, for_=for_, template=fname) layer=layer, for_=for_, template=fname)
# clean up code
def killMonkey(class_, name, fallback, attr=None):
"""Die monkey, die!"""
method = getattr(class_, name, None)
if isFiveMethod(method):
original = getattr(class_, fallback, None)
if original is None:
try:
delattr(class_, name)
except AttributeError:
pass
else:
setattr(class_, name, original)
if attr is not None:
try:
delattr(class_, attr)
except (AttributeError, KeyError):
pass
def untraversable(class_):
"""Restore class's initial state with respect to traversability"""
killMonkey(class_, '__bobo_traverse__', '__fallback_traverse__',
'__five_traversable__')
def undefaultViewable(class_):
"""Restore class's initial state with respect to being default
viewable."""
killMonkey(class_, '__browser_default__', '__fallback_default__',
'__five_viewable__')
def cleanUp():
for class_ in _traversable_monkies:
untraversable(class_)
for class_ in _defaultviewable_monkies:
undefaultViewable(class_)
from zope.testing.cleanup import addCleanUp
addCleanUp(cleanUp)
del addCleanUp
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Five ZCML directive schemas """Five ZCML directive schemas
$Id: fivedirectives.py 12915 2005-05-31 10:23:19Z philikon $ $Id: fivedirectives.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from zope.interface import Interface from zope.interface import Interface
from zope.app.publisher.browser.metadirectives import IBasicResourceInformation from zope.app.publisher.browser.metadirectives import IBasicResourceInformation
......
...@@ -11,27 +11,17 @@ ...@@ -11,27 +11,17 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Provide basic browser functionality """Add and edit views
$Id: browser.py 12915 2005-05-31 10:23:19Z philikon $ $Id: __init__.py 15514 2005-08-02 16:37:34Z yuppie $
""" """
# python
import sys import sys
from datetime import datetime from datetime import datetime
import transaction
# Zope 2
import Acquisition import Acquisition
from Acquisition import aq_inner, aq_parent, aq_base import transaction
from AccessControl import ClassSecurityInfo from zope.event import notify
from Globals import InitializeClass from zope.schema.interfaces import ValidationError
# Zope 3
from interfaces import ITraversable
from zope.interface import implements
from zope.component import getViewProviding
from zope.app.traversing.browser.interfaces import IAbsoluteURL
from zope.publisher.browser import isCGI_NAME from zope.publisher.browser import isCGI_NAME
from zope.i18n.interfaces import IUserPreferredCharsets from zope.i18n.interfaces import IUserPreferredCharsets
...@@ -40,86 +30,13 @@ from zope.app.location import LocationProxy ...@@ -40,86 +30,13 @@ from zope.app.location import LocationProxy
from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
from zope.app.form.browser.submit import Update from zope.app.form.browser.submit import Update
from zope.app.form.interfaces import WidgetsError, MissingInputError from zope.app.form.interfaces import WidgetsError, MissingInputError
from zope.event import notify
from zope.app.form.utility import setUpWidgets, getWidgetsData from zope.app.form.utility import setUpWidgets, getWidgetsData
from zope.app.form.interfaces import IInputWidget, WidgetsError from zope.app.form.interfaces import IInputWidget, WidgetsError
from zope.schema.interfaces import ValidationError
from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent
from zope.app.i18n import ZopeMessageIDFactory as _
# Five from Products.Five.browser import BrowserView
from Products.Five.pagetemplatefile import FivePageTemplateFile from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
class BrowserView(Acquisition.Explicit):
security = ClassSecurityInfo()
def __init__(self, context, request):
self.context = context
self.request = request
# XXX do not create any methods on the subclass called index_html,
# as this makes Zope 2 traverse into that first!
InitializeClass(BrowserView)
class AbsoluteURL(BrowserView):
"""An adapter for Zope3-style absolute_url using Zope2 methods
(original: zope.app.traversing.browser.absoluteurl)
"""
def __init__(self, context, request):
self.context, self.request = context, request
implements(IAbsoluteURL)
def __str__(self):
context = aq_inner(self.context)
return context.absolute_url()
__call__ = __str__
def breadcrumbs(self):
context = self.context.aq_inner
container = context.aq_parent
request = self.request
name = context.getId()
if container is None or self._isVirtualHostRoot() \
or not ITraversable.providedBy(container):
return (
{'name': name, 'url': context.absolute_url()},)
view = getViewProviding(container, IAbsoluteURL, request)
base = tuple(view.breadcrumbs())
base += (
{'name': name, 'url': ("%s/%s" % (base[-1]['url'], name))},)
return base
def _isVirtualHostRoot(self):
virtualrootpath = self.request.get('VirtualRootPhysicalPath', None)
if virtualrootpath is None:
return False
context = self.context.aq_inner
return context.restrictedTraverse(virtualrootpath) == context
class SiteAbsoluteURL(AbsoluteURL):
"""An adapter for Zope3-style absolute_url using Zope2 methods
This one is just used to stop breadcrumbs from crumbing up
to the Zope root.
(original: zope.app.traversing.browser.absoluteurl)
"""
def breadcrumbs(self):
context = self.context
request = self.request
return ({'name': context.getId(),
'url': context.absolute_url()
},)
class EditView(BrowserView): class EditView(BrowserView):
"""Simple edit-view base class """Simple edit-view base class
...@@ -136,7 +53,7 @@ class EditView(BrowserView): ...@@ -136,7 +53,7 @@ class EditView(BrowserView):
# Fall-back field names computes from schema # Fall-back field names computes from schema
fieldNames = property(lambda self: getFieldNamesInOrder(self.schema)) fieldNames = property(lambda self: getFieldNamesInOrder(self.schema))
# Fall-back template # Fall-back template
generated_form = FivePageTemplateFile('edit.pt') generated_form = ZopeTwoPageTemplateFile('edit.pt')
def __init__(self, context, request): def __init__(self, context, request):
BrowserView.__init__(self, context, request) BrowserView.__init__(self, context, request)
...@@ -218,7 +135,7 @@ class EditView(BrowserView): ...@@ -218,7 +135,7 @@ class EditView(BrowserView):
notify(ObjectModifiedEvent(content)) notify(ObjectModifiedEvent(content))
except WidgetsError, errors: except WidgetsError, errors:
self.errors = errors self.errors = errors
status = "An error occured." status = _("An error occured.")
transaction.abort() transaction.abort()
else: else:
setUpEditWidgets(self, self.schema, source=self.adapted, setUpEditWidgets(self, self.schema, source=self.adapted,
...@@ -226,13 +143,13 @@ class EditView(BrowserView): ...@@ -226,13 +143,13 @@ class EditView(BrowserView):
names=self.fieldNames) names=self.fieldNames)
if changed: if changed:
self.changed() self.changed()
# XXX: Needs i18n support: # XXX: Needs locale support:
# formatter = self.request.locale.dates.getFormatter( # formatter = self.request.locale.dates.getFormatter(
# 'dateTime', 'medium') # 'dateTime', 'medium')
# status = _("Updated on ${date_time}") status = _("Updated on ${date_time}")
# status.mapping = {'date_time': formatter.format( # status.mapping = {'date_time': formatter.format(
# datetime.utcnow())} # datetime.utcnow())}
status = "Updated on %s" % str(datetime.utcnow()) status.mapping = {'date_time': str(datetime.utcnow())}
self.update_status = status self.update_status = status
return status return status
...@@ -260,7 +177,7 @@ class AddView(EditView): ...@@ -260,7 +177,7 @@ class AddView(EditView):
self.createAndAdd(data) self.createAndAdd(data)
except WidgetsError, errors: except WidgetsError, errors:
self.errors = errors self.errors = errors
self.update_status = "An error occured." self.update_status = _("An error occured.")
return self.update_status return self.update_status
self.request.response.redirect(self.nextURL()) self.request.response.redirect(self.nextURL())
...@@ -332,3 +249,9 @@ class AddView(EditView): ...@@ -332,3 +249,9 @@ class AddView(EditView):
def nextURL(self): def nextURL(self):
return self.context.nextURL() return self.context.nextURL()
# BBB: Will be removed in future versions
from Products.Five import browser
browser.AddView = AddView
browser.EditView = EditView
<html metal:use-macro="context/@@standard_macros/page"> <html metal:use-macro="context/@@standard_macros/page"
i18n:domain="zope">
<body> <body>
<div metal:fill-slot="body"> <div metal:fill-slot="body">
...@@ -12,7 +13,7 @@ ...@@ -12,7 +13,7 @@
<h3 tal:condition="view/label" <h3 tal:condition="view/label"
tal:content="view/label" tal:content="view/label"
metal:define-slot="heading" metal:define-slot="heading"
>Edit something</h3> >Add something</h3>
<p tal:define="status view/update" <p tal:define="status view/update"
tal:condition="status" tal:condition="status"
...@@ -31,7 +32,7 @@ ...@@ -31,7 +32,7 @@
<div class="label"><input type="text" style="width:100%" /></div> <div class="label"><input type="text" style="width:100%" /></div>
</div> </div>
<div metal:use-macro="context/@@widget_macros/widget_rows" /> <div metal:use-macro="context/@@form_macros/widget_rows" />
<div class="separator"></div> <div class="separator"></div>
...@@ -70,3 +71,4 @@ ...@@ -70,3 +71,4 @@
</body> </body>
</html> </html>
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<include package="zope.app.form.browser" />
<browser:page
for="*"
name="form_macros"
permission="zope2.View"
class=".macros.FormMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
</configure>
\ No newline at end of file
<tal:tag condition="view/update"/> <tal:tag condition="view/update"/>
<html metal:use-macro="context/@@standard_macros/page"> <html metal:use-macro="context/@@standard_macros/view"
i18n:domain="zope">
<body> <body>
<div metal:fill-slot="body"> <div metal:fill-slot="body">
...@@ -33,7 +34,7 @@ ...@@ -33,7 +34,7 @@
<div class="field"><input type="text" style="width:100%" /></div> <div class="field"><input type="text" style="width:100%" /></div>
</div> </div>
<div metal:use-macro="context/@@widget_macros/widget_rows" /> <div metal:use-macro="context/@@form_macros/widget_rows" />
<div class="separator"></div> <div class="separator"></div>
......
############################################################################## ##############################################################################
# #
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved. # Copyright (c) 2004, 2005 Zope Corporation and Contributors.
# All Rights Reserved.
# #
# This software is subject to the provisions of the Zope Public License, # 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. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
...@@ -10,5 +11,12 @@ ...@@ -10,5 +11,12 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Form macros
# Five.tests.products $Id: macros.py 12884 2005-05-30 13:10:41Z philikon $
"""
from Products.Five.skin.standardmacros import StandardMacros
# copy of zope.app.form.browser.macros.FormMacros
class FormMacros(StandardMacros):
macro_pages = ('widget_macros', 'addform_macros')
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<meta:directives namespace="http://namespaces.zope.org/browser">
<meta:complexDirective
name="editform"
schema="zope.app.form.browser.metadirectives.IEditFormDirective"
handler=".metaconfigure.EditFormDirective"
>
<meta:subdirective
name="widget"
schema="zope.app.form.browser.metadirectives.IWidgetSubdirective"
/>
</meta:complexDirective>
<meta:complexDirective
name="addform"
schema="zope.app.form.browser.metadirectives.IAddFormDirective"
handler=".metaconfigure.AddFormDirective"
>
<meta:subdirective
name="widget"
schema="zope.app.form.browser.metadirectives.IWidgetSubdirective"
/>
</meta:complexDirective>
</meta:directives>
</configure>
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Edit form directives
$Id: metaconfigure.py 12884 2005-05-30 13:10:41Z philikon $
"""
import ExtensionClass
from zope.component import getGlobalService
from zope.component.servicenames import Presentation
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.app.publisher.browser.globalbrowsermenuservice import \
menuItemDirective
from zope.app.form.browser.metaconfigure import BaseFormDirective
from zope.app.container.interfaces import IAdding
from Products.Five.form import EditView, AddView
from Products.Five.metaclass import makeClass
from Products.Five.security import protectClass, initializeClass
from Products.Five.browser.pagetemplatefile import ZopeTwoPageTemplateFile
from Products.Five.browser.metaconfigure import makeClassForTemplate
def EditViewFactory(name, schema, label, permission, layer,
template, default_template, bases, for_, fields,
fulledit_path=None, fulledit_label=None, menu=u''):
s = getGlobalService(Presentation)
class_ = makeClassForTemplate(template, globals(), used_for=schema,
bases=bases)
class_.schema = schema
class_.label = label
class_.fieldNames = fields
class_.fulledit_path = fulledit_path
if fulledit_path and (fulledit_label is None):
fulledit_label = "Full edit"
class_.fulledit_label = fulledit_label
class_.generated_form = ZopeTwoPageTemplateFile(default_template)
s.provideView(for_, name, IBrowserRequest, class_, layer)
protectClass(class_, permission)
initializeClass(class_)
class FiveFormDirective(BaseFormDirective):
def _processWidgets(self):
if self._widgets:
customWidgetsObject = makeClass('CustomWidgetsMixin', (ExtensionClass.Base,), self._widgets)
self.bases = self.bases + (customWidgetsObject,)
class EditFormDirective(FiveFormDirective):
view = EditView
default_template = 'edit.pt'
title = 'Edit'
def _handle_menu(self):
if self.menu:
menuItemDirective(
self._context, self.menu, self.for_ or self.schema,
'@@' + self.name, self.title, permission=self.permission)
def __call__(self):
self._processWidgets()
self._handle_menu()
self._context.action(
discriminator=self._discriminator(),
callable=EditViewFactory,
args=self._args(),
kw={'menu': self.menu},
)
def AddViewFactory(name, schema, label, permission, layer,
template, default_template, bases, for_,
fields, content_factory, arguments,
keyword_arguments, set_before_add, set_after_add,
menu=u''):
s = getGlobalService(Presentation)
class_ = makeClassForTemplate(template, globals(), used_for=schema,
bases=bases)
class_.schema = schema
class_.label = label
class_.fieldNames = fields
class_._factory = content_factory
class_._arguments = arguments
class_._keyword_arguments = keyword_arguments
class_._set_before_add = set_before_add
class_._set_after_add = set_after_add
class_.generated_form = ZopeTwoPageTemplateFile(default_template)
s.provideView(for_, name, IBrowserRequest, class_, layer)
protectClass(class_, permission)
initializeClass(class_)
class AddFormDirective(FiveFormDirective):
view = AddView
default_template = 'add.pt'
for_ = IAdding
# default add form information
description = None
content_factory = None
arguments = None
keyword_arguments = None
set_before_add = None
set_after_add = None
def _handle_menu(self):
if self.menu or self.title:
if (not self.menu) or (not self.title):
raise ValueError("If either menu or title are specified, "
"they must both be specified")
# Add forms are really for IAdding components, so do not use
# for=self.schema.
menuItemDirective(
self._context, self.menu, self.for_, '@@' + self.name,
self.title, permission=self.permission,
description=self.description)
def _handle_arguments(self, leftover=None):
schema = self.schema
fields = self.fields
arguments = self.arguments
keyword_arguments = self.keyword_arguments
set_before_add = self.set_before_add
set_after_add = self.set_after_add
if leftover is None:
leftover = fields
if arguments:
missing = [n for n in arguments if n not in fields]
if missing:
raise ValueError("Some arguments are not included in the form",
missing)
optional = [n for n in arguments if not schema[n].required]
if optional:
raise ValueError("Some arguments are optional, use"
" keyword_arguments for them",
optional)
leftover = [n for n in leftover if n not in arguments]
if keyword_arguments:
missing = [n for n in keyword_arguments if n not in fields]
if missing:
raise ValueError(
"Some keyword_arguments are not included in the form",
missing)
leftover = [n for n in leftover if n not in keyword_arguments]
if set_before_add:
missing = [n for n in set_before_add if n not in fields]
if missing:
raise ValueError(
"Some set_before_add are not included in the form",
missing)
leftover = [n for n in leftover if n not in set_before_add]
if set_after_add:
missing = [n for n in set_after_add if n not in fields]
if missing:
raise ValueError(
"Some set_after_add are not included in the form",
missing)
leftover = [n for n in leftover if n not in set_after_add]
self.set_after_add += leftover
else:
self.set_after_add = leftover
def __call__(self):
self._processWidgets()
self._handle_menu()
self._handle_arguments()
self._context.action(
discriminator=self._discriminator(),
callable=AddViewFactory,
args=self._args()+(self.content_factory, self.arguments,
self.keyword_arguments,
self.set_before_add, self.set_after_add),
kw={'menu': self.menu},
)
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five"
xmlns:i18n="http://namespaces.zope.org/i18n"
i18n_domain="formtest">
<!-- make the zope2.Public permission work -->
<meta:redefinePermission from="zope2.Public" to="zope.Public" />
<!-- browser forms -->
<five:traversable class=".schemacontent.FieldContent" />
<browser:editform
schema=".schemacontent.IFieldContent"
for=".schemacontent.IFieldContent"
name="edit.html"
label="Edit Field Content"
permission="zope2.Public"
/>
<five:traversable class=".schemacontent.ComplexSchemaContent" />
<browser:editform
schema=".schemacontent.IComplexSchemaContent"
for=".schemacontent.IComplexSchemaContent"
name="edit.html"
permission="zope2.Public"
class=".schemacontent.ComplexSchemaView"
/>
<view
type="zope.publisher.interfaces.browser.IBrowserRequest"
for="zope.schema.interfaces.IObject"
provides="zope.app.form.interfaces.IInputWidget"
factory="zope.app.form.browser.objectwidget.ObjectWidget"
permission="zope.Public"
/>
<!-- With a widget override -->
<browser:editform
schema=".schemacontent.IFieldContent"
for=".schemacontent.IFieldContent"
name="widgetoverride.html"
permission="zope2.Public"
>
<widget
field="description"
class="zope.app.form.browser.TextAreaWidget"
/>
</browser:editform>
<browser:addform
schema=".schemacontent.IFieldContent"
content_factory=".schemacontent.FieldContent"
name="addfieldcontent.html"
label="Add Field Content"
permission="zope2.Public"
/>
<browser:addform
schema=".schemacontent.IFieldContent"
content_factory=".schemacontent.FieldContent"
name="addwidgetoverride.html"
permission="zope2.Public">
<widget
field="description"
class="zope.app.form.browser.TextAreaWidget"
/>
</browser:addform>
<browser:addform
schema=".schemacontent.IFieldContent"
content_factory=".schemacontent.FieldContent"
name="protectedaddform.html"
permission="zope2.ViewManagementScreens"
/>
<i18n:registerTranslations directory="locales"/>
</configure>
Testing forms Testing forms
============= =============
Before we can begin, we need to setup a traversable folder. Before we can begin, we need to set up a few things. We need a
Otherwise, Five won't get to to do its view lookup: manager account:
>>> from Products.Five.tests.products.FiveTest.helpers import \ >>> uf = self.folder.acl_users
... manage_addFiveTraversableFolder >>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
We need to configure all of Five for the functional test:
>>> import Products.Five.form.tests
>>> from Products.Five import zcml
>>> zcml.load_config('configure.zcml', package=Products.Five)
>>> zcml.load_config('configure.zcml', package=Products.Five.form.tests)
Finally, we need to setup a traversable folder. Otherwise, Five won't
get to to do its view lookup:
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'ftf') >>> manage_addFiveTraversableFolder(self.folder, 'ftf')
...@@ -30,15 +42,14 @@ For a protected one we need a manager account: ...@@ -30,15 +42,14 @@ For a protected one we need a manager account:
HTTP/1.1 200 OK HTTP/1.1 200 OK
... ...
otherwise we will fail to access it: otherwise we will fail to access it (N.B: this test will fail on Zope 2.8.1,
which incorrectly ignored its 'handle_errors' argument):
>>> print http(r""" >>> print http(r"""
... GET /test_folder_1_/ftf/+/protectedaddform.html HTTP/1.1 ... GET /test_folder_1_/ftf/+/protectedaddform.html HTTP/1.1
... Authorization: Basic viewer:secret ... """, handle_errors=True)
... """, handle_errors=False) HTTP/1.1 401 Unauthorized
Traceback (most recent call last):
... ...
Unauthorized: ...
Now let's add a piece of our sample content object to test more things Now let's add a piece of our sample content object to test more things
on it: on it:
...@@ -76,7 +87,6 @@ Having added this piece of content, we can access it under its URL: ...@@ -76,7 +87,6 @@ Having added this piece of content, we can access it under its URL:
>>> print http(r""" >>> print http(r"""
... GET /test_folder_1_/ftf/edittest HTTP/1.1 ... GET /test_folder_1_/ftf/edittest HTTP/1.1
... Authorization: Basic manager:r00t
... """, handle_errors=False) ... """, handle_errors=False)
HTTP/1.1 200 OK HTTP/1.1 200 OK
... ...
...@@ -411,13 +421,14 @@ The object's data will have changed accordingly: ...@@ -411,13 +421,14 @@ The object's data will have changed accordingly:
>>> unicodetest.somelist == [de_guo.decode('utf-8')] >>> unicodetest.somelist == [de_guo.decode('utf-8')]
True True
Object widget: Object widget:
-------------- --------------
A little more complex is the ``ObjectWidget``. Here we simply test A little more complex is the ``ObjectWidget``. Here we simply test
that the edit form works: that the edit form works:
>>> from Products.Five.tests.products.FiveTest.schemacontent import \ >>> from Products.Five.form.tests.schemacontent import \
... manage_addComplexSchemaContent ... manage_addComplexSchemaContent
>>> n = manage_addComplexSchemaContent(self.folder.ftf, 'objecttest') >>> n = manage_addComplexSchemaContent(self.folder.ftf, 'objecttest')
...@@ -426,3 +437,140 @@ that the edit form works: ...@@ -426,3 +437,140 @@ that the edit form works:
... """, handle_errors=False) ... """, handle_errors=False)
HTTP/1.1 200 OK HTTP/1.1 200 OK
... ...
i18n:
-----
And now the add form in German:
>>> print http(r"""
... GET /test_folder_1_/ftf/+/addfieldcontent.html HTTP/1.1
... Accept-Language: de
... Authorization: Basic manager:r00t
... """, handle_errors=False)
HTTP/1.1 200 OK
...Felderinhalt hinzuf...
...Eine kurz...Titel...
...Eine ausf...Beschreibung...
...Irgendeine Zahl...
...Irgendeine Liste...
...hinzuf...
...Auffrischen...
...Hinzuf...
...Objektname...
The same with an input error:
>>> print http(r"""
... POST /test_folder_1_/ftf/+/addfieldcontent.html HTTP/1.1
... Accept-Language: de
... Authorization: Basic manager:r00t
... Content-Length: 670
... Content-Type: multipart/form-data; boundary=---------------------------19588947601368617292863650127
...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.title"
...
...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.description"
...
...
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="field.somenumber"
...
... 0
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Hinzufxgen
... -----------------------------19588947601368617292863650127
... Content-Disposition: form-data; name="add_input_name"
...
...
... -----------------------------19588947601368617292863650127--
... """, handle_errors=False)
HTTP/1.1 200 OK
...Felderinhalt hinzuf...
...Ein Fehler ist aufgetreten...
...Es gab <strong>1</strong> Eingabefehler...
...Eine kurz...Titel...
...Erforderliche Eingabe fehlt...
...Eine ausf...Beschreibung...
...Irgendeine Zahl...
...Irgendeine Liste...
...hinzuf...
...Auffrischen...
...Hinzuf...
...Objektname...
And now the translated edit form:
>>> from Products.Five.form.tests.schemacontent import \
... manage_addFieldContent
>>> dummy = manage_addFieldContent(self.folder.ftf, 'i18ntest', 'titel')
>>> print http(r"""
... GET /test_folder_1_/ftf/i18ntest/edit.html HTTP/1.1
... Accept-Language: de
... Authorization: Basic manager:r00t
... """, handle_errors=False)
HTTP/1.1 200 OK
...Felderinhalt bearbeiten...
...Eine kurz...Titel...
...Eine ausf...Beschreibung...
...Irgendeine Zahl...
...Irgendeine Liste...
...hinzuf...
...Auffrischen...
...Abschicken...
Again with an input error:
>>> print http(r"""
... POST /test_folder_1_/ftf/i18ntest/edit.html HTTP/1.1
... Accept-Language: de
... Authorization: Basic manager:r00t
... Content-Length: 550
... Content-Type: multipart/form-data; boundary=---------------------------13070562555632576681565633754
...
... -----------------------------13070562555632576681565633754
... Content-Disposition: form-data; name="field.title"
...
...
... -----------------------------13070562555632576681565633754
... Content-Disposition: form-data; name="field.description"
...
...
... -----------------------------13070562555632576681565633754
... Content-Disposition: form-data; name="field.somenumber"
...
... 0
... -----------------------------13070562555632576681565633754
... Content-Disposition: form-data; name="UPDATE_SUBMIT"
...
... Abschicken
... -----------------------------13070562555632576681565633754--
... """, handle_errors=False)
HTTP/1.1 200 OK
...Felderinhalt bearbeiten...
...Ein Fehler ist aufgetreten...
...Es gab <strong>1</strong> Eingabefehler...
...Eine kurz...Titel...
...Erforderliche Eingabe fehlt...
...Eine ausf...Beschreibung...
...Irgendeine Zahl...
...Irgendeine Liste...
...hinzuf...
...Auffrischen...
...Abschicken...
Clean up
--------
Finally, we need to clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
##############################################################################
#
# ZopeTestCase
#
# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
#
# This version of framework.py will use the SOFTWARE_HOME
# environment variable to locate Zope and the Testing package.
#
# If the tests are run in an INSTANCE_HOME installation of Zope,
# Products.__path__ and sys.path with be adjusted to include the
# instance's Products and lib/python directories respectively.
#
# If you explicitly set INSTANCE_HOME prior to running the tests,
# auto-detection is disabled and the specified path will be used
# instead.
#
# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
# will be adjusted to use it.
#
# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
# is assumed, and you can attach to a running ZEO server (via the
# instance's custom_zodb.py).
#
##############################################################################
#
# The following code should be at the top of every test module:
#
# import os, sys
# if __name__ == '__main__':
# execfile(os.path.join(sys.path[0], 'framework.py'))
#
# ...and the following at the bottom:
#
# if __name__ == '__main__':
# framework()
#
##############################################################################
__version__ = '0.2.3'
# Save start state
#
__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
if __SOFTWARE_HOME.endswith(os.sep):
__SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
if __INSTANCE_HOME.endswith(os.sep):
__INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
# Find and import the Testing package
#
if not sys.modules.has_key('Testing'):
p0 = sys.path[0]
if p0 and __name__ == '__main__':
os.chdir(p0)
p0 = ''
s = __SOFTWARE_HOME
p = d = s and s or os.getcwd()
while d:
if os.path.isdir(os.path.join(p, 'Testing')):
zope_home = os.path.dirname(os.path.dirname(p))
sys.path[:1] = [p0, p, zope_home]
break
p, d = s and ('','') or os.path.split(p)
else:
print 'Unable to locate Testing package.',
print 'You might need to set SOFTWARE_HOME.'
sys.exit(1)
import Testing, unittest
execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
# Include ZopeTestCase support
#
if 1: # Create a new scope
p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
if not os.path.isdir(p):
print 'Unable to locate ZopeTestCase package.',
print 'You might need to install ZopeTestCase.'
sys.exit(1)
ztc_common = 'ztc_common.py'
ztc_common_global = os.path.join(p, ztc_common)
f = 0
if os.path.exists(ztc_common_global):
execfile(ztc_common_global)
f = 1
if os.path.exists(ztc_common):
execfile(ztc_common)
f = 1
if not f:
print 'Unable to locate %s.' % ztc_common
sys.exit(1)
# Debug
#
print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
sys.stdout.flush()
############################################################################## ##############################################################################
# #
# Copyright (c) 2004, 2005 Zope Corporation and Contributors. # Copyright (c) 2005 Zope Corporation and Contributors.
# All Rights Reserved. # All Rights Reserved.
# #
# This software is subject to the provisions of the Zope Public License, # This software is subject to the provisions of the Zope Public License,
...@@ -11,31 +11,41 @@ ...@@ -11,31 +11,41 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Schema content implementation msgid ""
msgstr ""
"Project-Id-Version: Five form tests\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: 2005-07-29 11:38+0100\n"
"Last-Translator: \n"
"Language-Team: Five Developers <z3-five@zope.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
$Id: schemacontent.py 12915 2005-05-31 10:23:19Z philikon $ msgid "Title"
""" msgstr "Titel"
from OFS.SimpleItem import SimpleItem
from Globals import InitializeClass
from zope.interface import implements msgid "A short description of the event."
from interfaces import IComplexSchemaContent msgstr "Eine kurze Beschreibung des Ereignisses."
from simplecontent import FieldSimpleContent
class ComplexSchemaContent(SimpleItem): msgid "Description"
implements(IComplexSchemaContent) msgstr "Beschreibung"
meta_type ="Complex Schema Content" msgid "A long description of the event."
msgstr "Eine ausführliche Beschreibung des Ereignisses."
def __init__(self, id): msgid "Some number"
self.id = id msgstr "Irgendeine Zahl"
self.fish = FieldSimpleContent('fish', 'title')
self.fish.description = ""
self.fishtype = 'Lost fishy'
InitializeClass(ComplexSchemaContent) msgid "Some List"
msgstr "Irgendeine Liste"
msgid "Some item"
msgstr "Irgendeine Element"
msgid "Edit Field Content"
msgstr "Felderinhalt bearbeiten"
msgid "Add Field Content"
msgstr "Felderinhalt hinzufügen"
def manage_addComplexSchemaContent(self, id, REQUEST=None):
"""Add the fancy fancy content."""
id = self._setObject(id, ComplexSchemaContent(id))
return ''
##############################################################################
#
# Copyright (c) 2005 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.
#
##############################################################################
msgid ""
msgstr ""
"Project-Id-Version: Five form tests\n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: Five Developers <z3-five@zope.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Title"
msgstr ""
msgid "A short description of the event."
msgstr ""
msgid "Description"
msgstr ""
msgid "A long description of the event."
msgstr ""
msgid "Some number"
msgstr ""
msgid "Some List"
msgstr ""
msgid "Some item"
msgstr ""
msgid "Edit Field Content"
msgstr ""
msgid "Add Field Content"
msgstr ""
...@@ -11,80 +11,68 @@ ...@@ -11,80 +11,68 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Test product interfaces """Demo schema content
$Id: interfaces.py 12915 2005-05-31 10:23:19Z philikon $ $Id: schemacontent.py 15514 2005-08-02 16:37:34Z yuppie $
""" """
from zope.interface import Interface from OFS.SimpleItem import SimpleItem
from zope.schema import Text, TextLine, Object, Int, List from Globals import InitializeClass
class IAdaptable(Interface): from zope.i18nmessageid import MessageIDFactory
"""This is a Zope 3 interface. from zope.interface import implements, Interface
""" from zope.schema import TextLine, Text, Object, Int, List
def method(): from zope.app.form import CustomWidgetFactory
"""This method will be adapted from zope.app.form.browser import ObjectWidget
"""
class IAdapted(Interface): _ = MessageIDFactory('formtest')
"""The interface we adapt to.
"""
def adaptedMethod(): class IFieldContent(Interface):
"""A method to adapt.
"""
class IOrigin(Interface):
"""Something we'll adapt"""
class IDestination(Interface):
"""The result of an adaption"""
def method():
"""Do something"""
class ISimpleContent(Interface):
pass
class ICallableSimpleContent(ISimpleContent):
pass
class IIndexSimpleContent(ISimpleContent):
pass
class IFancyContent(Interface):
pass
class IFieldSimpleContent(ISimpleContent):
title = TextLine( title = TextLine(
title=u"Title", title=_(u"Title"),
description=u"A short description of the event.", description=_(u"A short description of the event."),
default=u"", default=u"",
required=True required=True
) )
description = Text( description = Text(
title=u"Description", title=_(u"Description"),
description=u"A long description of the event.", description=_(u"A long description of the event."),
default=u"", default=u"",
required=False required=False
) )
somenumber = Int( somenumber = Int(
title=u"Some number", title=_(u"Some number"),
default=0, default=0,
required=False required=False
) )
somelist = List( somelist = List(
title=u"Some List", title=_(u"Some List"),
value_type=TextLine(title=u"Some item"), value_type=TextLine(title=_(u"Some item")),
default=[], default=[],
required=False required=False
) )
class FieldContent(SimpleItem):
"""A Viewable piece of content with fields"""
implements(IFieldContent)
meta_type = 'Five FieldContent'
def __init__(self, id, title):
self.id = id
self.title = title
InitializeClass(FieldContent)
def manage_addFieldContent(self, id, title, REQUEST=None):
"""Add the field content"""
id = self._setObject(id, FieldContent(id, title))
return ''
class IComplexSchemaContent(Interface): class IComplexSchemaContent(Interface):
fishtype = TextLine( fishtype = TextLine(
title=u"Fish type", title=u"Fish type",
description=u"The type of fish", description=u"The type of fish",
...@@ -93,6 +81,28 @@ class IComplexSchemaContent(Interface): ...@@ -93,6 +81,28 @@ class IComplexSchemaContent(Interface):
fish = Object( fish = Object(
title=u"Fish", title=u"Fish",
schema=IFieldSimpleContent, schema=IFieldContent,
description=u"The fishy object", description=u"The fishy object",
required=True) required=True)
class ComplexSchemaContent(SimpleItem):
implements(IComplexSchemaContent)
meta_type ="Five ComplexSchemaContent"
def __init__(self, id):
self.id = id
self.fish = FieldContent('fish', 'title')
self.fish.description = ""
self.fishtype = 'Lost fishy'
class ComplexSchemaView:
"""Needs a docstring"""
fish_widget = CustomWidgetFactory(ObjectWidget, FieldContent)
InitializeClass(ComplexSchemaContent)
def manage_addComplexSchemaContent(self, id, REQUEST=None):
"""Add the complex schema content"""
id = self._setObject(id, ComplexSchemaContent(id))
return ''
...@@ -13,54 +13,60 @@ ...@@ -13,54 +13,60 @@
############################################################################## ##############################################################################
"""Test forms """Test forms
$Id$ $Id: test_forms.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os, sys import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
import Products.Five.tests.fivetest # starts Zope, loads Five, etc.
import unittest
from Testing.ZopeTestCase import ZopeDocTestSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
def test_get_widgets_for_schema_fields(): def test_get_widgets_for_schema_fields():
""" """
>>> from zope.schema import Choice, TextLine Test widget lookup for schema fields
>>> salutation = Choice(title=u'Salutation',
... values=("Mr.", "Mrs.", "Captain", "Don"))
>>> contactname = TextLine(title=u'Name')
>>> from Products.Five.traversable import FakeRequest
>>> request = FakeRequest()
>>> salutation = salutation.bind(request)
>>> contactname = contactname.bind(request)
>>> from zope.app import zapi First, load the configuration files:
>>> from zope.app.form.interfaces import IInputWidget
>>> from zope.app.form.browser.textwidgets import TextWidget
>>> from zope.app.form.browser.itemswidgets import DropdownWidget
>>> view1 = zapi.getViewProviding(contactname, IInputWidget, request) >>> import Products.Five
>>> view1.__class__ == TextWidget >>> from Products.Five import zcml
True >>> zcml.load_config('configure.zcml', Products.Five)
>>> view2 = zapi.getViewProviding(salutation, IInputWidget, request) Now for some actual testing...
>>> view2.__class__ == DropdownWidget
True >>> from zope.schema import Choice, TextLine
""" >>> salutation = Choice(title=u'Salutation',
... values=("Mr.", "Mrs.", "Captain", "Don"))
>>> contactname = TextLine(title=u'Name')
>>> from Products.Five.traversable import FakeRequest
>>> request = FakeRequest()
>>> salutation = salutation.bind(request)
>>> contactname = contactname.bind(request)
>>> from zope.app import zapi
>>> from zope.app.form.interfaces import IInputWidget
>>> from zope.app.form.browser.textwidgets import TextWidget
>>> from zope.app.form.browser.itemswidgets import DropdownWidget
def setUpForms(self): >>> view1 = zapi.getViewProviding(contactname, IInputWidget, request)
uf = self.folder.acl_users >>> view1.__class__ == TextWidget
uf._doAddUser('viewer', 'secret', [], []) True
uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> view2 = zapi.getViewProviding(salutation, IInputWidget, request)
>>> view2.__class__ == DropdownWidget
True
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite(): def test_suite():
import unittest
from Testing.ZopeTestCase import ZopeDocTestSuite, FunctionalDocFileSuite
return unittest.TestSuite(( return unittest.TestSuite((
ZopeDocTestSuite(), ZopeDocTestSuite(),
FunctionalDocFileSuite( FunctionalDocFileSuite('forms.txt',
'forms.txt', package="Products.Five.form.tests",),
package="Products.Five.tests",
setUp=setUpForms),
)) ))
if __name__ == '__main__': if __name__ == '__main__':
......
##############################################################################
#
# Copyright (c) 2005 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.
#
##############################################################################
"""Mimick Zope3 i18n machinery for Zope 2
$Id: i18n.py 14400 2005-07-07 17:55:08Z philikon $
"""
from zope.interface import implements
from zope.i18n import interpolate
from zope.i18n.interfaces import ITranslationDomain, IUserPreferredLanguages
from zope.i18nmessageid import MessageID
from zope.app import zapi
from zope.publisher.browser import BrowserLanguages
class FiveTranslationService:
"""Translation service that delegates to ``zope.i18n`` machinery.
"""
# this is mostly a copy of zope.i18n.translate, with modifications
# regarding fallback and Zope 2 compatability
def translate(self, domain, msgid, mapping=None,
context=None, target_language=None, default=None):
if isinstance(msgid, MessageID):
domain = msgid.domain
default = msgid.default
mapping = msgid.mapping
util = zapi.queryUtility(ITranslationDomain, domain)
if util is None:
# fallback to translation service that was registered,
# DummyTranslationService the worst
ts = _fallback_translation_service
return ts.translate(domain, msgid, mapping=mapping, context=context,
target_language=target_language, default=default)
# in Zope3, context is adapted to IUserPreferredLanguages,
# which means context should be the request in this case.
if context is not None:
context = context.REQUEST
return util.translate(msgid, mapping=mapping, context=context,
target_language=target_language, default=default)
class LocalizerLanguages(object):
"""Languages adapter that chooses languages according to Localizer
settings."""
implements(IUserPreferredLanguages)
def __init__(self, context):
self.context = context
def getPreferredLanguages(self):
accept_language = self.context.AcceptLanguage
langs = []
for lang, node in accept_language.children.items():
langs.append((node.get_quality(), lang))
langs.extend([(n.get_quality(), l) for l, n
in node.children.items()])
langs.sort()
langs.reverse()
langs = [l for q, l in langs]
if '' in langs:
langs.remove('')
return langs
class PTSLanguages(object):
"""Languages adapter that chooses languages like
PlacelessTranslationService."""
implements(IUserPreferredLanguages)
def __init__(self, context):
self.context = context
def getPreferredLanguages(self):
from Products.PlacelessTranslationService.Negotiator import getLangPrefs
return getLangPrefs(self.context)
# Hook that will be used by Products.PageTemplates.GlobalTranslationService
_fallback_translation_service = None
<configure xmlns="http://namespaces.zope.org/zope"> <configure
xmlns="http://namespaces.zope.org/zope"
xmlns:i18n="http://namespaces.zope.org/i18n"
>
<utility
provides="zope.i18n.interfaces.INegotiator"
component="zope.i18n.negotiator.negotiator"
/>
<adapter
for="zope.publisher.interfaces.http.IHTTPRequest"
provides="zope.i18n.interfaces.IUserPreferredLanguages"
factory="zope.publisher.browser.BrowserLanguages"
/>
<adapter <adapter
factory="zope.publisher.http.HTTPCharsets"
for="zope.publisher.interfaces.http.IHTTPRequest" for="zope.publisher.interfaces.http.IHTTPRequest"
provides="zope.i18n.interfaces.IUserPreferredCharsets" provides="zope.i18n.interfaces.IUserPreferredCharsets"
factory="zope.publisher.http.HTTPCharsets"
/> />
<configure package="zope.app">
<i18n:registerTranslations directory="locales"/>
</configure>
</configure> </configure>
############################################################################## ##############################################################################
# #
# Copyright (c) 2000-2005 Zope Corporation and Contributors. # Copyright (c) 2004, 2005 Zope Corporation and Contributors.
# All Rights Reserved.
# #
# This software is subject to the provisions of the Zope Public License, # 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. # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
...@@ -12,30 +13,10 @@ ...@@ -12,30 +13,10 @@
############################################################################## ##############################################################################
"""Five interfaces """Five interfaces
$Id: interfaces.py 12915 2005-05-31 10:23:19Z philikon $ $Id: interfaces.py 14613 2005-07-13 10:39:05Z philikon $
""" """
from zope.interface import Interface, Attribute from zope.interface import Interface
from zope.interface.interfaces import IInterface from zope.interface.interfaces import IInterface
from zope.schema import Bool, BytesLine, Tuple
try:
from persistent.interfaces import IPersistent
except ImportError:
class IPersistent(Interface):
"""Persistent object"""
class IPersistentExtra(Interface):
def bobobase_modification_time():
""" """
def locked_in_version():
"""Was the object modified in any version?"""
def modified_in_version():
"""Was the object modified in this version?"""
class IBrowserDefault(Interface): class IBrowserDefault(Interface):
"""Provide a hook for deciding about the default view for an object""" """Provide a hook for deciding about the default view for an object"""
...@@ -46,7 +27,6 @@ class IBrowserDefault(Interface): ...@@ -46,7 +27,6 @@ class IBrowserDefault(Interface):
find the method to be published. find the method to be published.
""" """
class IMenuItemType(IInterface): class IMenuItemType(IInterface):
"""Menu item type """Menu item type
...@@ -56,1237 +36,20 @@ class IMenuItemType(IInterface): ...@@ -56,1237 +36,20 @@ class IMenuItemType(IInterface):
# #
# Zope 2.7 core interfaces # BBB: Zope core interfaces
#
# The interfaces here are only provided for backwards compatibility and will
# be removed in Five 1.2. Please import interfaces from the corresponding Zope
# package instead.
# #
# based on Acquisition.*AcquisitionWrapper from persistent.interfaces import IPersistent
class IAcquisitionWrapper(Interface): from AccessControl.interfaces import *
""" Wrapper object for acquisition. from Acquisition.interfaces import *
""" from App.interfaces import *
from OFS.interfaces import *
def aq_acquire(name, filter=None, extra=None, explicit=True, default=0, from webdav.interfaces import *
containment=0):
"""Get an attribute, acquiring it if necessary"""
def aq_inContextOf(obj, inner=1):
"""Test whether the object is currently in the context of the
argument"""
# based on Acquisition.*plicit
class IAcquisition(Interface):
""" Acquire attributes from containers.
"""
def __of__(context):
"""Return the object in a context"""
## XXX: these are wrapper attributes and/or module functions
##
## def aq_acquire(name, filter=None, extra=None, explicit=None):
## """Get an attribute, acquiring it if necessary"""
##
## def aq_get(name, default=None):
## """Get an attribute, acquiring it if necessary."""
##
## # those are computed attributes, aren't they?
##
## def aq_base():
## """Get the object unwrapped"""
##
## def aq_parent():
## """Get the parent of an object"""
##
## def aq_self():
## """Get the object with the outermost wrapper removed"""
##
## def aq_inner():
## """Get the object with alll but the innermost wrapper removed"""
##
## def aq_chain(containment=0):
## """Get a list of objects in the acquisition environment"""
# based on OFS.SimpleItem.Item and Management.Tabs
class IManageable(Interface):
"""Something that is manageable in the ZMI"""
def manage(URL1):
"""Show management screen"""
def manage_afterAdd(item, container):
"""Gets called after being added to a container"""
def manage_beforeDelete(item, container):
"""Gets called before being deleted"""
def manage_afterClone(item):
"""Gets called after being cloned"""
def manage_editedDialog(REQUEST, **args):
"""Show an 'edited' dialog"""
def filtered_manage_options(REQUEST=None):
""" """
def manage_workspace(REQUEST):
"""Dispatch to first interface in manage_options"""
def tabs_path_default(REQUEST):
""" """
def tabs_path_info(script, path):
""" """
def class_manage_path():
""" """
manage_options = Tuple(
title=u"Manage options",
)
manage_tabs = Attribute("""Management tabs""")
# based on OFS.FTPInterface.FTPInterface
class IFTPAccess(Interface):
"""Provide support for FTP access"""
def manage_FTPstat(REQUEST):
"""Returns a stat-like tuple. (marshalled to a string) Used by
FTP for directory listings, and MDTM and SIZE"""
def manage_FTPlist(REQUEST):
"""Returns a directory listing consisting of a tuple of
(id,stat) tuples, marshaled to a string. Note, the listing it
should include '..' if there is a Folder above the current
one.
In the case of non-foldoid objects it should return a single
tuple (id,stat) representing itself."""
# copied from webdav.WriteLockInterface.WriteLockInterface
class IWriteLock(Interface):
"""This represents the basic protocol needed to support the write lock
machinery.
It must be able to answer the questions:
o Is the object locked?
o Is the lock owned by the current user?
o What lock tokens are associated with the current object?
o What is their state (how long until they're supposed to time out?,
what is their depth? what type are they?
And it must be able to do the following:
o Grant a write lock on the object to a specified user.
- *If lock depth is infinite, this must also grant locks on **all**
subobjects, or fail altogether*
o Revoke a lock on the object.
- *If lock depth is infinite, this must also revoke locks on all
subobjects*
**All methods in the WriteLock interface that deal with checking valid
locks MUST check the timeout values on the lockitem (ie, by calling
'lockitem.isValid()'), and DELETE the lock if it is no longer valid**
"""
def wl_lockItems(killinvalids=0):
""" Returns (key, value) pairs of locktoken, lock.
if 'killinvalids' is true, invalid locks (locks whose timeout
has been exceeded) will be deleted"""
def wl_lockValues(killinvalids=0):
""" Returns a sequence of locks. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_lockTokens(killinvalids=0):
""" Returns a sequence of lock tokens. if 'killinvalids' is true,
invalid locks will be deleted"""
def wl_hasLock(token, killinvalids=0):
""" Returns true if the lock identified by the token is attached
to the object. """
def wl_isLocked():
""" Returns true if 'self' is locked at all. If invalid locks
still exist, they should be deleted."""
def wl_setLock(locktoken, lock):
""" Store the LockItem, 'lock'. The locktoken will be used to fetch
and delete the lock. If the lock exists, this MUST
overwrite it if all of the values except for the 'timeout' on the
old and new lock are the same. """
def wl_getLock(locktoken):
""" Returns the locktoken identified by the locktokenuri """
def wl_delLock(locktoken):
""" Deletes the locktoken identified by the locktokenuri """
def wl_clearLocks():
""" Deletes ALL DAV locks on the object - should only be called
by lock management machinery. """
# based on webdav.Resource.Resource
class IDAVResource(IWriteLock):
"""Provide basic WebDAV support for non-collection objects."""
__dav_resource__ = Bool(
title=u"Is DAV resource"
)
__http_methods__ = Tuple(
title=u"HTTP methods",
description=u"Sequence of valid HTTP methods"
)
def dav__init(request, response):
"""
Init expected HTTP 1.1 / WebDAV headers which are not
currently set by the base response object automagically.
Note we set an borg-specific header for ie5 :( Also, we sniff
for a ZServer response object, because we don't want to write
duplicate headers (since ZS writes Date and Connection
itself)."""
def dav__validate(object, methodname, REQUEST):
""" """
def dav__simpleifhandler(request, response, method='PUT',
col=0, url=None, refresh=0):
""" """
def HEAD(REQUEST, RESPONSE):
"""Retrieve resource information without a response body."""
def PUT(REQUEST, RESPONSE):
"""Replace the GET response entity of an existing resource.
Because this is often object-dependent, objects which handle
PUT should override the default PUT implementation with an
object-specific implementation. By default, PUT requests
fail with a 405 (Method Not Allowed)."""
def OPTIONS(REQUEST, RESPONSE):
"""Retrieve communication options."""
def TRACE(REQUEST, RESPONSE):
"""Return the HTTP message received back to the client as the
entity-body of a 200 (OK) response. This will often usually
be intercepted by the web server in use. If not, the TRACE
request will fail with a 405 (Method Not Allowed), since it
is not often possible to reproduce the HTTP request verbatim
from within the Zope environment."""
def DELETE(REQUEST, RESPONSE):
"""Delete a resource. For non-collection resources, DELETE may
return either 200 or 204 (No Content) to indicate success."""
def PROPFIND(REQUEST, RESPONSE):
"""Retrieve properties defined on the resource."""
def PROPPATCH(REQUEST, RESPONSE):
"""Set and/or remove properties defined on the resource."""
def MKCOL(REQUEST, RESPONSE):
"""Create a new collection resource. If called on an existing
resource, MKCOL must fail with 405 (Method Not Allowed)."""
def COPY(REQUEST, RESPONSE):
"""Create a duplicate of the source resource whose state
and behavior match that of the source resource as closely
as possible. Though we may later try to make a copy appear
seamless across namespaces (e.g. from Zope to Apache), COPY
is currently only supported within the Zope namespace."""
def MOVE(REQUEST, RESPONSE):
"""Move a resource to a new location. Though we may later try to
make a move appear seamless across namespaces (e.g. from Zope
to Apache), MOVE is currently only supported within the Zope
namespace."""
def LOCK(REQUEST, RESPONSE):
"""Lock a resource"""
def UNLOCK(REQUEST, RESPONSE):
"""Remove an existing lock on a resource."""
def manage_DAVget():
"""Gets the document source"""
def listDAVObjects():
""" """
# based on OFS.CopySupport.CopySource
class ICopySource(Interface):
"""Interface for objects which allow themselves to be copied."""
def _canCopy(op=0):
"""Called to make sure this object is copyable. The op var
is 0 for a copy, 1 for a move."""
def _notifyOfCopyTo(container, op=0):
"""Overide this to be pickly about where you go! If you dont
want to go there, raise an exception. The op variable is
0 for a copy, 1 for a move."""
def _getCopy(container):
"""
Commit a subtransaction to:
1) Make sure the data about to be exported is current
2) Ensure self._p_jar and container._p_jar are set even if
either one is a new object
"""
def _postCopy(container, op=0):
"""Called after the copy is finished to accomodate special cases.
The op var is 0 for a copy, 1 for a move."""
def _setId(id):
"""Called to set the new id of a copied object."""
def cb_isCopyable():
"""Is object copyable? Returns 0 or 1"""
def cb_isMoveable():
"""Is object moveable? Returns 0 or 1"""
def cb_userHasCopyOrMovePermission():
""" """
# based on OFS.Traversable.Traversable
class ITraversable(Interface):
def absolute_url(relative=0):
"""Return the absolute URL of the object.
This a canonical URL based on the object's physical
containment path. It is affected by the virtual host
configuration, if any, and can be used by external
agents, such as a browser, to address the object.
If the relative argument is provided, with a true value, then
the value of virtual_url_path() is returned.
Some Products incorrectly use '/'+absolute_url(1) as an
absolute-path reference. This breaks in certain virtual
hosting situations, and should be changed to use
absolute_url_path() instead.
"""
def absolute_url_path():
"""Return the path portion of the absolute URL of the object.
This includes the leading slash, and can be used as an
'absolute-path reference' as defined in RFC 2396.
"""
def virtual_url_path():
"""Return a URL for the object, relative to the site root.
If a virtual host is configured, the URL is a path relative to
the virtual host's root object. Otherwise, it is the physical
path. In either case, the URL does not begin with a slash.
"""
def getPhysicalPath():
'''Returns a path (an immutable sequence of strings)
that can be used to access this object again
later, for example in a copy/paste operation. getPhysicalRoot()
and getPhysicalPath() are designed to operate together.
'''
def unrestrictedTraverse(path, default=None, restricted=0):
"""Lookup an object by path,
path -- The path to the object. May be a sequence of strings or a slash
separated string. If the path begins with an empty path element
(i.e., an empty string or a slash) then the lookup is performed
from the application root. Otherwise, the lookup is relative to
self. Two dots (..) as a path element indicates an upward traversal
to the acquisition parent.
default -- If provided, this is the value returned if the path cannot
be traversed for any reason (i.e., no object exists at that path or
the object is inaccessible).
restricted -- If false (default) then no security checking is performed.
If true, then all of the objects along the path are validated with
the security machinery. Usually invoked using restrictedTraverse().
"""
def restrictedTraverse(path, default=None):
"""Trusted code traversal code, always enforces security"""
# based on AccessControl.Owned.Owned
class IOwned(Interface):
manage_owner = Attribute("""Manage owner view""")
def owner_info():
"""Get ownership info for display"""
def getOwner(info=0):
"""Get the owner
If a true argument is provided, then only the owner path and id are
returned. Otherwise, the owner object is returned.
"""
def getOwnerTuple():
"""Return a tuple, (userdb_path, user_id) for the owner.
o Ownership can be acquired, but only from the containment path.
o If unowned, return None.
"""
def getWrappedOwner():
"""Get the owner, modestly wrapped in the user folder.
o If the object is not owned, return None.
o If the owner's user database doesn't exist, return Nobody.
o If the owner ID does not exist in the user database, return Nobody.
"""
def changeOwnership(user, recursive=0):
"""Change the ownership to the given user. If 'recursive' is
true then also take ownership of all sub-objects, otherwise
sub-objects retain their ownership information."""
def userCanTakeOwnership():
""" """
def manage_takeOwnership(REQUEST, RESPONSE, recursive=0):
"""Take ownership (responsibility) for an object. If 'recursive'
is true, then also take ownership of all sub-objects.
"""
def manage_changeOwnershipType(explicit=1, RESPONSE=None, REQUEST=None):
"""Change the type (implicit or explicit) of ownership.
"""
def _deleteOwnershipAfterAdd():
""" """
def manage_fixupOwnershipAfterAdd():
""" """
# based on App.Undo.UndoSupport
class IUndoSupport(Interface):
manage_UndoForm = Attribute("""Manage Undo form""")
def get_request_var_or_attr(name, default):
""" """
def undoable_transactions(first_transaction=None,
last_transaction=None,
PrincipiaUndoBatchSize=None):
""" """
def manage_undo_transactions(transaction_info=(), REQUEST=None):
""" """
# based on many classes
class IZopeObject(Interface):
isPrincipiaFolderish = Bool(
title=u"Is a folderish object",
description=u"Should be false for simple items",
)
meta_type = BytesLine(
title=u"Meta type",
description=u"The object's Zope2 meta type",
)
# based on OFS.SimpleItem.Item
class IItem(IZopeObject, IManageable, IFTPAccess, IDAVResource,
ICopySource, ITraversable, IOwned, IUndoSupport):
__name__ = BytesLine(
title=u"Name"
)
title = BytesLine(
title=u"Title"
)
icon = BytesLine(
title=u"Icon",
description=u"Name of icon, relative to SOFTWARE_URL",
)
def getId():
"""Return the id of the object as a string.
This method should be used in preference to accessing an id
attribute of an object directly. The getId method is public.
"""
def title_or_id():
"""Returns the title if it is not blank and the id otherwise."""
def title_and_id():
"""Returns the title if it is not blank and the id otherwise. If the
title is not blank, then the id is included in parens."""
def raise_standardErrorMessage(client=None, REQUEST={},
error_type=None, error_value=None, tb=None,
error_tb=None, error_message='',
tagSearch=None, error_log_url=''):
"""Raise standard error message"""
# based on OFS.SimpleItem.Item_w__name__
class IItemWithName(IItem):
"""Item with name"""
def _setId(id):
"""Set the id"""
def getPhysicalPath():
"""Returns a path (an immutable sequence of strings) that can be used
to access this object again later, for example in a copy/paste
operation."""
# based on AccessControl.PermissionMapping.RoleManager
class IPermissionMapping(Interface):
def manage_getPermissionMapping():
"""Return the permission mapping for the object
This is a list of dictionaries with:
permission_name -- The name of the native object permission
class_permission -- The class permission the permission is
mapped to.
"""
def manage_setPermissionMapping(permission_names=[],
class_permissions=[], REQUEST=None):
"""Change the permission mapping"""
# based on AccessControl.Role.RoleManager
class IRoleManager(IPermissionMapping):
"""An object that has configurable permissions"""
permissionMappingPossibleValues = Attribute("""Acquired attribute""")
def ac_inherited_permissions(all=0):
"""Get all permissions not defined in ourself that are inherited This
will be a sequence of tuples with a name as the first item and
an empty tuple as the second."""
def permission_settings(permission=None):
"""Return user-role permission settings. If 'permission' is passed to
the method then only the settings for 'permission' returned."""
manage_roleForm = Attribute(""" """)
def manage_role(role_to_manage, permissions=[], REQUEST=None):
"""Change the permissions given to the given role"""
manage_acquiredForm = Attribute(""" """)
def manage_acquiredPermissions(permissions=[], REQUEST=None):
"""Change the permissions that acquire"""
manage_permissionForm = Attribute(""" """)
def manage_permission(permission_to_manage, roles=[], acquire=0,
REQUEST=None):
"""Change the settings for the given permission
If optional arg acquire is true, then the roles for the permission
are acquired, in addition to the ones specified, otherwise the
permissions are restricted to only the designated roles."""
def manage_access(REQUEST, **kw):
"""Return an interface for making permissions settings"""
def manage_changePermissions(REQUEST):
"""Change all permissions settings, called by management screen"""
def permissionsOfRole(role):
"""used by management screen"""
def rolesOfPermission(permission):
"""used by management screen"""
def acquiredRolesAreUsedBy(permission):
"""used by management screen"""
# Local roles support
# -------------------
#
# Local roles allow a user to be given extra roles in the context
# of a particular object (and its children). When a user is given
# extra roles in a particular object, an entry for that user is made
# in the __ac_local_roles__ dict containing the extra roles.
__ac_local_roles__ = Attribute(""" """)
manage_listLocalRoles = Attribute(""" """)
manage_editLocalRoles = Attribute(""" """)
def has_local_roles():
""" """
def get_local_roles():
""" """
def users_with_local_role(role):
""" """
def get_valid_userids():
""" """
def get_local_roles_for_userid(userid):
""" """
def manage_addLocalRoles(userid, roles, REQUEST=None):
"""Set local roles for a user."""
def manage_setLocalRoles(userid, roles, REQUEST=None):
"""Set local roles for a user."""
def manage_delLocalRoles(userids, REQUEST=None):
"""Remove all local roles for a user."""
#------------------------------------------------------------
def access_debug_info():
"""Return debug info"""
def valid_roles():
"""Return list of valid roles"""
def validate_roles(roles):
"""Return true if all given roles are valid"""
def userdefined_roles():
"""Return list of user-defined roles"""
def manage_defined_roles(submit=None, REQUEST=None):
"""Called by management screen."""
def _addRole(role, REQUEST=None):
""" """
def _delRoles(roles, REQUEST=None):
""" """
def _has_user_defined_role(role):
""" """
def manage_editRoles(REQUEST, acl_type='A', acl_roles=[]):
""" """
def _setRoles(acl_type, acl_roles):
""" """
def possible_permissions():
""" """
# based on OFS.SimpleItem.SimpleItem
class ISimpleItem(IItem, IPersistent, IAcquisition, IRoleManager):
"""Not-so-simple item"""
# based on OFS.CopySupport.CopyContainer
class ICopyContainer(Interface):
"""Interface for containerish objects which allow cut/copy/paste"""
# The following three methods should be overridden to store sub-objects
# as non-attributes.
def _setOb(id, object):
""" """
def _delOb(id):
""" """
def _getOb(id, default=None):
""" """
def manage_CopyContainerFirstItem(REQUEST):
""" """
def manage_CopyContainerAllItems(REQUEST):
""" """
def manage_cutObjects(ids=None, REQUEST=None):
"""Put a reference to the objects named in ids in the clip board"""
def manage_copyObjects(ids=None, REQUEST=None, RESPONSE=None):
"""Put a reference to the objects named in ids in the clip board"""
def _get_id(id):
"""Allow containers to override the generation of object copy id by
attempting to call its _get_id method, if it exists."""
def manage_pasteObjects(cb_copy_data=None, REQUEST=None):
"""Paste previously copied objects into the current object. If
calling manage_pasteObjects from python code, pass the result
of a previous call to manage_cutObjects or manage_copyObjects
as the first argument."""
manage_renameForm = Attribute("""Rename management view""")
def manage_renameObjects(ids=[], new_ids=[], REQUEST=None):
"""Rename several sub-objects"""
def manage_renameObject(id, new_id, REQUEST=None):
"""Rename a particular sub-object"""
def manage_clone(ob, id, REQUEST=None):
"""Clone an object, creating a new object with the given id."""
def cb_dataValid():
"""Return true if clipboard data seems valid."""
def cb_dataItems():
"""List of objects in the clip board"""
def _verifyObjectPaste(object, validate_src=1):
"""Verify whether the current user is allowed to paste the passed
object into self. This is determined by checking to see if the
user could create a new object of the same meta_type of the
object passed in and checking that the user actually is
allowed to access the passed in object in its existing
context.
Passing a false value for the validate_src argument will skip
checking the passed in object in its existing context. This is
mainly useful for situations where the passed in object has no
existing context, such as checking an object during an import
(the object will not yet have been connected to the
acquisition hierarchy)."""
# based on App.Management.Navigation
class INavigation(Interface):
"""Basic navigation UI support"""
manage = Attribute(""" """)
manage_menu = Attribute(""" """)
manage_top_frame = Attribute(""" """)
manage_page_header = Attribute(""" """)
manage_page_footer = Attribute(""" """)
manage_form_title = Attribute("""Add Form""")
zope_quick_start = Attribute(""" """)
manage_copyright = Attribute(""" """)
manage_zmi_prefs = Attribute(""" """)
def manage_zmi_logout(REQUEST, RESPONSE):
"""Logout current user"""
INavigation.setTaggedValue('manage_page_style.css', Attribute(""" """))
# based on webdav.Collection.Collection
class IDAVCollection(IDAVResource):
"""The Collection class provides basic WebDAV support for collection
objects. It provides default implementations for all supported
WebDAV HTTP methods. The behaviors of some WebDAV HTTP methods for
collections are slightly different than those for non-collection
resources."""
__dav_collection__ = Bool(
title=u"Is a DAV collection",
description=u"Should be true",
)
def dav__init(request, response):
""" """
def HEAD(REQUEST, RESPONSE):
"""Retrieve resource information without a response body."""
def PUT(REQUEST, RESPONSE):
"""The PUT method has no inherent meaning for collection
resources, though collections are not specifically forbidden
to handle PUT requests. The default response to a PUT request
for collections is 405 (Method Not Allowed)."""
def DELETE(REQUEST, RESPONSE):
"""Delete a collection resource. For collection resources, DELETE
may return either 200 (OK) or 204 (No Content) to indicate total
success, or may return 207 (Multistatus) to indicate partial
success. Note that in Zope a DELETE currently never returns 207."""
def listDAVObjects():
""" """
# based on OFS.ObjectManager.ObjectManager
class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable,
IAcquisition, IPersistent, IDAVCollection, ITraversable):
"""Generic object manager
This interface provides core behavior for collections of
heterogeneous objects."""
meta_types = Tuple(
title=u"Meta types",
description=u"Sub-object types that are specific to this object",
)
isAnObjectManager = Bool(
title=u"Is an object manager",
)
manage_main = Attribute(""" """)
manage_index_main = Attribute(""" """)
manage_addProduct = Attribute(""" """)
manage_importExportForm = Attribute(""" """)
def all_meta_types(interfaces=None):
""" """
def filtered_meta_types(user=None):
"""Return a list of the types for which the user has adequate
permission to add that type of object."""
def _setOb(id, object):
""" """
def _delOb(id):
""" """
def _getOb(id, default=None):
""" """
def _setObject(id, object, roles=None, user=None, set_owner=1):
""" """
def _delObject(id, dp=1):
""" """
def objectIds(spec=None):
"""Returns a list of subobject ids of the current object. If 'spec'
is specified, returns objects whose meta_type matches 'spec'.
"""
def objectValues(spec=None):
"""Returns a list of actual subobjects of the current object. If
'spec' is specified, returns only objects whose meta_type
match 'spec'."""
def objectItems(spec=None):
"""Returns a list of (id, subobject) tuples of the current object. If
'spec' is specified, returns only objects whose meta_type
match 'spec'"""
def objectMap():
"""Return a tuple of mappings containing subobject meta-data"""
def superValues(t):
"""Return all of the objects of a given type located in this object
and containing objects."""
def manage_delObjects(ids=[], REQUEST=None):
"""Delete a subordinate object
The objects specified in 'ids' get deleted.
"""
def tpValues():
"""Return a list of subobjects, used by tree tag."""
def manage_exportObject(id='', download=None, toxml=None,
RESPONSE=None,REQUEST=None):
"""Exports an object to a file and returns that file."""
def manage_importObject(file, REQUEST=None, set_owner=1):
"""Import an object from a file"""
def _importObjectFromFile(filepath, verify=1, set_owner=1):
""" """
def __getitem__(key):
""" """
# based on OFS.PropertyManager.PropertyManager
class IPropertyManager(Interface):
"""The PropertyManager mixin class provides an object with
transparent property management. An object which wants to
have properties should inherit from PropertyManager.
An object may specify that it has one or more predefined
properties, by specifying an _properties structure in its
class::
_properties=({'id':'title', 'type': 'string', 'mode': 'w'},
{'id':'color', 'type': 'string', 'mode': 'w'},
)
The _properties structure is a sequence of dictionaries, where
each dictionary represents a predefined property. Note that if a
predefined property is defined in the _properties structure, you
must provide an attribute with that name in your class or instance
that contains the default value of the predefined property.
Each entry in the _properties structure must have at least an 'id'
and a 'type' key. The 'id' key contains the name of the property,
and the 'type' key contains a string representing the object's type.
The 'type' string must be one of the values: 'float', 'int', 'long',
'string', 'lines', 'text', 'date', 'tokens', 'selection', or
'multiple section'.
For 'selection' and 'multiple selection' properties, there is an
addition item in the property dictionay, 'select_variable' which
provides the name of a property or method which returns a list of
strings from which the selection(s) can be chosen.
Each entry in the _properties structure may *optionally* provide a
'mode' key, which specifies the mutability of the property. The 'mode'
string, if present, must contain 0 or more characters from the set
'w','d'.
A 'w' present in the mode string indicates that the value of the
property may be changed by the user. A 'd' indicates that the user
can delete the property. An empty mode string indicates that the
property and its value may be shown in property listings, but that
it is read-only and may not be deleted.
Entries in the _properties structure which do not have a 'mode' key
are assumed to have the mode 'wd' (writeable and deleteable).
To fully support property management, including the system-provided
tabs and user interfaces for working with properties, an object which
inherits from PropertyManager should include the following entry in
its manage_options structure::
{'label':'Properties', 'action':'manage_propertiesForm',}
to ensure that a 'Properties' tab is displayed in its management
interface. Objects that inherit from PropertyManager should also
include the following entry in its __ac_permissions__ structure::
('Manage properties', ('manage_addProperty',
'manage_editProperties',
'manage_delProperties',
'manage_changeProperties',)),
"""
manage_propertiesForm = Attribute(""" """)
manage_propertyTypeForm = Attribute(""" """)
title = BytesLine(
title=u"Title"
)
_properties = Tuple(
title=u"Properties",
)
propertysheets = Attribute(""" """)
def valid_property_id(id):
""" """
def hasProperty(id):
"""Return true if object has a property 'id'"""
def getProperty(id, d=None):
"""Get the property 'id', returning the optional second
argument or None if no such property is found."""
def getPropertyType(id):
"""Get the type of property 'id', returning None if no
such property exists"""
def _setProperty(id, value, type='string'):
""" """
def _updateProperty(id, value):
"""Update the value of an existing property. If value is a string, an
attempt will be made to convert the value to the type of the
existing property."""
def _delProperty(id):
""" """
def propertyIds():
"""Return a list of property ids """
def propertyValues():
"""Return a list of actual property objects """
def propertyItems():
"""Return a list of (id,property) tuples """
def _propertyMap():
"""Return a tuple of mappings, giving meta-data for properties """
def propertyMap():
"""Return a tuple of mappings, giving meta-data for properties.
Return copies of the real definitions for security.
"""
def propertyLabel(id):
"""Return a label for the given property id
"""
def propdict():
""" """
# Web interface
def manage_addProperty(id, value, type, REQUEST=None):
"""Add a new property via the web. Sets a new property with
the given id, type, and value."""
def manage_editProperties(REQUEST):
"""Edit object properties via the web.
The purpose of this method is to change all property values,
even those not listed in REQUEST; otherwise checkboxes that
get turned off will be ignored. Use manage_changeProperties()
instead for most situations.
"""
def manage_changeProperties(REQUEST=None, **kw):
"""Change existing object properties.
Change object properties by passing either a mapping object
of name:value pairs {'foo':6} or passing name=value parameters
"""
def manage_changePropertyTypes(old_ids, props, REQUEST=None):
"""Replace one set of properties with another
Delete all properties that have ids in old_ids, then add a
property for each item in props. Each item has a new_id,
new_value, and new_type. The type of new_value should match
new_type.
"""
def manage_delProperties(ids=None, REQUEST=None):
"""Delete one or more properties specified by 'ids'."""
# based on OFS.FindSupport.FindSupport
class IFindSupport(Interface):
"""Find support for Zope Folders"""
manage_findFrame = Attribute(""" """)
manage_findForm = Attribute(""" """)
manage_findAdv = Attribute(""" """)
manage_findResult = Attribute(""" """)
def ZopeFind(obj, obj_ids=None, obj_metatypes=None,
obj_searchterm=None, obj_expr=None,
obj_mtime=None, obj_mspec=None,
obj_permission=None, obj_roles=None,
search_sub=0,
REQUEST=None, result=None, pre=''):
"""Zope Find interface"""
PrincipiaFind = ZopeFind
def ZopeFindAndApply(obj, obj_ids=None, obj_metatypes=None,
obj_searchterm=None, obj_expr=None,
obj_mtime=None, obj_mspec=None,
obj_permission=None, obj_roles=None,
search_sub=0,
REQUEST=None, result=None, pre='',
apply_func=None, apply_path=''):
"""Zope Find interface and apply"""
# based on OFS.Folder.Folder
class IFolder(IObjectManager, IPropertyManager, IRoleManager,
IDAVCollection, IItem, IFindSupport):
"""Folders are basic container objects that provide a standard
interface for object management. Folder objects also implement a
management interface and can have arbitrary properties."""
# copied from OFS.IOrderSupport.IOrderedContainer
class IOrderedContainer(Interface):
""" Ordered Container interface.
This interface provides a common mechanism for maintaining ordered
collections.
"""
def moveObjectsByDelta(ids, delta, subset_ids=None):
""" Move specified sub-objects by delta.
If delta is higher than the possible maximum, objects will be moved to
the bottom. If delta is lower than the possible minimum, objects will
be moved to the top.
If subset_ids is not None, delta will be interpreted relative to the
subset specified by a sequence of ids. The position of objects that
are not part of this subset will not be changed.
The order of the objects specified by ids will always be preserved. So
if you don't want to change their original order, make sure the order
of ids corresponds to their original order.
If an object with id doesn't exist an error will be raised.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
def moveObjectsUp(ids, delta=1, subset_ids=None):
""" Move specified sub-objects up by delta in container.
If no delta is specified, delta is 1. See moveObjectsByDelta for more
details.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
def moveObjectsDown(ids, delta=1, subset_ids=None):
""" Move specified sub-objects down by delta in container.
If no delta is specified, delta is 1. See moveObjectsByDelta for more
details.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
def moveObjectsToTop(ids, subset_ids=None):
""" Move specified sub-objects to top of container.
See moveObjectsByDelta for more details.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
def moveObjectsToBottom(ids, subset_ids=None):
""" Move specified sub-objects to bottom of container.
See moveObjectsByDelta for more details.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
def orderObjects(key, reverse=None):
""" Order sub-objects by key and direction.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
def getObjectPosition(id):
""" Get the position of an object by its id.
Permission -- Access contents information
Returns -- Position
"""
def moveObjectToPosition(id, position):
""" Move specified object to absolute position.
Permission -- Manage properties
Returns -- Number of moved sub-objects
"""
# based on OFS.OrderedFolder.OrderedFolder
class IOrderedFolder(IOrderedContainer, IFolder):
"""Ordered folder"""
# based on OFS.Application.Application
class IApplication(IFolder, IFindSupport):
"""Top-level system object"""
isTopLevelPrincipiaApplicationObject = Bool(
title=u"Is top level Principa application object",
)
HelpSys = Attribute("Help system")
p_ = Attribute(""" """)
misc_ = Attribute("Misc.")
def PrincipiaRedirect(destination,URL1):
"""Utility function to allow user-controlled redirects"""
Redirect = ZopeRedirect = PrincipiaRedirect
def __bobo_traverse__(REQUEST, name=None):
"""Bobo traverse"""
def PrincipiaTime(*args):
"""Utility function to return current date/time"""
ZopeTime = PrincipiaTime
def ZopeAttributionButton():
"""Returns an HTML fragment that displays the 'powered by zope'
button along with a link to the Zope site."""
test_url = ZopeAttributionButton
def absolute_url(relative=0):
'''The absolute URL of the root object is BASE1 or "/".'''
def absolute_url_path():
'''The absolute URL path of the root object is BASEPATH1 or "/".'''
def virtual_url_path():
'''The virtual URL path of the root object is empty.'''
def getPhysicalPath():
'''Returns a path that can be used to access this object again
later, for example in a copy/paste operation. Designed to
be used with getPhysicalRoot().
'''
def getPhysicalRoot():
"""Returns self"""
def fixupZClassDependencies(rebuild=0):
""" """
def checkGlobalRegistry(): # BBB: for old names used in Five 1.0
"""Check the global (zclass) registry for problems, which can IAcquisition = IAcquirer
be caused by things like disk-based products being deleted. IPermissionMapping = IPermissionMappingSupport
Return true if a problem is found"""
...@@ -6,135 +6,135 @@ ...@@ -6,135 +6,135 @@
<implements <implements
class="Acquisition.ImplicitAcquisitionWrapper" class="Acquisition.ImplicitAcquisitionWrapper"
interface=".interfaces.IAcquisitionWrapper" interface="Acquisition.interfaces.IAcquisitionWrapper"
/> />
<implements <implements
class="Acquisition.ExplicitAcquisitionWrapper" class="Acquisition.ExplicitAcquisitionWrapper"
interface=".interfaces.IAcquisitionWrapper" interface="Acquisition.interfaces.IAcquisitionWrapper"
/> />
<implements <implements
class="Acquisition.Implicit" class="Acquisition.Implicit"
interface=".interfaces.IAcquisition" interface="Acquisition.interfaces.IAcquirer"
/> />
<implements <implements
class="Acquisition.Explicit" class="Acquisition.Explicit"
interface=".interfaces.IAcquisition" interface="Acquisition.interfaces.IAcquirer"
/> />
<!-- DAV --> <!-- DAV -->
<implements <implements
class="webdav.Lockable.LockableItem" class="webdav.Lockable.LockableItem"
interface=".interfaces.IWriteLock" interface="webdav.interfaces.IWriteLock"
/> />
<implements <implements
class="webdav.Resource.Resource" class="webdav.Resource.Resource"
interface=".interfaces.IDAVResource" interface="webdav.interfaces.IDAVResource"
/> />
<implements <implements
class="webdav.Collection.Collection" class="webdav.Collection.Collection"
interface=".interfaces.IDAVCollection" interface="webdav.interfaces.IDAVCollection"
/> />
<!-- OFS --> <!-- OFS -->
<implements <implements
class="OFS.CopySupport.CopySource" class="OFS.CopySupport.CopySource"
interface=".interfaces.ICopySource" interface="OFS.interfaces.ICopySource"
/> />
<implements <implements
class="OFS.CopySupport.CopyContainer" class="OFS.CopySupport.CopyContainer"
interface=".interfaces.ICopyContainer" interface="OFS.interfaces.ICopyContainer"
/> />
<implements <implements
class="OFS.Traversable.Traversable" class="OFS.Traversable.Traversable"
interface=".interfaces.ITraversable" interface="OFS.interfaces.ITraversable"
/> />
<implements <implements
class="OFS.SimpleItem.Item" class="OFS.SimpleItem.Item"
interface=".interfaces.IItem" interface="OFS.interfaces.IItem"
/> />
<implements <implements
class="OFS.SimpleItem.Item_w__name__" class="OFS.SimpleItem.Item_w__name__"
interface=".interfaces.IItemWithName" interface="OFS.interfaces.IItemWithName"
/> />
<implements <implements
class="OFS.SimpleItem.SimpleItem" class="OFS.SimpleItem.SimpleItem"
interface=".interfaces.ISimpleItem" interface="OFS.interfaces.ISimpleItem"
/> />
<implements <implements
class="OFS.ObjectManager.ObjectManager" class="OFS.ObjectManager.ObjectManager"
interface=".interfaces.IObjectManager" interface="OFS.interfaces.IObjectManager"
/> />
<implements <implements
class="OFS.PropertyManager.PropertyManager" class="OFS.PropertyManager.PropertyManager"
interface=".interfaces.IPropertyManager" interface="OFS.interfaces.IPropertyManager"
/> />
<implements <implements
class="OFS.FindSupport.FindSupport" class="OFS.FindSupport.FindSupport"
interface=".interfaces.IFindSupport" interface="OFS.interfaces.IFindSupport"
/> />
<implements <implements
class="OFS.Folder.Folder" class="OFS.Folder.Folder"
interface=".interfaces.IFolder" interface="OFS.interfaces.IFolder"
/> />
<implements <implements
class="OFS.OrderSupport.OrderSupport" class="OFS.OrderSupport.OrderSupport"
interface=".interfaces.IOrderedContainer" interface="OFS.interfaces.IOrderedContainer"
/> />
<implements <implements
class="OFS.OrderedFolder.OrderedFolder" class="OFS.OrderedFolder.OrderedFolder"
interface=".interfaces.IOrderedFolder" interface="OFS.interfaces.IOrderedFolder"
/> />
<implements <implements
class="OFS.Application.Application" class="OFS.Application.Application"
interface=".interfaces.IApplication" interface="OFS.interfaces.IApplication"
/> />
<!-- App --> <!-- App -->
<implements <implements
class="App.Undo.UndoSupport" class="App.Undo.UndoSupport"
interface=".interfaces.IUndoSupport" interface="App.interfaces.IUndoSupport"
/> />
<implements <implements
class="App.Management.Navigation" class="App.Management.Navigation"
interface=".interfaces.INavigation" interface="App.interfaces.INavigation"
/> />
<!-- AccessControl --> <!-- AccessControl -->
<implements <implements
class="AccessControl.Owned.Owned" class="AccessControl.Owned.Owned"
interface=".interfaces.IOwned" interface="AccessControl.interfaces.IOwned"
/> />
<implements <implements
class="AccessControl.PermissionMapping.RoleManager" class="AccessControl.PermissionMapping.RoleManager"
interface=".interfaces.IPermissionMapping" interface="AccessControl.interfaces.IPermissionMappingSupport"
/> />
<implements <implements
class="AccessControl.Role.RoleManager" class="AccessControl.Role.RoleManager"
interface=".interfaces.IRoleManager" interface="AccessControl.interfaces.IRoleManager"
/> />
</configure> </configure>
...@@ -2,6 +2,13 @@ ...@@ -2,6 +2,13 @@
xmlns="http://namespaces.zope.org/zope" xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta"> xmlns:meta="http://namespaces.zope.org/meta">
<include package=".browser" file="meta.zcml" />
<include package=".form" file="meta.zcml" />
<!-- load the zope:modulealias and zope:hook directives -->
<include package="zope.modulealias" file="meta.zcml" />
<include package="zope.configuration" file="meta.zcml" />
<meta:directives namespace="http://namespaces.zope.org/zope"> <meta:directives namespace="http://namespaces.zope.org/zope">
<meta:directive <meta:directive
...@@ -10,12 +17,6 @@ ...@@ -10,12 +17,6 @@
handler="zope.app.security.metaconfigure.definePermission" handler="zope.app.security.metaconfigure.definePermission"
/> />
<meta:directive
name="redefinePermission"
schema="zope.app.security.metadirectives.IRedefinePermission"
handler="zope.app.security.metaconfigure.redefinePermission"
/>
<meta:directive <meta:directive
name="interface" name="interface"
schema="zope.app.component.metadirectives.IInterfaceDirective" schema="zope.app.component.metadirectives.IInterfaceDirective"
...@@ -46,6 +47,12 @@ ...@@ -46,6 +47,12 @@
handler="zope.app.component.metaconfigure.utility" handler="zope.app.component.metaconfigure.utility"
/> />
<meta:directive
name="factory"
schema="zope.app.component.metadirectives.IFactoryDirective"
handler="zope.app.component.metaconfigure.factory"
/>
<meta:directive <meta:directive
name="serviceType" name="serviceType"
schema="zope.app.component.metadirectives.IServiceTypeDirective" schema="zope.app.component.metadirectives.IServiceTypeDirective"
...@@ -89,135 +96,6 @@ ...@@ -89,135 +96,6 @@
</meta:directives> </meta:directives>
<meta:directives namespace="http://namespaces.zope.org/browser">
<meta:directive
name="layer"
schema="zope.app.publisher.browser.metadirectives.ILayerDirective"
handler="zope.app.publisher.browser.metaconfigure.layer"
/>
<meta:directive
name="skin"
schema="zope.app.publisher.browser.metadirectives.ISkinDirective"
handler="zope.app.publisher.browser.metaconfigure.skin"
/>
<meta:directive
name="defaultSkin"
schema="zope.app.publisher.browser.metadirectives.IDefaultSkinDirective"
handler="zope.app.publisher.browser.metaconfigure.defaultSkin"
/>
<meta:directive
name="defaultView"
schema="zope.app.publisher.browser.metadirectives.IDefaultViewDirective"
handler=".browserconfigure.defaultView"
/>
<meta:directive
name="page"
schema="zope.app.publisher.browser.metadirectives.IPageDirective"
handler=".browserconfigure.page"
/>
<meta:complexDirective
name="pages"
schema="zope.app.publisher.browser.metadirectives.IPagesDirective"
handler=".browserconfigure.pages"
>
<meta:subdirective
name="page"
schema="zope.app.publisher.browser.metadirectives.IPagesPageSubdirective"
/>
</meta:complexDirective>
<meta:directive
name="resource"
schema="zope.app.publisher.browser.metadirectives.IResourceDirective"
handler=".browserconfigure.resource"
/>
<meta:directive
name="resourceDirectory"
schema="zope.app.publisher.browser.metadirectives.IResourceDirectoryDirective"
handler=".browserconfigure.resourceDirectory"
/>
<meta:directive
name="menu"
schema="zope.app.publisher.browser.metadirectives.IMenuDirective"
handler="zope.app.publisher.browser.globalbrowsermenuservice.menuDirective"
/>
<meta:directive
name="menuItem"
schema="zope.app.publisher.browser.metadirectives.IMenuItemDirective"
handler="zope.app.publisher.browser.globalbrowsermenuservice.menuItemDirective"
/>
<meta:complexDirective
name="menuItems"
schema="zope.app.publisher.browser.metadirectives.IMenuItemsDirective"
handler="zope.app.publisher.browser.globalbrowsermenuservice.menuItemsDirective"
>
<meta:subdirective
name="menuItem"
schema="zope.app.publisher.browser.metadirectives.IMenuItemSubdirective"
/>
</meta:complexDirective>
<meta:complexDirective
name="view"
schema="zope.app.publisher.browser.metadirectives.IViewDirective"
handler=".browserconfigure.view"
>
<meta:subdirective
name="page"
schema="zope.app.publisher.browser.metadirectives.IViewPageSubdirective"
/>
<meta:subdirective
name="defaultPage"
schema="zope.app.publisher.browser.metadirectives.IViewDefaultPageSubdirective"
/>
</meta:complexDirective>
<meta:complexDirective
name="editform"
schema="zope.app.form.browser.metadirectives.IEditFormDirective"
handler=".browserconfigure.EditFormDirective"
>
<meta:subdirective
name="widget"
schema="zope.app.form.browser.metadirectives.IWidgetSubdirective"
/>
</meta:complexDirective>
<meta:complexDirective
name="addform"
schema="zope.app.form.browser.metadirectives.IAddFormDirective"
handler=".browserconfigure.AddFormDirective"
>
<meta:subdirective
name="widget"
schema="zope.app.form.browser.metadirectives.IWidgetSubdirective"
/>
</meta:complexDirective>
</meta:directives>
<meta:directives namespace="http://namespaces.zope.org/five"> <meta:directives namespace="http://namespaces.zope.org/five">
<!-- specific to Five --> <!-- specific to Five -->
...@@ -286,4 +164,14 @@ ...@@ -286,4 +164,14 @@
</meta:directives> </meta:directives>
<meta:directive
name="redefinePermission"
namespace="http://namespaces.zope.org/meta"
schema="zope.app.security.metadirectives.IRedefinePermission"
handler="zope.app.security.metaconfigure.redefinePermission"
/>
<!-- load the i18n:registerTranslations directive -->
<include package="zope.app.i18n" file="meta.zcml" />
</configure> </configure>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Generic Components ZCML Handlers """Generic Components ZCML Handlers
$Id: metaconfigure.py 12915 2005-05-31 10:23:19Z philikon $ $Id: metaconfigure.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from types import ModuleType from types import ModuleType
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Five security handling """Five security handling
$Id: security.py 12915 2005-05-31 10:23:19Z philikon $ $Id: security.py 14595 2005-07-12 21:26:12Z philikon $
""" """
from zope.interface import implements, classProvides from zope.interface import implements, classProvides
from zope.component import queryUtility, getUtility from zope.component import queryUtility, getUtility
...@@ -62,7 +62,6 @@ def checkPermission(permission, object, interaction=None): ...@@ -62,7 +62,6 @@ def checkPermission(permission, object, interaction=None):
checkPermission is guaranteed to return True if permission is checkPermission is guaranteed to return True if permission is
CheckerPublic or None. CheckerPublic or None.
""" """
if (permission in ('zope.Public', 'zope2.Public') or if (permission in ('zope.Public', 'zope2.Public') or
permission is None or permission is CheckerPublic): permission is None or permission is CheckerPublic):
return True return True
......
...@@ -14,11 +14,14 @@ ...@@ -14,11 +14,14 @@
"""Use structured monkey-patching to enable ``ISized`` adapters for """Use structured monkey-patching to enable ``ISized`` adapters for
Zope 2 objects. Zope 2 objects.
$Id: sizeconfigure.py 12915 2005-05-31 10:23:19Z philikon $ $Id: sizeconfigure.py 14449 2005-07-09 21:12:22Z philikon $
""" """
from zope.app.size.interfaces import ISized from zope.app.size.interfaces import ISized
from Products.Five.fiveconfigure import isFiveMethod from Products.Five.fiveconfigure import isFiveMethod
# holds classes that were monkeyed with; for clean up
_monkied = []
def get_size(self): def get_size(self):
size = ISized(self, None) size = ISized(self, None)
if size is not None: if size is not None:
...@@ -37,6 +40,8 @@ def classSizable(class_): ...@@ -37,6 +40,8 @@ def classSizable(class_):
if hasattr(class_, "get_size") and not isFiveMethod(class_.get_size): if hasattr(class_, "get_size") and not isFiveMethod(class_.get_size):
class_.__five_original_get_size = class_.get_size class_.__five_original_get_size = class_.get_size
class_.get_size = get_size class_.get_size = get_size
# remember class for clean up
_monkied.append(class_)
def sizable(_context, class_): def sizable(_context, class_):
_context.action( _context.action(
...@@ -44,3 +49,18 @@ def sizable(_context, class_): ...@@ -44,3 +49,18 @@ def sizable(_context, class_):
callable = classSizable, callable = classSizable,
args=(class_,) args=(class_,)
) )
# clean up code
from Products.Five.fiveconfigure import killMonkey
from zope.testing.cleanup import addCleanUp
def unsizable(class_):
"""Restore class's initial state with respect to being sizable"""
killMonkey(class_, 'get_size', '__five_original_get_size')
def cleanUp():
for class_ in _monkied:
unsizable(class_)
addCleanUp(cleanUp)
del addCleanUp
<configure xmlns="http://namespaces.zope.org/zope" <configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five"> xmlns:meta="http://namespaces.zope.org/meta"
xmlns:five="http://namespaces.zope.org/five">
<!-- Copy this file to your ``INSTANCE_HOME/etc`` directory --> <!-- Copy this file to your ``INSTANCE_HOME/etc`` directory -->
<include package="Products.Five" /> <include package="Products.Five" />
<redefinePermission from="zope2.Public" to="zope.Public" /> <meta:redefinePermission from="zope2.Public" to="zope.Public" />
<include files="package-includes/*-meta.zcml" /> <include files="package-includes/*-meta.zcml" />
<include files="package-includes/*-configure.zcml" /> <include files="package-includes/*-configure.zcml" />
......
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<browser:page
for="*"
name="standard_macros"
permission="zope2.View"
class=".standardmacros.StandardMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
<browser:page
for="*"
template="five_template.pt"
name="five_template"
permission="zope.Public"
/>
</configure>
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Mimick the Zope 3 skinning system in Five. """Mimick the Zope 3 skinning system in Five.
$Id: skin.py 12915 2005-05-31 10:23:19Z philikon $ $Id: standardmacros.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from zope.interface.common.mapping import IItemMapping from zope.interface.common.mapping import IItemMapping
from zope.interface import implements from zope.interface import implements
...@@ -51,7 +51,3 @@ class StandardMacros(BrowserView, Macros): ...@@ -51,7 +51,3 @@ class StandardMacros(BrowserView, Macros):
macro_pages = ('five_template', macro_pages = ('five_template',
'widget_macros', 'widget_macros',
'form_macros',) 'form_macros',)
# copy of zope.app.form.browser.macros.FormMacros
class FormMacros(StandardMacros):
macro_pages = ('widget_macros', 'addform_macros')
<html metal:define-macro="birdmacro"><head><title>bird macro</title></head><body>Color: <metal:block define-slot="color" /><metal:block define-slot="birdinfo" /></body></html>
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<!-- macro aggregation page -->
<browser:page
for="*"
name="fivetest_macros"
permission="zope2.View"
class=".demomacros.StandardMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
<!-- macro pages -->
<browser:page
for="OFS.interfaces.IFolder"
template="bird.pt"
name="bird.pt"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for="OFS.interfaces.IFolder"
template="bird.pt"
name="bird_macros"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for="OFS.interfaces.IFolder"
template="dog.pt"
name="dog_macros"
permission="zope2.ViewManagementScreens"
/>
</configure>
\ No newline at end of file
...@@ -11,35 +11,14 @@ ...@@ -11,35 +11,14 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Size adapters """Demo StandardMacros
$Id: size.py 12915 2005-05-31 10:23:19Z philikon $ $Id: demomacros.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from zope.interface import implements from Products.Five import StandardMacros as BaseMacros
from zope.app.size.interfaces import ISized
class SimpleContentSize(object): class StandardMacros(BaseMacros):
"""Size for ``SimpleContent`` objects."""
implements(ISized)
def __init__(self, context): macro_pages = ('bird_macros', 'dog_macros')
self.context = context aliases = {'flying':'birdmacro',
'walking':'dogmacro'}
def sizeForSorting(self):
return ('byte', 42)
def sizeForDisplay(self):
return "What is the meaning of life?"
class FancyContentSize(object):
"""Size for ``SimpleContent`` objects."""
implements(ISized)
def __init__(self, context):
self.context = context
def sizeForSorting(self):
return ('line', 143)
def sizeForDisplay(self):
return "That's not the meaning of life!"
##############################################################################
#
# ZopeTestCase
#
# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
#
# This version of framework.py will use the SOFTWARE_HOME
# environment variable to locate Zope and the Testing package.
#
# If the tests are run in an INSTANCE_HOME installation of Zope,
# Products.__path__ and sys.path with be adjusted to include the
# instance's Products and lib/python directories respectively.
#
# If you explicitly set INSTANCE_HOME prior to running the tests,
# auto-detection is disabled and the specified path will be used
# instead.
#
# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
# will be adjusted to use it.
#
# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
# is assumed, and you can attach to a running ZEO server (via the
# instance's custom_zodb.py).
#
##############################################################################
#
# The following code should be at the top of every test module:
#
# import os, sys
# if __name__ == '__main__':
# execfile(os.path.join(sys.path[0], 'framework.py'))
#
# ...and the following at the bottom:
#
# if __name__ == '__main__':
# framework()
#
##############################################################################
__version__ = '0.2.3'
# Save start state
#
__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
if __SOFTWARE_HOME.endswith(os.sep):
__SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
if __INSTANCE_HOME.endswith(os.sep):
__INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
# Find and import the Testing package
#
if not sys.modules.has_key('Testing'):
p0 = sys.path[0]
if p0 and __name__ == '__main__':
os.chdir(p0)
p0 = ''
s = __SOFTWARE_HOME
p = d = s and s or os.getcwd()
while d:
if os.path.isdir(os.path.join(p, 'Testing')):
zope_home = os.path.dirname(os.path.dirname(p))
sys.path[:1] = [p0, p, zope_home]
break
p, d = s and ('','') or os.path.split(p)
else:
print 'Unable to locate Testing package.',
print 'You might need to set SOFTWARE_HOME.'
sys.exit(1)
import Testing, unittest
execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
# Include ZopeTestCase support
#
if 1: # Create a new scope
p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
if not os.path.isdir(p):
print 'Unable to locate ZopeTestCase package.',
print 'You might need to install ZopeTestCase.'
sys.exit(1)
ztc_common = 'ztc_common.py'
ztc_common_global = os.path.join(p, ztc_common)
f = 0
if os.path.exists(ztc_common_global):
execfile(ztc_common_global)
f = 1
if os.path.exists(ztc_common):
execfile(ztc_common)
f = 1
if not f:
print 'Unable to locate %s.' % ztc_common
sys.exit(1)
# Debug
#
print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
sys.stdout.flush()
##############################################################################
#
# Copyright (c) 2004, 2005 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 standard macros
$Id: test_standardmacros.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_standard_macros():
"""Test standard macros
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.login('manager')
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> manage_addFiveTraversableFolder(self.folder, 'testoid', 'Testoid')
>>> import Products.Five.skin.tests
>>> from Products.Five import zcml
>>> zcml.load_config('configure.zcml', package=Products.Five)
>>> zcml.load_config('configure.zcml', package=Products.Five.skin.tests)
Test macro access through our flavour of StandardMacros. First,
when looking up a non-existing macro, we get a KeyError:
>>> view = self.folder.unrestrictedTraverse('testoid/@@fivetest_macros')
>>> view['non-existing-macro']
Traceback (most recent call last):
...
KeyError: 'non-existing-macro'
Existing macros are accessible through index notation:
>>> for macroname in ('birdmacro', 'dogmacro', 'flying', 'walking'):
... view[macroname] is not None
True
True
True
True
Aliases are resolve correctly:
>>> view['flying'] is view['birdmacro']
True
>>> view['walking'] is view['dogmacro']
True
One can also access the macros through regular traversal:
>>> base = 'testoid/@@fivetest_macros/%s'
>>> for macro in ('birdmacro', 'dogmacro', 'flying', 'walking'):
... view = self.folder.unrestrictedTraverse(base % macro)
... view is not None
True
True
True
True
Clean up:
>>> from zope.app.tests.placelesssetup import tearDown
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 helpers
$Id: __init__.py 14470 2005-07-10 11:57:49Z philikon $
"""
from Products.Five.testing.restricted import RestrictedPythonTestCase
from Products.Five.testing.folder import FiveTraversableFolder
from Products.Five.testing.folder import manage_addFiveTraversableFolder
from Products.Five.testing.folder import NoVerifyPasteFolder
from Products.Five.testing.folder import manage_addNoVerifyPasteFolder
...@@ -11,17 +11,19 @@ ...@@ -11,17 +11,19 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Fancy content """Test content objects.
$Id: fancycontent.py 12915 2005-05-31 10:23:19Z philikon $ $Id: fancycontent.py 14470 2005-07-10 11:57:49Z philikon $
""" """
import Acquisition import Acquisition
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from Globals import InitializeClass from Globals import InitializeClass
from zope.interface import implements from zope.interface import Interface, implements
from interfaces import IFancyContent
class IFancyContent(Interface):
pass
class FancyAttribute(Acquisition.Explicit): class FancyAttribute(Acquisition.Explicit):
"""Doc test fanatics""" """Doc test fanatics"""
......
...@@ -11,29 +11,14 @@ ...@@ -11,29 +11,14 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Test helpers """Test folders
$Id: helpers.py 12915 2005-05-31 10:23:19Z philikon $ $Id: folder.py 14468 2005-07-10 11:39:32Z philikon $
""" """
import urllib
def add_and_edit(self, id, REQUEST):
"""Helper function to point to the object's management screen if
'Add and Edit' button is pressed.
id -- id of the object we just added
"""
if REQUEST is None:
return
try:
u = self.DestinationURL()
except:
u = REQUEST['URL1']
if REQUEST.has_key('submit_edit'):
u = "%s/%s" % (u, urllib.quote(id))
REQUEST.RESPONSE.redirect(u+'/manage_main')
from OFS.Folder import Folder from OFS.Folder import Folder
from OFS.interfaces import IFolder
from zope.interface import implements
from Products.Five.traversable import Traversable
class NoVerifyPasteFolder(Folder): class NoVerifyPasteFolder(Folder):
"""Folder that does not perform paste verification. """Folder that does not perform paste verification.
...@@ -48,14 +33,13 @@ def manage_addNoVerifyPasteFolder(container, id, title=''): ...@@ -48,14 +33,13 @@ def manage_addNoVerifyPasteFolder(container, id, title=''):
folder.id = id folder.id = id
folder.title = title folder.title = title
class FiveTraversableFolder(Folder): class FiveTraversableFolder(Traversable, Folder):
"""Folder that is declared Five traversable, see configure.zcml """Folder that is five-traversable
""" """
pass implements(IFolder)
def manage_addFiveTraversableFolder(container, id, title=''): def manage_addFiveTraversableFolder(container, id, title=''):
container._setObject(id, FiveTraversableFolder()) container._setObject(id, FiveTraversableFolder())
folder = container[id] folder = container[id]
folder.id = id folder.id = id
folder.title = title folder.title = title
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Restricted python test helpers
Based on Plone's RestrictedPythonTestCase, with kind permission by the
Plone developers.
$Id: restricted.py 14473 2005-07-10 13:02:21Z philikon $
"""
from AccessControl import Unauthorized
from Testing.ZopeTestCase import ZopeTestCase
def addPythonScript(folder, id, params='', body=''):
"""Add a PythonScript to folder."""
# clean up any 'ps' that's already here..
try:
folder._getOb(id)
folder.manage_delObjects([id])
except AttributeError:
pass # it's okay, no 'ps' exists yet
factory = folder.manage_addProduct['PythonScripts']
factory.manage_addPythonScript(id)
folder[id].ZPythonScript_edit(params, body)
def checkRestricted(folder, psbody):
"""Perform a check by running restricted Python code."""
addPythonScript(folder, 'ps', body=psbody)
try:
folder.ps()
except Unauthorized, e:
raise AssertionError, e
def checkUnauthorized(folder, psbody):
"""Perform a check by running restricted Python code. Expect to
encounter an Unauthorized exception."""
addPythonScript(folder, 'ps', body=psbody)
try:
folder.ps()
except Unauthorized:
pass
else:
raise AssertionError, "Authorized but shouldn't be"
class RestrictedPythonTestCase(ZopeTestCase):
"""Javiotic test case for restricted code."""
def addPS(self, id, params='', body=''):
addPythonScript(self.folder, id, params, body)
def check(self, psbody):
checkRestricted(self.folder, psbody)
def checkUnauthorized(self, psbody):
checkUnauthorized(self.folder, psbody)
...@@ -11,29 +11,45 @@ ...@@ -11,29 +11,45 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Simple content implementationm """Simple content class(es) for browser tests
$Id: simplecontent.py 12915 2005-05-31 10:23:19Z philikon $ $Id: simplecontent.py 17810 2005-09-24 09:12:59Z efge $
""" """
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from Globals import InitializeClass from Globals import InitializeClass
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from helpers import add_and_edit
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from zope.interface import implements
from interfaces import ISimpleContent, ICallableSimpleContent,\
IIndexSimpleContent, IFieldSimpleContent
class SimpleContent(SimpleItem): from zope.interface import Interface, implements
from Products.Five.traversable import Traversable
class ISimpleContent(Interface):
pass
class ICallableSimpleContent(ISimpleContent):
pass
class IIndexSimpleContent(ISimpleContent):
pass
class SimpleContent(Traversable, SimpleItem):
implements(ISimpleContent) implements(ISimpleContent)
meta_type = 'Five SimpleContent' meta_type = 'Five SimpleContent'
security = ClassSecurityInfo() security = ClassSecurityInfo()
afterAdd_called = False
beforeDelete_called = False
def __init__(self, id, title): def __init__(self, id, title):
self.id = id self.id = id
self.title = title self.title = title
def manage_afterAdd(self, item, container):
self.afterAdd_called = True
def manage_beforeDelete(self, item, container):
self.beforeDelete_called = True
security.declarePublic('mymethod') security.declarePublic('mymethod')
def mymethod(self): def mymethod(self):
return "Hello world" return "Hello world"
...@@ -70,42 +86,14 @@ class IndexSimpleContent(SimpleItem): ...@@ -70,42 +86,14 @@ class IndexSimpleContent(SimpleItem):
InitializeClass(IndexSimpleContent) InitializeClass(IndexSimpleContent)
class FieldSimpleContent(SimpleContent):
"""A Viewable piece of content with fields"""
implements(IFieldSimpleContent)
meta_type = 'Five FieldSimpleContent'
InitializeClass(FieldSimpleContent)
manage_addSimpleContentForm = PageTemplateFile(
"www/simpleContentAdd", globals(),
__name__ = 'manage_addSimpleContentForm')
def manage_addSimpleContent(self, id, title, REQUEST=None): def manage_addSimpleContent(self, id, title, REQUEST=None):
"""Add the simple content.""" """Add the simple content."""
id = self._setObject(id, SimpleContent(id, title)) self._setObject(id, SimpleContent(id, title))
add_and_edit(self, id, REQUEST)
return ''
def manage_addCallableSimpleContent(self, id, title, REQUEST=None): def manage_addCallableSimpleContent(self, id, title, REQUEST=None):
"""Add the viewable simple content.""" """Add the viewable simple content."""
id = self._setObject(id, CallableSimpleContent(id, title)) self._setObject(id, CallableSimpleContent(id, title))
add_and_edit(self, id, REQUEST)
return ''
def manage_addIndexSimpleContent(self, id, title, REQUEST=None): def manage_addIndexSimpleContent(self, id, title, REQUEST=None):
"""Add the viewable simple content.""" """Add the viewable simple content."""
id = self._setObject(id, IndexSimpleContent(id, title)) self._setObject(id, IndexSimpleContent(id, title))
add_and_edit(self, id, REQUEST)
return ''
manage_addFieldSimpleContentForm = PageTemplateFile(
"www/fieldSimpleContentAdd", globals(),
__name__ = 'manage_addFieldSimpleContentForm')
def manage_addFieldSimpleContent(self, id, title, REQUEST=None):
"""Add the viewable simple content."""
id = self._setObject(id, FieldSimpleContent(id, title))
add_and_edit(self, id, REQUEST)
return ''
...@@ -11,12 +11,35 @@ ...@@ -11,12 +11,35 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Adapter test classes. """Adapter test fixtures
$Id: classes.py 12915 2005-05-31 10:23:19Z philikon $ $Id: adapters.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from zope.interface import implements from zope.interface import implements, Interface
from interfaces import IAdaptable, IAdapted, IOrigin, IDestination
class IAdaptable(Interface):
"""This is a Zope 3 interface.
"""
def method():
"""This method will be adapted
"""
class IAdapted(Interface):
"""The interface we adapt to.
"""
def adaptedMethod():
"""A method to adapt.
"""
class IOrigin(Interface):
"""Something we'll adapt"""
class IDestination(Interface):
"""The result of an adaption"""
def method():
"""Do something"""
class Adaptable: class Adaptable:
implements(IAdaptable) implements(IAdaptable)
......
############################################################################## ##############################################################################
# #
# Copyright (c) 2004, 2005 Zope Corporation and Contributors. # Copyright (c) 2005 Zope Corporation and Contributors.
# All Rights Reserved. # All Rights Reserved.
# #
# This software is subject to the provisions of the Zope Public License, # This software is subject to the provisions of the Zope Public License,
...@@ -11,32 +11,33 @@ ...@@ -11,32 +11,33 @@
# FOR A PARTICULAR PURPOSE. # FOR A PARTICULAR PURPOSE.
# #
############################################################################## ##############################################################################
"""Load zope.conf for tests. """Boiler plate test module
$Id: zopeconf.py 12915 2005-05-31 10:23:19Z philikon $ $Id: boilerplate.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os import os, sys
from os.path import join, abspath, dirname, split, exists if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def process(): def test_boilerplate():
"""Read in zope.conf configuration file. """
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
>>> import Products.Five.tests
>>> from Products.Five import zcml
>>> zcml.load_config('boilerplate.zcml', Products.Five.tests)
>>> from Products.Five.testing import manage_addFiveTraversableFolder
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> from Products.Five.testing.fancycontent import manage_addFancyContent
This is a hack but there doesn't seem to be a better way. >>> tearDown()
""" """
_prefix = os.environ.get('INSTANCE_HOME')
if not _prefix:
try:
__file__
except NameError:
# Test was called directly, so no __file__ global exists.
_prefix = abspath(os.curdir)
else:
# Test was called by another test.
_prefix = abspath(dirname(__file__))
_prefix = join(_prefix, '..', '..', '..')
_config = join(_prefix, 'etc', 'zope.conf') def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if exists(_config): if __name__ == '__main__':
from Zope import configure framework()
configure(_config)
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<adapter
for=".adapters.IAdaptable"
provides=".adapters.IAdapted"
factory=".adapters.Adapter"
/>
<!-- this is a test whether five:traversable can be called more than
once on a class; SimpleContent inherits from Traversable, so
one directive suffices here -->
<five:traversable class="Products.Five.testing.simplecontent.SimpleContent" />
<!-- this is a test whether the *directive* can be called more than
once without raising a conflicting configuration exception -->
<five:traversable class="Products.Five.testing.simplecontent.SimpleContent" />
<!-- this tests whether five:traversable can be called on a class that
already provides __bobo_traverse__, such as our FancyContent -->
<five:traversable class="Products.Five.testing.fancycontent.FancyContent" />
<!-- Testing the vocabulary directive -->
<vocabulary
name="aVocabulary"
factory="zope.schema.tests.test_vocabulary.SampleVocabulary"
/>
<!-- testing that products meta.zcml statements are picked up. -->
<include file="meta.zcml" />
<five:parrot
class=".metaconfigure.NorwegianBlue"
name="Polly"
/>
<!-- stuff that we'll override in overrides.zcml -->
<adapter
for=".adapters.IOrigin"
provides=".adapters.IDestination"
factory=".adapters.OriginalAdapter"
/>
</configure>
Test events
===========
Before we can start, we need to set up an event subscriber that allows
us to inspect events that will be thrown during the test:
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
Add a folder that doesn't verify objects on paste. We use it as a
test sandbox:
>>> from Products.Five.testing import manage_addNoVerifyPasteFolder
>>> manage_addNoVerifyPasteFolder(self.folder, 'npvf')
>>> folder = self.folder.npvf
Finally add a manager user login, give it the right permissions and
log in using it:
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> self.setPermissions(standard_permissions + ['Copy or Move'], 'Manager')
>>> self.login('manager')
>>> from zope.app.event.tests.placelesssetup import getEvents, clearEvents
Sending events
--------------
Zope 2 classes need to be modified so that they send Zope 3 style
events. Our stub class here is such a case. We can add it to a
folder, for example, ...
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(folder, 'foo', 'Foo')
and no event will have been triggered:
>>> len(getEvents())
0
Clean up:
>>> folder.manage_delObjects(['foo'])
Now make the class send events:
>>> from Products.Five.eventconfigure import classSendEvents
>>> from Products.Five.testing.simplecontent import SimpleContent
>>> classSendEvents(SimpleContent)
Added event
------------
Let's add an object to a folder:
>>> manage_addSimpleContent(folder, 'foo', 'Foo')
One object event should have been sent with the event's object being
our foo object:
>>> events = getEvents()
>>> len(events)
1
>>> foo = folder.foo
>>> events[0].object == foo
True
That object event should have been an object added event:
>>> from zope.app.container.interfaces import IObjectAddedEvent
>>> events = getEvents(IObjectAddedEvent)
>>> len(events)
1
>>> events[0].object == foo
True
>>> events[0].newParent == foo.aq_parent
True
Check that the object's original manage_afterAdd method was also called:
>>> foo.afterAdd_called
True
Now clean up:
>>> clearEvents()
Moved event (I) -- Renaming
--------------------------
Somehow we need to at least commit a subtransaction to make renaming
succeed:
>>> import transaction
>>> transaction.commit(1)
Let's rename the object we created before:
>>> folder.manage_renameObject('foo', 'bar')
We should get two events...
>>> events = getEvents()
>>> len(events)
2
the removed event...
>>> event = events[0]
>>> from zope.app.container.interfaces import IObjectRemovedEvent
>>> IObjectRemovedEvent.providedBy(event)
True
>>> event.object == foo
True
>>> event.oldName, event.newName
('foo', None)
>>> event.oldParent == folder
True
>>> event.newParent is None
True
and the moved event:
>>> event = events[1]
>>> event.object == foo
True
>>> event.oldName, event.newName
('foo', 'bar')
>>> event.oldParent == folder
True
>>> event.newParent == folder
True
Now clean up:
>>> folder.manage_delObjects(['bar'])
>>> clearEvents()
We don't delete the stub object just yet because it's being used in
the next part of the test.
Moved event (II) -- Cut and paste
---------------------------------
Let's move from one folder to another:
>>> manage_addNoVerifyPasteFolder(folder, 'folder1', 'Folder1')
>>> folder1 = folder.folder1
>>> manage_addNoVerifyPasteFolder(folder, 'folder2', 'Folder2')
>>> folder2 = folder.folder2
>>> manage_addSimpleContent(folder1, 'foo', 'Foo')
>>> foo = folder1.foo
We need to trigger a subtransaction before cut/paste can work:
>>> transaction.commit(1)
>>> cb = folder1.manage_cutObjects(['foo'])
>>> info = folder2.manage_pasteObjects(cb)
Apart from the added event we triggerred when we added the stub object
to the folder, we expect two events...
>>> events = getEvents()
>>> len(events)
3
>>> len(getEvents(IObjectAddedEvent))
1
a removed event...
>>> event = events[1]
>>> from zope.app.container.interfaces import IObjectRemovedEvent
>>> IObjectRemovedEvent.providedBy(event)
True
>>> event.oldParent == folder1
True
>>> event.newParent is None
True
and a moved event:
>>> event = events[2]
>>> event.object == foo
True
>>> event.oldName, event.newName
('foo', 'foo')
>>> event.oldParent == folder1
True
>>> event.newParent == folder2
True
Now clean up:
>>> folder.manage_delObjects(['folder1'])
>>> folder.manage_delObjects(['folder2'])
>>> clearEvents()
Copied event
------------
>>> manage_addSimpleContent(folder, 'foo', 'Foo')
>>> manage_addNoVerifyPasteFolder(folder, 'folder1')
>>> folder1 = folder.folder1
We need to trigger subtransaction before copy/paste can work
>>> transaction.commit(1)
>>> cb = folder.manage_copyObjects(['foo'])
>>> info = folder1.manage_pasteObjects(cb)
>>> foo = folder1.foo
Apart from the added event we triggerred when we added the stub object
to the folder, we expect two events...
>>> events = getEvents()
>>> len(events)
3
a copied event...
>>> event = events[1]
>>> from zope.app.event.interfaces import IObjectCopiedEvent
>>> IObjectCopiedEvent.providedBy(event)
True
>>> events[1].object == foo
True
and an added event:
>>> event = events[2]
>>> IObjectAddedEvent.providedBy(event)
True
>>> event.object == foo
True
>>> event.newName
'foo'
>>> event.newParent == folder1
True
Now clean up:
>>> folder.manage_delObjects(['folder1'])
>>> folder.manage_delObjects(['foo'])
>>> clearEvents()
Removed event
-------------
>>> manage_addSimpleContent(folder, 'foo', 'Foo')
>>> foo = folder.foo
>>> foo.beforeDelete_called
False
>>> folder.manage_delObjects(['foo'])
>>> events = getEvents()
>>> len(events)
2
>>> events[1].object.id
'foo'
Check that the object's original manage_beforeDelete method was also called:
>>> foo.beforeDelete_called
True
>>> clearEvents()
Clean up
--------
Finally, we need to put our stub class back the way it was before we
monkeyed with it:
>>> from Products.Five.eventconfigure import cleanUp
>>> cleanUp()
Now adding an object won't trigger an event anymore:
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> manage_addSimpleContent(folder, 'foo', 'Foo')
>>> len(getEvents())
0
Finally, we need to tear down everything else (services, etc.)
>>> tearDown()
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Five test fixtures.
$Id: fivetest.py 12915 2005-05-31 10:23:19Z philikon $
"""
# ZopeTestCase was not designed to run tests as part of the
# Zope test suite proper. In particular, it intercepts product
# installation. We have to work around this for the Five tests
# by starting up Zope2 before doing anything else.
#
# Note: fivetest must be imported first thing by test modules!
def _part_of_zope_suite():
# Find out if we run from softwarehome
from os.path import normcase, realpath
from App.config import getConfiguration
softwarehome = normcase(realpath(getConfiguration().softwarehome))
return normcase(realpath(__file__)).startswith(softwarehome)
def _start_zope():
# Startup Zope 2.7 or 2.8+
import Testing
try:
import Zope2 as Zope
except ImportError:
import Zope
Zope.startup()
def _load_test_config():
# Load up the ZCML config for the FiveTest product
from os.path import dirname, join
from Products.Five import zcml
from Products.Five.tests.products import FiveTest
prefix = dirname(FiveTest.__file__)
zcml.load_config(join(prefix, 'testing.zcml'), FiveTest)
def _main():
if _part_of_zope_suite():
_start_zope()
else:
from Testing.ZopeTestCase import installProduct
installProduct('Five')
installProduct('PythonScripts')
_load_test_config()
_main()
from Testing.ZopeTestCase import *
class FiveTestCase(ZopeTestCase):
pass
############################################################################## ##############################################################################
# #
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved. # ZopeTestCase
#
# 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.
#
##############################################################################
##############################################################################
#
# ZopeTestCase
# #
# COPY THIS FILE TO YOUR 'tests' DIRECTORY. # COPY THIS FILE TO YOUR 'tests' DIRECTORY.
# #
...@@ -24,14 +12,14 @@ ...@@ -24,14 +12,14 @@
# instance's Products and lib/python directories respectively. # instance's Products and lib/python directories respectively.
# #
# If you explicitly set INSTANCE_HOME prior to running the tests, # If you explicitly set INSTANCE_HOME prior to running the tests,
# auto-detection is disabled and the specified path will be used # auto-detection is disabled and the specified path will be used
# instead. # instead.
# #
# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME # If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
# will be adjusted to use it. # will be adjusted to use it.
# #
# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup # If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
# is assumed, and you can attach to a running ZEO server (via the # is assumed, and you can attach to a running ZEO server (via the
# instance's custom_zodb.py). # instance's custom_zodb.py).
# #
############################################################################## ##############################################################################
...@@ -82,10 +70,6 @@ if not sys.modules.has_key('Testing'): ...@@ -82,10 +70,6 @@ if not sys.modules.has_key('Testing'):
print 'You might need to set SOFTWARE_HOME.' print 'You might need to set SOFTWARE_HOME.'
sys.exit(1) sys.exit(1)
# zope.conf must be read before 'import Testing'
import zopeconf
zopeconf.process()
import Testing, unittest import Testing, unittest
execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py')) execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
...@@ -120,3 +104,4 @@ if 1: # Create a new scope ...@@ -120,3 +104,4 @@ if 1: # Create a new scope
print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set') print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set') print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
sys.stdout.flush() sys.stdout.flush()
##############################################################################
#
# Copyright (c) 2004-2005 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.
#
##############################################################################
msgid ""
msgstr ""
"Project-Id-Version: Five 1.1\n"
"POT-Creation-Date: Sun Jun 12 17:22:49 2005\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Philipp von Weitershausen <philikon@philikon.de>\n"
"Language-Team: Five Developers <z3-five@zope.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "This is a message"
msgstr "Dies ist eine Nachricht"
# Default: "This is an explicit message"
msgid "explicit-msg"
msgstr "Dies ist eine explizite Nachricht"
msgid "These are ${number} messages"
msgstr "Dies sind ${number} Nachrichten"
# Default: "These are ${number} explicit messages"
msgid "explicit-msgs"
msgstr "Dies sind ${number} explizite Nachrichten"
msgid "This is an attribute"
msgstr "Dies ist ein Attribut"
# Default: "Explicit title"
msgid "explicit-title"
msgstr "Expliziter Titel"
# Default: "Explicit summary"
msgid "explicit-summary"
msgstr "Explizite Zusammenfassung"
##############################################################################
#
# Copyright (c) 2004-2005 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.
#
##############################################################################
# This file contains no message ids because Zope's default language
# is English
msgid ""
msgstr ""
"Project-Id-Version: Five 1.1\n"
"POT-Creation-Date: Sun Jun 12 17:22:49 2005\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Philipp von Weitershausen <philikon@philikon.de>\n"
"Language-Team: Five Developers <z3-five@zope.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
##############################################################################
#
# Copyright (c) 2004-2005 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.
#
##############################################################################
msgid ""
msgstr ""
"Project-Id-Version: Five 1.1\n"
"POT-Creation-Date: Sun Jun 12 17:22:49 2005\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Philipp von Weitershausen <philikon@philikon.de>\n"
"Language-Team: Five Developers <z3-five@zope.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "This is a message"
msgstr ""
# Default: "This is an explicit message"
msgid "explicit-msg"
msgstr ""
msgid "These are ${number} messages"
msgstr ""
# Default: "These are ${number} explicit messages"
msgid "explicit-msgs"
msgstr ""
msgid "This is an attribute"
msgstr ""
# Default: "Explicit title"
msgid "explicit-title"
msgstr ""
# Default: "Explicit summary"
msgid "explicit-summary"
msgstr ""
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Parrot directive and support classes """Parrot directive and support classes
$Id: metaconfigure.py 5287 2004-06-25 11:42:27Z philikon $ $Id: metaconfigure.py 12884 2005-05-30 13:10:41Z philikon $
""" """
from zope.interface import Interface from zope.interface import Interface
from zope.configuration.fields import GlobalObject from zope.configuration.fields import GlobalObject
......
<configure xmlns="http://namespaces.zope.org/zope">
<!-- OverrideAdapter instead of OriginalAdapter -->
<adapter
for=".adapters.IOrigin"
provides=".adapters.IDestination"
factory=".adapters.OverrideAdapter"
/>
</configure>
#import simplecontent
#import fancycontent
#def initialize(context):
#
# context.registerClass(
# simplecontent.SimpleContent,
# constructors = (simplecontent.manage_addSimpleContentForm,
# simplecontent.manage_addSimpleContent),
# )
#
# context.registerClass(
# simplecontent.CallableSimpleContent,
# constructors = (simplecontent.manage_addSimpleContentForm,
# simplecontent.manage_addCallableSimpleContent),
# )
#
# context.registerClass(
# simplecontent.IndexSimpleContent,
# constructors = (simplecontent.manage_addSimpleContentForm,
# simplecontent.manage_addIndexSimpleContent),
# )
#
# context.registerClass(
# fancycontent.FancyContent,
# constructors = (fancycontent.manage_addFancyContent,)
# )
#
# context.registerClass(
# simplecontent.FieldSimpleContent,
# constructors = (simplecontent.manage_addFieldSimpleContentForm,
# simplecontent.manage_addFieldSimpleContent,)
# )
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five"
i18n_domain="fivetest">
<!-- this is a test whether five:traversable can be called more than
once on a class; SimpleContent inherits from api.Viewable, so
one directive suffices here -->
<five:traversable class=".simplecontent.SimpleContent" />
<five:defaultViewable class=".simplecontent.SimpleContent" />
<!-- this is a test whether the *directive* can be called -->
<!-- more than once without raising a conflicting -->
<!-- configuration exception -->
<five:traversable class=".simplecontent.SimpleContent" />
<five:defaultViewable class=".simplecontent.SimpleContent" />
<browser:defaultView
for=".interfaces.ISimpleContent"
name="eagle.txt"
/>
<!-- this tests whether five:traversable can be called on a class that
already provides __bobo_traverse__, such as our FancyContent -->
<five:traversable class=".fancycontent.FancyContent" />
<!-- this tests whether five:defaultViewable can be called on a class that
already provides __call__, such as our
CallableSimpleContent
-->
<five:defaultViewable class=".simplecontent.CallableSimpleContent" />
<!-- five:pagesFromDirectory loads all .pt files in a directory as pages.
This is mainly used to load Zope2 skin templates so they can be used
in five skins and layers. -->
<five:pagesFromDirectory
module="Products.Five.tests.products.FiveTest"
directory="pages"
/>
<browser:defaultView
for=".interfaces.ICallableSimpleContent"
name="__call__"
/>
<!-- this tests whether five:defaultViewable can be called on a class that
already provides index_html, such as our
IndexSimpleContent
-->
<five:defaultViewable class=".simplecontent.IndexSimpleContent" />
<browser:defaultView
for=".interfaces.IIndexSimpleContent"
name="index_html"
/>
<adapter
for=".interfaces.IAdaptable"
provides=".interfaces.IAdapted"
factory=".classes.Adapter"
/>
<!-- attribute page -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
attribute="eagle"
name="eagle.txt"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
name="eagle.method"
permission="zope2.ViewManagementScreens"
allowed_attributes="eagle"
/>
<browser:page
for=".interfaces.IFancyContent"
class=".browser.FancyContentView"
attribute="view"
name="fancy"
permission="zope2.Public"
/>
<browser:pages
for=".interfaces.ISimpleContent"
class=".browser.NoDocstringView"
permission="zope2.Public">
<browser:page
name="nodoc-method"
attribute="method"
/>
<browser:page
name="nodoc-function"
attribute="function"
/>
<browser:page
name="nodoc-object"
attribute="object"
/>
</browser:pages>
<!-- attribute page -->
<browser:pages
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
permission="zope2.ViewManagementScreens"
>
<browser:page
name="eagle-page.txt"
attribute="eagle"
/>
<browser:page
name="mouse-page.txt"
attribute="mouse"
/>
</browser:pages>
<!-- template/class page -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
template="falcon.pt"
name="falcon.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page (with simple python expression) -->
<browser:page
for=".interfaces.ISimpleContent"
template="owl.pt"
name="owl.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page which calls on context using python -->
<browser:page
for=".interfaces.ISimpleContent"
template="flamingo.pt"
name="flamingo.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template page which calls on context using path -->
<browser:page
for=".interfaces.ISimpleContent"
template="flamingo2.pt"
name="flamingo2.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template/class page which calls on context, view, views -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
template="condor.pt"
name="condor.html"
permission="zope2.ViewManagementScreens"
/>
<!-- test TALES -->
<browser:page
for=".interfaces.ISimpleContent"
template="ostrich.pt"
name="ostrich.html"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for=".interfaces.ISimpleContent"
template="ostrich2.pt"
name="ostrich2.html"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for=".interfaces.ISimpleContent"
template="tales_traversal.pt"
name="tales_traversal.html"
permission="zope2.ViewManagementScreens"
/>
<!-- template security -->
<browser:page
for=".interfaces.ISimpleContent"
template="security.pt"
name="security.html"
permission="zope2.View"
/>
<!-- macro page -->
<browser:page
for=".interfaces.ISimpleContent"
template="bird.pt"
name="bird.pt"
permission="zope2.ViewManagementScreens"
/>
<!-- macro aggregation page -->
<browser:page
for="*"
name="fivetest_macros"
permission="zope2.View"
class=".browser.StandardMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
<browser:page
for=".interfaces.ISimpleContent"
template="bird.pt"
name="bird_macros"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for=".interfaces.ISimpleContent"
template="dog.pt"
name="dog_macros"
permission="zope2.ViewManagementScreens"
/>
<!-- template page that uses macro page -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
template="seagull.pt"
name="seagull.html"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
template="parakeet.pt"
name="parakeet.html"
permission="zope2.ViewManagementScreens"
/>
<!-- a publicly accessible page, attribute, template, template/class -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
attribute="eagle"
name="public_attribute_page"
permission="zope2.Public"
/>
<browser:page
for=".interfaces.ISimpleContent"
template="owl.pt"
name="public_template_page"
permission="zope2.Public"
/>
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
template="falcon.pt"
name="public_template_class_page"
permission="zope2.Public"
/>
<!-- a couple simple resources -->
<browser:resource
template="cockatiel.pt"
name="cockatiel.html"
permission="zope2.ViewManagementScreens"
/>
<browser:resource
file="style.css"
name="style.css"
permission="zope2.ViewManagementScreens"
/>
<browser:resource
image="pattern.png"
name="pattern.png"
permission="zope2.ViewManagementScreens"
/>
<browser:resourceDirectory
name="fivetest_resources"
directory="."
permission="zope2.ViewManagementScreens"
/>
<!-- browser forms -->
<five:traversable class=".simplecontent.FieldSimpleContent" />
<browser:editform
schema=".interfaces.IFieldSimpleContent"
for=".interfaces.IFieldSimpleContent"
name="edit.html"
permission="zope2.Public"
/>
<browser:editform
schema=".interfaces.ISimpleContent"
for=".interfaces.ISimpleContent"
name="protectededitform.html"
permission="zope2.ViewManagementScreens"
/>
<five:traversable class=".schemacontent.ComplexSchemaContent" />
<browser:editform
schema=".interfaces.IComplexSchemaContent"
for=".interfaces.IComplexSchemaContent"
name="edit.html"
permission="zope2.Public"
class=".browser.ComplexSchemaView"
/>
<view
type="zope.publisher.interfaces.browser.IBrowserRequest"
for="zope.schema.interfaces.IObject"
provides="zope.app.form.interfaces.IInputWidget"
factory="zope.app.form.browser.objectwidget.ObjectWidget"
permission="zope.Public"
/>
<!-- With a widget override -->
<browser:editform
schema=".interfaces.IFieldSimpleContent"
for=".interfaces.IFieldSimpleContent"
name="widgetoverride.html"
permission="zope2.Public"
>
<widget
field="description"
class="zope.app.form.browser.TextAreaWidget"
/>
</browser:editform>
<five:traversable class=".helpers.FiveTraversableFolder" />
<browser:addform
schema=".interfaces.IFieldSimpleContent"
content_factory=".simplecontent.FieldSimpleContent"
name="addfieldcontent.html"
permission="zope2.Public"
/>
<browser:addform
schema=".interfaces.IFieldSimpleContent"
content_factory=".simplecontent.FieldSimpleContent"
name="addwidgetoverride.html"
permission="zope2.Public">
<widget
field="description"
class="zope.app.form.browser.TextAreaWidget"
/>
</browser:addform>
<browser:addform
schema=".interfaces.IFieldSimpleContent"
content_factory=".simplecontent.FieldSimpleContent"
name="protectedaddform.html"
permission="zope2.ViewManagementScreens"
/>
<!-- stuff that we'll override in overrides.zcml -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
attribute="eagle"
name="overridden_view"
permission="zope2.Public"
/>
<adapter
for=".interfaces.IOrigin"
provides=".interfaces.IDestination"
factory=".classes.OriginalAdapter"
/>
<!-- browser:page directives with new style classes are ignored -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.NewStyleClass"
name="invalid_page"
attribute="method"
permission="zope2.Public"
/>
<!-- browser menu support -->
<browser:menu
id="testmenu"
title="Test menu" />
<browser:menuItem
for=".interfaces.ISimpleContent"
menu="testmenu"
title="Test Menu Item"
action="seagull.html"
description="This is a test menu item"
permission="zope2.Public"
/>
<browser:menuItems
for=".interfaces.ISimpleContent"
menu="testmenu">
<menuItem
title="Test Menu Item 2"
action="parakeet.html"
description="This is a test menu item"
permission="zope2.Public"
/>
<menuItem
title="Test Menu Item 3"
action="falcon.html"
description="This is a test menu item"
permission="zope2.Public"
/>
</browser:menuItems>
<!-- page in a menu -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
template="parakeet.pt"
name="parakeet_beyond.html"
permission="zope2.ViewManagementScreens"
title="A page based entry"
menu="testmenu"
/>
<browser:editform
schema=".interfaces.IFieldSimpleContent"
for=".interfaces.IFieldSimpleContent"
name="edit2.html"
permission="zope2.Public"
title="An edit form based entry"
menu="testmenu"
/>
<!-- register size adapters -->
<five:sizable
class=".simplecontent.SimpleContent"
/>
<five:sizable
class=".fancycontent.FancyContent"
/>
<adapter
for=".interfaces.ISimpleContent"
provides="zope.app.size.interfaces.ISized"
factory=".size.SimpleContentSize"
/>
<adapter
for=".interfaces.IFancyContent"
provides="zope.app.size.interfaces.ISized"
factory=".size.FancyContentSize"
/>
<!-- subscribe to all events -->
<five:sendEvents
class=".simplecontent.SimpleContent"
/>
<subscriber
factory=".subscriber.objectEventSubscriber"
for="zope.app.event.interfaces.IObjectEvent"
/>
<subscriber
factory=".subscriber.objectMovedEventSubscriber"
for="zope.app.container.interfaces.IObjectMovedEvent"
/>
<subscriber
factory=".subscriber.objectAddedEventSubscriber"
for="zope.app.container.interfaces.IObjectAddedEvent"
/>
<subscriber
factory=".subscriber.objectCopiedEventSubscriber"
for="zope.app.event.interfaces.IObjectCopiedEvent"
/>
<subscriber
factory=".subscriber.objectRemovedEventSubscriber"
for="zope.app.container.interfaces.IObjectRemovedEvent"
/>
<!-- Testing the vocabulary directive -->
<vocabulary
name="aVocabulary"
factory="zope.schema.tests.test_vocabulary.SampleVocabulary"
/>
<!-- testing that products meta.zcml statements are picked up. -->
<five:parrot
class=".metaconfigure.NorwegianBlue"
name="Polly"
/>
<!-- as new style classes are ignored, zope.app.form.browser
can be imported -->
<include package="zope.app.form.browser"/>
</configure>
<p tal:content="python:context.mymethod()">Replaced</p>
\ No newline at end of file
<p tal:content="context/mymethod">Replaced</p>
\ No newline at end of file
<ul>
<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="item"/>
</ul>
\ No newline at end of file
<html metal:use-macro="context/@@fivetest_macros/birdmacro"><head><title>bird macro</title></head><body><metal:block fill-slot="color">green</metal:block><metal:block fill-slot="birdinfo"><img alt="" src="" tal:attributes="src context/++resource++pattern.png" /></metal:block></body></html>
<html metal:use-macro="views/bird.pt/birdmacro"><metal:block fill-slot="color">gray</metal:block></html>
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Event test fixtures
$Id: subscriber.py 12915 2005-05-31 10:23:19Z philikon $
"""
class EventCatcher:
def __init__(self):
self._events = []
def subscriber(self, event):
self._events.append(event)
def getEvents(self):
return self._events
def clear(self):
self._events = []
objectEventCatcher = EventCatcher()
objectEventSubscriber = objectEventCatcher.subscriber
objectMovedEventCatcher = EventCatcher()
objectMovedEventSubscriber = objectMovedEventCatcher.subscriber
objectAddedEventCatcher = EventCatcher()
objectAddedEventSubscriber = objectAddedEventCatcher.subscriber
objectCopiedEventCatcher = EventCatcher()
objectCopiedEventSubscriber = objectCopiedEventCatcher.subscriber
objectRemovedEventCatcher = EventCatcher()
objectRemovedEventSubscriber = objectRemovedEventCatcher.subscriber
def clear():
objectEventCatcher.clear()
objectMovedEventCatcher.clear()
objectAddedEventCatcher.clear()
objectCopiedEventCatcher.clear()
objectRemovedEventCatcher.clear()
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<redefinePermission from="zope2.Public" to="zope.Public" />
<include file="meta.zcml" />
<include file="configure.zcml" />
<includeOverrides file="overrides.zcml" />
</configure>
<h1 tal:replace="structure here/manage_page_header">Header</h1>
<h2 tal:define="form_title string:Add Field Simple Content"
tal:replace="structure here/manage_form_title">Form Title</h2>
<p class="form-help">
Add Field Simple Content
</p>
<form action="manage_addFieldSimpleContent" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit_add"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<h1 tal:replace="structure here/manage_page_footer">Footer</h1>
<h1 tal:replace="structure here/manage_page_header">Header</h1>
<h2 tal:define="form_title string:Add Simple Content"
tal:replace="structure here/manage_form_title">Form Title</h2>
<p class="form-help">
Add Simple Content
</p>
<form action="manage_addSimpleContent" method="post">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Title
</div>
</td>
<td align="left" valign="top">
<input type="text" name="title" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit_add"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<h1 tal:replace="structure here/manage_page_footer">Footer</h1>
#
# Runs all tests in the current directory
#
# Execute like:
# python runalltests.py
#
# Alternatively use the testrunner:
# python /path/to/Zope/utilities/testrunner.py -qa
#
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
import unittest
TestRunner = unittest.TextTestRunner
suite = unittest.TestSuite()
tests = os.listdir(os.curdir)
tests = [n[:-3] for n in tests if n.startswith('test') and n.endswith('.py')]
for test in tests:
m = __import__(test)
if hasattr(m, 'test_suite'):
suite.addTest(m.test_suite())
if __name__ == '__main__':
TestRunner().run(suite)
#TestRunner(descriptions=1, verbosity=2).run(suite)
...@@ -13,15 +13,14 @@ ...@@ -13,15 +13,14 @@
############################################################################## ##############################################################################
""" Unit tests for Z2 -> Z3 bridge utilities. """ Unit tests for Z2 -> Z3 bridge utilities.
$Id$ $Id: test_bridge.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os, sys import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
def test_suite(): def test_suite():
from Testing.ZopeTestCase import installProduct, ZopeDocFileSuite from Testing.ZopeTestCase import ZopeDocFileSuite
installProduct('Five')
return ZopeDocFileSuite('bridge.txt', package="Products.Five.tests") return ZopeDocFileSuite('bridge.txt', package="Products.Five.tests")
if __name__ == '__main__': if __name__ == '__main__':
......
##############################################################################
#
# Copyright (c) 2004, 2005 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 the basic ZCML directives
$Id: test_directives.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_directives():
"""
Test ZCML directives
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
There isn't much to test here since the actual directive handlers
are either tested in other, more specific tests, or they're
already tested in Zope 3. We'll just do a symbolic test of
adapters and overrides of adapters here as well as registering
meta directives.
But first, we load the configuration file:
>>> import Products.Five.tests
>>> from Products.Five import zcml
>>> zcml.load_config('meta.zcml', Products.Five)
>>> zcml.load_config('directives.zcml', Products.Five.tests)
Now for some testing. Here we check whether the registered
adapter works:
>>> from Products.Five.tests.adapters import IAdapted, IDestination
>>> from Products.Five.tests.adapters import Adaptable, Origin
>>> obj = Adaptable()
>>> adapted = IAdapted(obj)
>>> adapted.adaptedMethod()
'Adapted: The method'
Now let's load some overriding ZCML statements:
>>> zcml.load_string(
... '''<includeOverrides
... package="Products.Five.tests"
... file="overrides.zcml" />''')
>>> origin = Origin()
>>> dest = IDestination(origin)
>>> dest.method()
'Overridden'
Clean up:
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
...@@ -13,148 +13,15 @@ ...@@ -13,148 +13,15 @@
############################################################################## ##############################################################################
"""Test events triggered by Five """Test events triggered by Five
$Id: test_event.py 12915 2005-05-31 10:23:19Z philikon $ $Id: test_event.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os, sys import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import *
import transaction
from Products.Five.tests.products.FiveTest.subscriber import clear
from Products.Five.tests.products.FiveTest.subscriber import objectEventCatcher, \
objectAddedEventCatcher, objectMovedEventCatcher, \
objectCopiedEventCatcher, objectRemovedEventCatcher
from Products.Five.tests.products.FiveTest.simplecontent import manage_addSimpleContent
from Products.Five.tests.products.FiveTest.helpers import manage_addNoVerifyPasteFolder
class EventTest(FiveTestCase):
def afterSetUp(self):
manage_addNoVerifyPasteFolder(self.folder, 'npvf')
self.folder = self.folder.npvf
uf = self.folder.acl_users
uf._doAddUser('manager', 'r00t', ['Manager'], [])
self.login('manager')
self.setPermissions(
standard_permissions + ['Copy or Move'], 'Manager')
# clear all events
clear()
def test_added_event(self):
manage_addSimpleContent(self.folder, 'foo', 'Foo')
foo = self.folder.foo
events = objectEventCatcher.getEvents()
self.assertEquals(1, len(events))
self.assertEquals(foo.getPhysicalPath(),
events[0].object.getPhysicalPath())
events = objectAddedEventCatcher.getEvents()
self.assertEquals(1, len(events))
self.assertEquals(foo.getPhysicalPath(),
events[0].object.getPhysicalPath())
self.assertEquals(foo.aq_parent.getPhysicalPath(),
events[0].newParent.getPhysicalPath())
def test_moved_event(self):
manage_addSimpleContent(self.folder, 'foo', 'Foo')
# somehow we need to at least commit a subtransaction to make
# renaming succeed
transaction.commit(1)
self.folder.manage_renameObject('foo', 'bar')
bar = self.folder.bar
events = objectEventCatcher.getEvents()
self.assertEquals(3, len(events))
# will have new location so should still match
self.assertEquals(bar.getPhysicalPath(),
events[0].object.getPhysicalPath())
self.assertEquals(bar.getPhysicalPath(),
events[1].object.getPhysicalPath())
# removed event
self.assertEquals('foo',
events[1].oldName)
self.assertEquals(None,
events[1].newName)
# moved event
self.assertEquals('foo',
events[2].oldName)
self.assertEquals('bar',
events[2].newName)
self.assertEquals(self.folder.getPhysicalPath(),
events[2].oldParent.getPhysicalPath())
self.assertEquals(self.folder.getPhysicalPath(),
events[2].oldParent.getPhysicalPath())
def test_moved_event2(self):
# move from one folder to another
manage_addNoVerifyPasteFolder(self.folder, 'folder1', 'Folder1')
folder1 = self.folder.folder1
manage_addNoVerifyPasteFolder(self.folder, 'folder2', 'Folder2')
folder2 = self.folder.folder2
manage_addSimpleContent(folder1, 'foo', 'Foo')
foo = folder1.foo
# need to trigger subtransaction before copy/paste can work
transaction.commit(1)
cb = folder1.manage_cutObjects(['foo'])
folder2.manage_pasteObjects(cb)
newfoo = folder2.foo
events = objectMovedEventCatcher.getEvents()
self.assertEquals(3, len(events))
self.assertEquals(1, len(objectAddedEventCatcher.getEvents()))
# removed event
self.assertEquals(folder1.getPhysicalPath(),
events[1].oldParent.getPhysicalPath())
self.assertEquals(None,
events[1].newParent)
# moved event
self.assertEquals(newfoo.getPhysicalPath(),
events[2].object.getPhysicalPath())
self.assertEquals(folder1.getPhysicalPath(),
events[2].oldParent.getPhysicalPath())
self.assertEquals(folder2.getPhysicalPath(),
events[2].newParent.getPhysicalPath())
self.assertEquals('foo',
events[2].oldName)
self.assertEquals('foo',
events[2].newName)
def test_copied_event(self):
manage_addSimpleContent(self.folder, 'foo', 'Foo')
manage_addNoVerifyPasteFolder(self.folder, 'folder1')
folder1 = self.folder.folder1
# need to trigger subtransaction before copy/paste can work
transaction.commit(1)
cb = self.folder.manage_copyObjects(['foo'])
folder1.manage_pasteObjects(cb)
foo_copy = folder1.foo
events = objectCopiedEventCatcher.getEvents()
self.assertEquals(1, len(events))
self.assertEquals(foo_copy.getPhysicalPath(),
events[0].object.getPhysicalPath())
events = objectAddedEventCatcher.getEvents()
self.assertEquals(2, len(events))
self.assertEquals(foo_copy.getPhysicalPath(),
events[1].object.getPhysicalPath())
def test_removed_event(self):
manage_addSimpleContent(self.folder, 'foo', 'Foo')
self.folder.manage_delObjects(['foo'])
events = objectRemovedEventCatcher.getEvents()
self.assertEquals(1, len(events))
self.assertEquals('foo', events[0].object.id)
def test_suite(): def test_suite():
from unittest import TestSuite, makeSuite from Testing.ZopeTestCase import ZopeDocFileSuite
suite = TestSuite() return ZopeDocFileSuite('event.txt', package="Products.Five.tests")
suite.addTest(makeSuite(EventTest))
return suite
if __name__ == '__main__': if __name__ == '__main__':
framework() framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Five tests.
$Id: test_five.py 12915 2005-05-31 10:23:19Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import *
import re
import glob
import unittest
import zope
from zope.interface import directlyProvides, Interface, implements
from zope.component import getViewProviding
from zope.schema import Choice, TextLine
from zope.app.form.interfaces import IInputWidget
from zope.app.traversing.browser.interfaces import IAbsoluteURL
from zope.app.traversing.interfaces import IContainmentRoot
from Products.Five.tests.products.FiveTest.classes import Adaptable, Origin
from Products.Five.tests.products.FiveTest.interfaces import IAdapted, IDestination
from Products.Five.tests.products.FiveTest.browser import SimpleContentView
from Products.Five.resource import Resource, PageTemplateResource
from Products.Five.traversable import FakeRequest
from Products.Five.fiveconfigure import classDefaultViewable
from OFS.Traversable import Traversable
from Products.Five.tests.products.FiveTest.simplecontent import manage_addSimpleContent
from Products.Five.tests.products.FiveTest.simplecontent import manage_addCallableSimpleContent
from Products.Five.tests.products.FiveTest.simplecontent import manage_addIndexSimpleContent
from Products.Five.tests.products.FiveTest.fancycontent import manage_addFancyContent
from Products.Five.tests.products import FiveTest
_prefix = os.path.dirname(FiveTest.__file__)
dir_resource_names = [os.path.basename(r)
for r in (glob.glob('%s/*.png' % _prefix) +
glob.glob('%s/*.pt' % _prefix) +
glob.glob('%s/[a-z]*.py' % _prefix) +
glob.glob('%s/*.css' % _prefix))]
def normalize_html(s):
s = re.sub(r"[ \t\n]+", "", s)
return s
class FiveTest(FiveTestCase):
def afterSetUp(self):
manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
manage_addCallableSimpleContent(self.folder, 'testcall', 'TestCall')
manage_addIndexSimpleContent(self.folder, 'testindex', 'TestIndex')
uf = self.folder.acl_users
uf._doAddUser('manager', 'r00t', ['Manager'], [])
self.login('manager')
def test_adapters(self):
obj = Adaptable()
adapted = IAdapted(obj)
self.assertEquals(
"Adapted: The method",
adapted.adaptedMethod())
def test_adapters2(self):
obj = Adaptable()
adapted = IAdapted(obj)
self.assertEquals(
"Adapted: The method",
adapted.adaptedMethod())
def test_overrides(self):
origin = Origin()
dest = IDestination(origin)
self.assertEquals(dest.method(), "Overridden")
view = self.folder.unrestrictedTraverse('testoid/overridden_view')
self.assertEquals(view(), "The mouse has been eaten by the eagle")
def test_attribute_view(self):
view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
self.assert_(isinstance(view, SimpleContentView))
self.assertEquals('The eagle has landed', view())
def test_existing_docstrings_arent_modified(self):
view = self.folder.unrestrictedTraverse('testoid/eagle.txt')
self.assertEquals(view.eagle.__doc__, SimpleContentView.eagle.__doc__)
def test_pages_view(self):
view = self.folder.unrestrictedTraverse('testoid/eagle-page.txt')
self.assert_(isinstance(view, SimpleContentView))
self.assertEquals('The eagle has landed', view())
view = self.folder.unrestrictedTraverse('testoid/mouse-page.txt')
self.assert_(isinstance(view, SimpleContentView))
self.assertEquals('The mouse has been eaten by the eagle', view())
def test_template_view(self):
view = self.folder.unrestrictedTraverse('testoid/falcon.html')
self.assert_(isinstance(view, SimpleContentView))
self.assertEquals(u'<p>The falcon has taken flight</p>\n', view())
def test_template_view_without_class(self):
view = self.folder.unrestrictedTraverse('testoid/owl.html')
self.assertEquals(u'<p>2</p>\n', view())
def test_template_view_context(self):
view = self.folder.unrestrictedTraverse('testoid/flamingo.html')
self.assertEquals(u'<p>Hello world</p>\n', view())
def test_template_view_context_path(self):
view = self.folder.unrestrictedTraverse('testoid/flamingo2.html')
self.assertEquals(u'<p>Hello world</p>\n', view())
def test_template_view_resource_traversal(self):
view = self.folder.unrestrictedTraverse('testoid/parakeet.html')
expected = """\
<html>
<head>
<title>bird macro</title>
</head>
<body>
Color: green
<img alt="" src="http://nohost/test_folder_1_/testoid/++resource++pattern.png" />
</body>
</html>
"""
expected = normalize_html(expected)
self.assertEquals(expected, normalize_html(view()))
def test_view_backwards_compatibility(self):
old_view = self.folder.unrestrictedTraverse('testoid/direct')
self.assertEquals('Direct traversal worked', old_view())
def test_zpt_things(self):
view = self.folder.unrestrictedTraverse('testoid/condor.html')
expected = """\
<p>Hello world</p>
<p>The eagle has landed</p>
<p>Hello world</p>
"""
self.assertEquals(expected, view())
def test_repeat(self):
view = self.folder.unrestrictedTraverse('testoid/ostrich.html')
expected = """\
<ul>
<li>Alpha</li>
<li>Beta</li>
<li>Gamma</li>
</ul>
"""
self.assertEquals(expected, view())
def test_macro_access(self):
view = self.folder.unrestrictedTraverse('testoid/seagull.html')
self.assertEquals('<html><head><title>bird macro</title></head><body>Color: gray</body></html>\n', view())
def test_repeat_iterator(self):
view = self.folder.unrestrictedTraverse('testoid/ostrich2.html')
expected = """\
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
</ul>
"""
self.assertEquals(expected, view())
def test_tales_traversal(self):
view = self.folder.unrestrictedTraverse('testoid/tales_traversal.html')
expected = """\
<p>testoid</p>
<p>test_folder_1_</p>
"""
self.assertEquals(expected, view())
def test_zpt_security(self):
self.logout()
view = self.folder.unrestrictedTraverse('testoid/security.html')
expected = """\
<div>NoneType</div>
<div>smtpd</div>
"""
self.assertEquals(expected, view())
def test_template_resource(self):
resource = self.folder.unrestrictedTraverse('testoid/++resource++cockatiel.html')
self.assert_(isinstance(resource, Resource))
expected = 'http://nohost/test_folder_1_/testoid/++resource++cockatiel.html'
self.assertEquals(expected, resource())
def test_file_resource(self):
resource = self.folder.unrestrictedTraverse('testoid/++resource++style.css')
self.assert_(isinstance(resource, Resource))
expected = 'http://nohost/test_folder_1_/testoid/++resource++style.css'
self.assertEquals(expected, resource())
def test_image_resource(self):
resource = self.folder.unrestrictedTraverse('testoid/++resource++pattern.png')
expected = 'http://nohost/test_folder_1_/testoid/++resource++pattern.png'
self.assert_(isinstance(resource, Resource))
self.assertEquals(expected, resource())
def test_resource_directory(self):
base = 'testoid/++resource++fivetest_resources/%s'
base_url = 'http://nohost/test_folder_1_/' + base
abs_url = self.folder.unrestrictedTraverse(base % '')()
self.assertEquals(abs_url + '/', base_url % '')
for r in dir_resource_names:
resource = self.folder.unrestrictedTraverse(base % r)
self.assert_(isinstance(resource, Resource))
# PageTemplateResource's __call__ renders the template
if not isinstance(resource, PageTemplateResource):
self.assertEquals(resource(), base_url % r)
def test_breadcrumbs(self):
view = self.folder.unrestrictedTraverse('testoid/@@absolute_url')
expected = (
{'url': 'http://nohost', 'name': ''},
{'url': 'http://nohost/test_folder_1_', 'name': 'test_folder_1_'},
{'url': 'http://nohost/test_folder_1_/testoid', 'name': 'testoid'})
self.assertEquals(expected, view.breadcrumbs())
def test_virtualhost_breadcrumbs(self):
# Get REQUEST in shape
request = self.request = self.app.REQUEST
request['PARENTS'] = [self.folder.test_folder_1_]
request.setServerURL(
protocol='http', hostname='foo.bar.com', port='80')
request.setVirtualRoot('')
view = self.folder.unrestrictedTraverse('testoid/@@absolute_url')
expected = (
{'url': 'http://foo.bar.com', 'name': 'test_folder_1_'},
{'url': 'http://foo.bar.com/testoid', 'name': 'testoid'})
self.assertEquals(expected, view.breadcrumbs())
def test_containement_root_breadcrumbs(self):
# Should stop breadcrumbs from crumbing
directlyProvides(self.folder, IContainmentRoot)
view = self.folder.unrestrictedTraverse('testoid/@@absolute_url')
expected = (
{'url': 'http://nohost/test_folder_1_', 'name': 'test_folder_1_'},
{'url': 'http://nohost/test_folder_1_/testoid', 'name': 'testoid'})
self.assertEquals(expected, view.breadcrumbs())
def test_standard_macros(self):
view = self.folder.unrestrictedTraverse('testoid/@@fivetest_macros')
self.assertRaises(KeyError, view.__getitem__, 'non-existing-macro')
self.failUnless(view['birdmacro'])
self.failUnless(view['dogmacro'])
# Test aliases
self.failUnless(view['flying'])
self.failUnless(view['walking'])
self.assertEquals(view['flying'], view['birdmacro'])
self.assertEquals(view['walking'], view['dogmacro'])
# Test traversal
base = 'testoid/@@fivetest_macros/%s'
for macro in ('birdmacro', 'dogmacro',
'flying', 'walking'):
view = self.folder.unrestrictedTraverse(base % macro)
self.failUnless(view)
def test_unrestrictedTraverse_non_existing(self):
self.assertRaises(AttributeError, self.folder.unrestrictedTraverse,
'testoid/@@invalid_page')
def test_get_widgets_for_schema_fields(self):
salutation = Choice(title=u'Salutation', values=("Mr.", "Mrs.", "Captain", "Don"))
contactname = TextLine(title=u'Name')
request = FakeRequest()
salutation = salutation.bind(request)
contactname = contactname.bind(request)
view1 = getViewProviding(contactname, IInputWidget, request)
self.assertEquals(view1.__class__, zope.app.form.browser.textwidgets.TextWidget)
view2 = getViewProviding(salutation, IInputWidget, request)
self.assertEquals(view2.__class__, zope.app.form.browser.itemswidgets.DropdownWidget)
# Disabled __call__ overriding for now. Causes more trouble
# than it fixes.
# def test_existing_call(self):
# view = self.folder.unrestrictedTraverse('testcall')
# self.assertEquals("Default __call__ called", view())
class PublishTest(Functional, FiveTestCase):
"""Test a few publishing features"""
def afterSetUp(self):
manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
manage_addCallableSimpleContent(self.folder, 'testcall', 'TestCall')
manage_addIndexSimpleContent(self.folder, 'testindex', 'TestIndex')
uf = self.folder.acl_users
uf._doAddUser('viewer', 'secret', [], [])
uf._doAddUser('manager', 'r00t', ['Manager'], [])
def test_no_doc_string(self):
for view_name in ['nodoc-function', 'nodoc-method', 'nodoc-object']:
response = self.publish('/test_folder_1_/testoid/%s' % view_name)
self.assertEquals("No docstring", response.getBody())
def test_fallback_raises_notfound(self):
# If we return None in __fallback_traverse__, this test passes
# but for the wrong reason: None doesn't have a docstring so
# BaseRequest raises NotFoundError. A functional test would be
# perfect here.
response = self.publish('/test_folder_1_/testoid/doesntexist')
self.assertEquals(404, response.getStatus())
def test_existing_bobo_traverse(self):
manage_addFancyContent(self.folder, 'fancy', '')
# check if the old bobo_traverse method can still kick in
response = self.publish('/test_folder_1_/fancy/something-else')
self.assertEquals('something-else', response.getBody())
# check if z3-based view lookup works
response = self.publish('/test_folder_1_/fancy/fancy')
self.assertEquals("Fancy, fancy", response.getBody())
def test_publish_image_resource(self):
url = '/test_folder_1_/testoid/++resource++pattern.png'
response = self.publish(url, basic='manager:r00t')
self.assertEquals(200, response.getStatus())
def test_publish_file_resource(self):
url = '/test_folder_1_/testoid/++resource++style.css'
response = self.publish(url, basic='manager:r00t')
self.assertEquals(200, response.getStatus())
def test_publish_template_resource(self):
url = '/test_folder_1_/testoid/++resource++cockatiel.html'
response = self.publish(url, basic='manager:r00t')
self.assertEquals(200, response.getStatus())
# Disabled __call__ overriding for now. Causes more trouble
# than it fixes.
# def test_existing_call(self):
# response = self.publish('/test_folder_1_/testcall')
# self.assertEquals("Default __call__ called", response.getBody())
def test_publish_resource_directory(self):
base_url = '/test_folder_1_/testoid/++resource++fivetest_resources/%s'
for r in dir_resource_names:
if r.endswith('.pt'):
# page templates aren't guaranteed to render
continue
response = self.publish(base_url % r, basic='manager:r00t')
self.assertEquals(200, response.getStatus())
def test_existing_index(self):
response = self.publish('/test_folder_1_/testindex')
self.assertEquals("Default index_html called", response.getBody())
def test_default_view(self):
response = self.publish('/test_folder_1_/testoid', basic='manager:r00t')
self.assertEquals("The eagle has landed", response.getBody())
def test_pages_from_directory(self):
response = self.publish('/test_folder_1_/testoid/dirpage1')
self.assert_('page 1' in response.getBody())
response = self.publish('/test_folder_1_/testoid/dirpage2')
self.assert_('page 2' in response.getBody())
class IRecurse(Interface):
pass
class Recurse(Traversable):
implements(IRecurse)
def view(self):
return self()
def __call__(self):
return 'foo'
classDefaultViewable(Recurse)
class RecursionTest(unittest.TestCase):
def setUp(self):
self.ob = Recurse()
def test_recursive_call(self):
from zope.app import zapi
from zope.publisher.interfaces.browser import IBrowserRequest
pres = zapi.getGlobalService('Presentation')
type = IBrowserRequest
pres.setDefaultViewName(IRecurse, type, 'view')
self.assertEquals(self.ob.view(), 'foo')
self.assertEquals(self.ob(), 'foo')
from zope.app.publisher.browser.globalbrowsermenuservice import \
globalBrowserMenuService
class MenuTest(FiveTestCase):
def afterSetUp(self):
manage_addIndexSimpleContent(self.folder, 'test', 'Test')
def test_menu(self):
request = FakeRequest()
# XXX not sure why we need this..
request.getURL = lambda: 'http://www.infrae.com'
menu = globalBrowserMenuService.getMenu('testmenu',
self.folder.test,
request)
self.assertEquals(3, len(menu))
# sort menu items by title so we get a stable testable result
menu.sort(lambda x, y: cmp(x['title'], y['title']))
self.assertEquals('Test Menu Item', menu[0]['title'])
self.assertEquals('seagull.html', menu[0]['action'])
self.assertEquals('Test Menu Item 2', menu[1]['title'])
self.assertEquals('parakeet.html', menu[1]['action'])
class SizeTest(FiveTestCase):
def test_no_get_size_on_original(self):
manage_addSimpleContent(self.folder, 'simple', 'Simple')
obj = self.folder.simple
self.assertEquals(obj.get_size(), 42)
def test_get_size_on_original_and_fallback(self):
manage_addFancyContent(self.folder, 'fancy', 'Fancy')
obj = self.folder.fancy
self.assertEquals(obj.get_size(), 43)
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(RecursionTest))
suite.addTest(makeSuite(FiveTest))
suite.addTest(makeSuite(PublishTest))
suite.addTest(makeSuite(MenuTest))
suite.addTest(makeSuite(SizeTest))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Unit tests for the i18n framework
$Id: test_i18n.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_directive():
"""
Test the i18n directive
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
First, we need to register the ZCML directive:
>>> import zope.app.i18n
>>> from Products.Five import zcml
>>> zcml.load_config('meta.zcml', zope.app.i18n)
Let's register the gettext locales using the ZCML directive:
>>> configure_zcml = '''
... <configure xmlns="http://namespaces.zope.org/zope"
... xmlns:i18n="http://namespaces.zope.org/i18n"
... package="Products.Five.tests">
... <i18n:registerTranslations directory="locales" />
... </configure>'''
>>> zcml.load_string(configure_zcml)
Now, take an arbitrary message id from that domain:
>>> from zope.i18nmessageid import MessageIDFactory
>>> from zope.i18n import translate
>>> _ = MessageIDFactory('fivetest')
>>> msg = _(u'explicit-msg', u'This is an explicit message')
As you can see, both the default functionality and translation to
German work:
>>> translate(msg)
u'This is an explicit message'
>>> translate(msg, target_language='de')
u'Dies ist eine explizite Nachricht'
Clean up:
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 import conflicts
$Id: test_import_conflicts.py 18150 2005-10-04 16:19:49Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def testImportConflicts():
"""
In a Five environment, importing Zope 3 packages that would use
interfaces from the Zope 3 transaction module would lead to an
error, because of the monkey patching. The zope.app.mail package
makes use of transaction interfaces, for example the following
class:
>>> from zope.app.mail.delivery import QueueProcessorThread
Note that this only concerns Zope 2.7 and Zope X3 3.0.
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
...@@ -13,176 +13,227 @@ ...@@ -13,176 +13,227 @@
############################################################################## ##############################################################################
"""Test security induced by ZCML """Test security induced by ZCML
$Id: test_security.py 12915 2005-05-31 10:23:19Z philikon $ $Id: test_security.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os, sys import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import * from zope.interface import Interface, implements
from AccessControl import ClassSecurityInfo
import zope.security
from zope.component import getView class IDummy(Interface):
from zope.testing.cleanup import CleanUp """Just a marker interface"""
import Products.Five.security class Dummy1:
from Products.Five import zcml implements(IDummy)
from Products.Five.traversable import FakeRequest def foo(self): pass
from Products.Five.security import clearSecurityInfo, newInteraction def bar(self): pass
from Products.Five.tests.dummy import Dummy1, Dummy2 def baz(self): pass
from Globals import InitializeClass def keg(self): pass
def wot(self): pass
class Dummy2(Dummy1):
security = ClassSecurityInfo()
security.declarePublic('foo')
security.declareProtected('View management screens', 'bar')
security.declarePrivate('baz')
security.declareProtected('View management screens', 'keg')
def test_security_equivalence():
"""This test demonstrates that the traditional declarative security of
Zope 2 can be replaced by ZCML statements without any loss of
information.
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
We start out with two classes, ``Dummy1`` and ``Dummy2``. They
are identical in every way, except that ``Dummy2`` has security
declarations and ``Dummy1`` does not. Before we do anything, none
of them have security access controls:
>>> from Products.Five.tests.test_security import Dummy1, Dummy2
>>> hasattr(Dummy1, '__ac_permissions__')
False
>>> hasattr(Dummy2, '__ac_permissions__')
False
Before we can make security declarations through ZCML, we need to
register the directive and the permission:
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config('meta.zcml', Products.Five)
>>> zcml.load_config('permissions.zcml', Products.Five)
Now we initialize the security for ``Dummy2`` and provide some
ZCML declarations for ``Dummy1``:
>>> configure_zcml = '''
... <configure xmlns="http://namespaces.zope.org/zope">
... <content class="Products.Five.tests.test_security.Dummy1">
... <allow attributes="foo" />
... <!--deny attributes="baz" /--> <!-- XXX not yet supported -->
... <require attributes="bar keg"
... permission="zope2.ViewManagementScreens"
... />
... </content>
... </configure>
... '''
>>> zcml.load_string(configure_zcml)
>>> from Globals import InitializeClass
>>> InitializeClass(Dummy2)
Now we compare their access controls:
>>> ac1 = getattr(Dummy1, '__ac_permissions__')
>>> ac2 = getattr(Dummy2, '__ac_permissions__')
>>> ac1 == ac2
True
Now we look at the individual permissions:
>>> bar_roles1 = getattr(Dummy1, 'bar__roles__').__of__(Dummy1)
>>> bar_roles1.__of__(Dummy1)
('Manager',)
>>> keg_roles1 = getattr(Dummy1, 'keg__roles__').__of__(Dummy1)
>>> keg_roles1.__of__(Dummy1)
('Manager',)
>>> foo_roles1 = getattr(Dummy1, 'foo__roles__')
>>> foo_roles1 is None
True
>>> # XXX Not yet supported.
>>> # baz_roles1 = getattr(Dummy1, 'baz__roles__')
>>> # baz_roles1
()
>>> bar_roles2 = getattr(Dummy2, 'bar__roles__').__of__(Dummy2)
>>> bar_roles2.__of__(Dummy2)
('Manager',)
def assertRolesEqual(actual, expect): >>> keg_roles2 = getattr(Dummy2, 'keg__roles__').__of__(Dummy2)
if actual: >>> keg_roles2.__of__(Dummy2)
# filter out embedded permissions, which appear when ('Manager',)
# verbose security is enabled
filtered = [r for r in actual if not r.endswith('_Permission')]
if isinstance(actual, tuple):
actual = tuple(filtered)
else:
actual = filtered
if actual != expect:
raise AssertionError('%s != %s' % (repr(actual), repr(expect)))
>>> foo_roles2 = getattr(Dummy2, 'foo__roles__')
>>> foo_roles2 is None
True
class PageSecurityTest(FiveTestCase): >>> baz_roles2 = getattr(Dummy2, 'baz__roles__')
>>> baz_roles2
()
def test_page_security(self): Before we end we should clean up after ourselves:
decl = """
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<browser:page >>> from Products.Five.security import clearSecurityInfo
for="Products.Five.tests.dummy.IDummy" >>> clearSecurityInfo(Dummy1)
class="Products.Five.tests.dummy.DummyView" >>> clearSecurityInfo(Dummy2)
attribute="foo"
name="test_page_security"
permission="zope2.ViewManagementScreens"
/>
</configure> >>> tearDown()
""" """
zcml.load_string(decl)
request = FakeRequest()
# Wrap into an acquisition so that imPermissionRole objects
# can be evaluated.
view = getView(Dummy1(), 'test_page_security', request)
ac = getattr(view, '__ac_permissions__') def test_checkPermission():
# It's protecting the object with the permission, and not the """
# attribute, so we get ('',) instead of ('foo',). Test checkPermission
ex_ac = (('View management screens', ('',)),)
self.assertEquals(ac, ex_ac)
# Wrap into an acquisition so that imPermissionRole objects >>> from zope.app.tests.placelesssetup import setUp, tearDown
# can be evaluated. __roles__ is a imPermissionRole object. >>> setUp()
view = view.__of__(self.folder)
view_roles = getattr(view, '__roles__', None)
self.failIf(view_roles is None)
self.failIf(view_roles == ())
assertRolesEqual(view_roles, ('Manager',))
Zope 3 has a function zope.security.checkPermission which provides
an easy way of checking whether the currently authenticated user
has the permission to access an object. The function delegates to
the security policy's checkPermission() method.
class SecurityEquivalenceTest(FiveTestCase): Five has the same function, Five.security.checkPermission, but in
a Zope2-compatible implementation. It too uses the currently
active security policy of Zope 2 for the actual permission
checking.
def setUp(self): >>> import Products.Five
self.dummy1 = Dummy1 >>> from Products.Five import zcml
self.dummy2 = Dummy2 >>> zcml.load_config('meta.zcml', Products.Five)
>>> zcml.load_config('permissions.zcml', Products.Five)
def tearDown(self): In the following we want to test Five's checkPermission function.
clearSecurityInfo(self.dummy1) We do that by taking the test's folder and asserting several
clearSecurityInfo(self.dummy2) standard permissions. What we want to assure is that
checkPermission translates the Zope 2 permissions correctly,
especially the edge cases:
def test_equivalence(self): a) zope2.Public (which should always be available to everyone)
self.failIf(hasattr(self.dummy1, '__ac_permissions__'))
self.failIf(hasattr(self.dummy2, '__ac_permissions__'))
decl = """ >>> from Products.Five.security import checkPermission
<configure xmlns="http://namespaces.zope.org/zope"> >>> checkPermission('zope2.Public', self.folder)
True
<content b) zope2.Private (which should never available to anyone)
class="Products.Five.tests.dummy.Dummy1">
<allow attributes="foo" /> >>> checkPermission('zope.Private', self.folder)
False
>>> checkPermission('zope2.Private', self.folder)
False
<!-- XXX not yet supported Any other standard Zope 2 permission will also resolve correctly:
<deny attributes="baz" />
-->
<require attributes="bar keg" >>> checkPermission('zope2.AccessContentsInformation', self.folder)
permission="zope2.ViewManagementScreens" True
/>
</content> Invalid permissions will obviously result in a negative response:
</configure>
"""
zcml.load_string(decl)
InitializeClass(self.dummy2)
ac1 = getattr(self.dummy1, '__ac_permissions__') >>> checkPermission('notapermission', self.folder)
ac2 = getattr(self.dummy2, '__ac_permissions__') False
self.assertEquals(ac1, ac2)
bar_roles1 = getattr(self.dummy1, 'bar__roles__').__of__(self.dummy1)
assertRolesEqual(bar_roles1.__of__(self.dummy1), ('Manager',))
keg_roles1 = getattr(self.dummy1, 'keg__roles__').__of__(self.dummy1) In addition to using Five's ``checkPermission`` function directly,
assertRolesEqual(keg_roles1.__of__(self.dummy1), ('Manager',)) we also expect the same behaviour when we use Zope 3's
zope.security.checkPermission function. Code from within Zope 3
will use that and therefore it should work transparently. For
that to work, a new Five "interaction" needs to be started (the
old one from placelesssetup needs to be ended first):
foo_roles1 = getattr(self.dummy1, 'foo__roles__') >>> from zope.security.management import endInteraction
assertRolesEqual(foo_roles1, None) >>> endInteraction()
# XXX Not yet supported. >>> from Products.Five.security import newInteraction
# baz_roles1 = getattr(self.dummy1, 'baz__roles__') >>> newInteraction()
# self.assertEquals(baz_roles1, ())
bar_roles2 = getattr(self.dummy2, 'bar__roles__').__of__(self.dummy2) a) zope2.Public (which should always be available to everyone)
assertRolesEqual(bar_roles2.__of__(self.dummy2), ('Manager',))
keg_roles2 = getattr(self.dummy2, 'keg__roles__').__of__(self.dummy2) >>> from zope.security import checkPermission
assertRolesEqual(keg_roles2.__of__(self.dummy2), ('Manager',)) >>> checkPermission('zope2.Public', self.folder)
True
foo_roles2 = getattr(self.dummy2, 'foo__roles__') b) zope2.Private (which should never available to anyone)
assertRolesEqual(foo_roles2, None)
baz_roles2 = getattr(self.dummy2, 'baz__roles__') >>> checkPermission('zope.Private', self.folder)
assertRolesEqual(baz_roles2, ()) False
>>> checkPermission('zope2.Private', self.folder)
False
Any other standard Zope 2 permission will also resolve correctly:
class FiveCheckPermissionTest(FiveTestCase): >>> checkPermission('zope2.AccessContentsInformation', self.folder)
True
def afterSetUp(self): Invalid permissions will obviously result in a negative response:
self.checkPermission = Products.Five.security.checkPermission
def test_publicPermissionId(self): >>> checkPermission('notapermission', self.folder)
self.failUnless(self.checkPermission('zope2.Public', self.folder)) False
def test_privatePermissionId(self):
self.failIf(self.checkPermission('zope.Private', self.folder))
self.failIf(self.checkPermission('zope2.Private', self.folder))
def test_accessPermissionId(self): Clean up:
self.failUnless(self.checkPermission('zope2.AccessContentsInformation',
self.folder))
def test_invalidPermissionId(self): >>> tearDown()
self.failIf(self.checkPermission('notapermission', self.folder)) """
class Zope3CheckPermissionTest(FiveCheckPermissionTest):
def afterSetUp(self):
self.checkPermission = zope.security.checkPermission
newInteraction()
def test_suite(): def test_suite():
from unittest import TestSuite, makeSuite from Testing.ZopeTestCase import ZopeDocTestSuite
suite = TestSuite() return ZopeDocTestSuite()
suite.addTest(makeSuite(SecurityEquivalenceTest))
suite.addTest(makeSuite(PageSecurityTest))
suite.addTest(makeSuite(FiveCheckPermissionTest))
suite.addTest(makeSuite(Zope3CheckPermissionTest))
return suite
if __name__ == '__main__': if __name__ == '__main__':
framework() framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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 security from restricted Python interpreter
$Id: test_security2.py 12915 2005-05-31 10:23:19Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import *
from Products.Five.tests.products.FiveTest.helpers import manage_addFiveTraversableFolder
from AccessControl import getSecurityManager
from AccessControl import Unauthorized
import glob
from Products.Five.tests.products import FiveTest
_prefix = os.path.dirname(FiveTest.__file__)
dir_resource_names = [os.path.basename(r)
for r in (glob.glob('%s/*.png' % _prefix) +
glob.glob('%s/*.pt' % _prefix) +
glob.glob('%s/[a-z]*.py' % _prefix) +
glob.glob('%s/*.css' % _prefix))]
ViewManagementScreens = 'View management screens'
from Products.Five.tests.products.FiveTest.simplecontent import manage_addSimpleContent
class RestrictedPythonTest(FiveTestCase):
"""
Test whether code is really restricted
Kind permission from Plone to use this.
"""
def addPS(self, id, params='', body=''):
# clean up any 'ps' that's already here..
try:
self.folder._getOb(id)
self.folder.manage_delObjects([id])
except AttributeError:
pass # it's okay, no 'ps' exists yet
factory = self.folder.manage_addProduct['PythonScripts']
factory.manage_addPythonScript(id)
self.folder[id].ZPythonScript_edit(params, body)
def check(self, psbody):
self.addPS('ps', body=psbody)
try:
self.folder.ps()
except (ImportError, Unauthorized), e:
self.fail(e)
def checkUnauthorized(self, psbody):
self.addPS('ps', body=psbody)
try:
self.folder.ps()
except (AttributeError, Unauthorized):
pass
else:
self.fail("Authorized but shouldn't be")
view_names = [
'eagle.txt',
'falcon.html',
'owl.html',
'flamingo.html',
'flamingo2.html',
'condor.html',
'protectededitform.html']
public_view_names = [
'public_attribute_page',
'public_template_page',
'public_template_class_page']
resource_names = [
'cockatiel.html',
'style.css',
'pattern.png'
]
class SecurityTest(RestrictedPythonTest):
def afterSetUp(self):
manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
uf = self.folder.acl_users
uf._doAddUser('viewer', 'secret', [], [])
uf._doAddUser('manager', 'r00t', ['Manager'], [])
def test_no_permission(self):
self.login('viewer')
for view_name in view_names:
self.checkUnauthorized(
'context.restrictedTraverse("testoid/%s")()' % view_name)
def test_resource_no_permission(self):
self.login('viewer')
for resource in resource_names:
self.checkUnauthorized(
'context.restrictedTraverse("testoid/++resource++%s")()' %
resource)
def test_directory_resource_no_permission(self):
self.login('viewer')
base = 'testoid/++resource++fivetest_resources/%s'
for resource in dir_resource_names:
path = base % resource
self.checkUnauthorized(
'context.restrictedTraverse("%s")' % path)
def test_permission(self):
self.login('manager')
for view_name in view_names:
self.check(
'context.restrictedTraverse("testoid/%s")()' % view_name)
def test_resource_permission(self):
self.login('manager')
for resource in resource_names:
self.check(
'context.restrictedTraverse("testoid/++resource++%s")()' %
resource)
def test_directory_resource_permission(self):
self.login('manager')
base = 'testoid/++resource++fivetest_resources/%s'
for resource in dir_resource_names:
path = base % resource
self.check(
'context.restrictedTraverse("%s")' % path)
def test_public_permission(self):
self.logout()
for view_name in public_view_names:
self.check(
'context.restrictedTraverse("testoid/%s")()' % view_name)
def test_view_method_permission(self):
self.login('manager')
self.check(
'context.restrictedTraverse("testoid/eagle.method").eagle()')
class PublishTest(Functional, FiveTestCase):
"""A functional test for security actually involving the publisher.
"""
def afterSetUp(self):
manage_addSimpleContent(self.folder, 'testoid', 'Testoid')
uf = self.folder.acl_users
uf._doAddUser('viewer', 'secret', [], [])
uf._doAddUser('manager', 'r00t', ['Manager'], [])
def test_no_permission(self):
for view_name in view_names:
response = self.publish('/test_folder_1_/testoid/%s' % view_name,
basic='viewer:secret')
# we expect that we get a 401 Unauthorized
status = response.getStatus()
self.failUnless(status == 401, (status, 401, view_name))
def test_all_permissions(self):
permissions = self.folder.possible_permissions()
self.folder._addRole('Viewer')
self.folder.manage_role('Viewer', permissions)
self.folder.manage_addLocalRoles(
'viewer', ['Viewer'])
for view_name in view_names:
response = self.publish('/test_folder_1_/testoid/%s' % view_name,
basic='viewer:secret')
status = response.getStatus()
self.failUnless(status == 200, (status, 200, view_name))
def test_almost_all_permissions(self):
permissions = self.folder.possible_permissions()
permissions.remove(ViewManagementScreens)
self.folder._addRole('Viewer')
self.folder.manage_role('Viewer', permissions)
self.folder.manage_addLocalRoles(
'viewer', ['Viewer'])
for view_name in view_names:
response = self.publish('/test_folder_1_/testoid/%s' % view_name,
basic='viewer:secret')
# we expect that we get a 401 Unauthorized
status = response.getStatus()
self.failUnless(status == 401, (status, 401, view_name))
def test_manager_permission(self):
for view_name in view_names:
response = self.publish('/test_folder_1_/testoid/%s' % view_name,
basic='manager:r00t')
# we expect that we get a 200 Ok
self.assertEqual(response.getStatus(), 200)
def test_public_permission(self):
for view_name in public_view_names:
response = self.publish('/test_folder_1_/testoid/%s' % view_name)
status = response.getStatus()
self.failUnless(status == 200, (status, 200, view_name))
def test_addpages(self):
manage_addFiveTraversableFolder(self.folder, 'ftf')
# Unprotected as anonymous
response = self.publish('/test_folder_1_/ftf/+/addfieldcontent.html')
self.assertEqual(response.getStatus(), 200)
# Protected as manager
response = self.publish('/test_folder_1_/ftf/+/protectedaddform.html',
basic='manager:r00t')
self.assertEqual(response.getStatus(), 200)
# Protected as user
response = self.publish('/test_folder_1_/ftf/+/protectedaddform.html',
basic='viewer:secret')
self.assertEqual(response.getStatus(), 401)
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(SecurityTest))
suite.addTest(makeSuite(PublishTest))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# Copyright (c) 2004, 2005 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.
#
##############################################################################
"""Size adapters for testing
$Id: test_size.py 14595 2005-07-12 21:26:12Z philikon $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from zope.interface import implements
from zope.app.size.interfaces import ISized
class SimpleContentSize(object):
"""Size for ``SimpleContent`` objects."""
implements(ISized)
def __init__(self, context):
self.context = context
def sizeForSorting(self):
return ('byte', 42)
def sizeForDisplay(self):
return "What is the meaning of life?"
class FancyContentSize(object):
"""Size for ``SimpleContent`` objects."""
implements(ISized)
def __init__(self, context):
self.context = context
def sizeForSorting(self):
return ('line', 143)
def sizeForDisplay(self):
return "That's not the meaning of life!"
def test_size():
"""
Test size adapters
Set up:
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
>>> configure_zcml = '''
... <configure xmlns="http://namespaces.zope.org/zope"
... xmlns:five="http://namespaces.zope.org/five">
... <five:sizable class="Products.Five.testing.simplecontent.SimpleContent" />
... <five:sizable class="Products.Five.testing.fancycontent.FancyContent" />
... <adapter
... for="Products.Five.testing.simplecontent.ISimpleContent"
... provides="zope.app.size.interfaces.ISized"
... factory="Products.Five.tests.test_size.SimpleContentSize"
... />
... <adapter
... for="Products.Five.testing.fancycontent.IFancyContent"
... provides="zope.app.size.interfaces.ISized"
... factory="Products.Five.tests.test_size.FancyContentSize"
... />
... </configure>'''
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config('meta.zcml', Products.Five)
>>> zcml.load_string(configure_zcml)
>>> from Products.Five.testing.simplecontent import manage_addSimpleContent
>>> from Products.Five.testing.fancycontent import manage_addFancyContent
We have registered an ``ISized`` adapter for SimpleContent:
>>> n = manage_addSimpleContent(self.folder, 'simple', 'Simple')
>>> self.folder.simple.get_size()
42
Fancy content already has a ``get_size`` method
>>> n = manage_addFancyContent(self.folder, 'fancy', 'Fancy')
>>> self.folder.fancy.get_size()
43
Clean up:
>>> tearDown()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
return ZopeDocTestSuite()
if __name__ == '__main__':
framework()
...@@ -13,57 +13,70 @@ ...@@ -13,57 +13,70 @@
############################################################################## ##############################################################################
"""Unit tests for the viewable module. """Unit tests for the viewable module.
$Id: test_viewable.py 12948 2005-05-31 20:24:58Z philikon $ $Id: test_viewable.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import os, sys import os, sys
if __name__ == '__main__': if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py')) execfile(os.path.join(sys.path[0], 'framework.py'))
import Products.Five.tests.fivetest # starts Zope, loads Five, etc.
def test_defaultView(): def test_defaultView():
""" """
Take a class Foo and an interface I1:: Testing default view functionality
>>> from zope.app.tests.placelesssetup import setUp, tearDown
>>> setUp()
Take a class Foo and an interface IFoo:
>>> class Foo: >>> class Foo:
... pass ... pass
>>> from zope.interface import Interface >>> from zope.interface import Interface
>>> class I1(Interface): >>> class IFoo(Interface):
... pass ... pass
Set up a default view for I1:: Set up a default view for IFoo:
>>> from zope.app import zapi >>> from zope.app import zapi
>>> pres = zapi.getGlobalService('Presentation') >>> pres = zapi.getGlobalService('Presentation')
>>> from zope.publisher.interfaces.browser import IBrowserRequest >>> from zope.publisher.interfaces.browser import IBrowserRequest
>>> pres.setDefaultViewName(I1, IBrowserRequest, 'foo.html')
and a BrowserDefault for an instance of Foo:: and default view names for everything and IFoo objects in particular:
>>> pres.setDefaultViewName(None, IBrowserRequest, u'index.html')
>>> pres.setDefaultViewName(IFoo, IBrowserRequest, u'foo.html')
Now take a BrowserDefault for an instance of Foo::
>>> foo = Foo() >>> foo = Foo()
>>> from Products.Five.viewable import BrowserDefault >>> from Products.Five.viewable import BrowserDefault
>>> bd = BrowserDefault(foo) >>> bd = BrowserDefault(foo)
You'll see that no default view is returned:: For now the default view name is index.html, like we set above:
>>> from Products.Five.traversable import FakeRequest
>>> request = FakeRequest()
>>> request = self.app.REQUEST
>>> obj, path = bd.defaultView(request) >>> obj, path = bd.defaultView(request)
>>> obj is foo >>> obj is foo
True True
>>> path is None >>> path
True [u'index.html']
unless you mark the object with I1:: until we mark the object with IFoo:
>>> from zope.interface import directlyProvides >>> from zope.interface import directlyProvides
>>> directlyProvides(foo, I1) >>> directlyProvides(foo, IFoo)
>>> obj, path = bd.defaultView(request) >>> obj, path = bd.defaultView(request)
>>> obj is foo >>> obj is foo
True True
>>> path >>> path
['foo.html'] [u'foo.html']
Clean up:
>>> tearDown()
""" """
def test_suite(): def test_suite():
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Machinery for making things traversable through adaptation """Machinery for making things traversable through adaptation
$Id: traversable.py 12915 2005-05-31 10:23:19Z philikon $ $Id: traversable.py 18841 2005-10-23 09:57:38Z philikon $
""" """
from zExceptions import NotFound from zExceptions import NotFound
from zope.exceptions import NotFoundError from zope.exceptions import NotFoundError
...@@ -38,6 +38,9 @@ class FakeRequest: ...@@ -38,6 +38,9 @@ class FakeRequest:
def has_key(self, key): def has_key(self, key):
return False return False
def getURL(self):
return "http://codespeak.net/z3/five"
class Traversable: class Traversable:
"""A mixin to make an object traversable using an ITraverser adapter. """A mixin to make an object traversable using an ITraverser adapter.
""" """
...@@ -68,12 +71,14 @@ class Traversable: ...@@ -68,12 +71,14 @@ class Traversable:
REQUEST = FakeRequest() REQUEST = FakeRequest()
# con Zope 3 into using Zope 2's checkPermission # con Zope 3 into using Zope 2's checkPermission
newInteraction() newInteraction()
try: try:
return ITraverser(self).traverse( return ITraverser(self).traverse(
path=[name], request=REQUEST).__of__(self) path=[name], request=REQUEST).__of__(self)
except (ComponentLookupError, NotFoundError, except (ComponentLookupError, NotFoundError,
AttributeError, KeyError, NotFound): AttributeError, KeyError, NotFound):
pass pass
try: try:
return getattr(self, name) return getattr(self, name)
except AttributeError: except AttributeError:
...@@ -100,5 +105,3 @@ class FiveTraversable(DefaultTraversable): ...@@ -100,5 +105,3 @@ class FiveTraversable(DefaultTraversable):
return getView(context, name, REQUEST) return getView(context, name, REQUEST)
except ComponentLookupError: except ComponentLookupError:
pass pass
# If a view can't be found, then use default traversable
return super(FiveTraversable, self).traverse(name, furtherPath)
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""Machinery for making things viewable """Machinery for making things viewable
$Id: viewable.py 12915 2005-05-31 10:23:19Z philikon $ $Id: viewable.py 14595 2005-07-12 21:26:12Z philikon $
""" """
import inspect import inspect
from zExceptions import NotFound from zExceptions import NotFound
...@@ -21,8 +21,9 @@ from zope.exceptions import NotFoundError ...@@ -21,8 +21,9 @@ from zope.exceptions import NotFoundError
from zope.component import getView, getDefaultViewName, ComponentLookupError from zope.component import getView, getDefaultViewName, ComponentLookupError
from zope.interface import implements from zope.interface import implements
from zope.publisher.interfaces.browser import IBrowserRequest from zope.publisher.interfaces.browser import IBrowserRequest
from traversable import FakeRequest
from interfaces import IBrowserDefault from Products.Five.traversable import FakeRequest
from Products.Five.interfaces import IBrowserDefault
_marker = object _marker = object
...@@ -96,7 +97,6 @@ class Viewable: ...@@ -96,7 +97,6 @@ class Viewable:
# return False # return False
class BrowserDefault(object): class BrowserDefault(object):
implements(IBrowserDefault) implements(IBrowserDefault)
def __init__(self, context): def __init__(self, context):
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
############################################################################## ##############################################################################
"""ZCML machinery """ZCML machinery
$Id: zcml.py 12915 2005-05-31 10:23:19Z philikon $ $Id: zcml.py 14452 2005-07-09 21:15:33Z philikon $
""" """
import os import os
from zope.configuration import xmlconfig from zope.configuration import xmlconfig
...@@ -23,12 +23,8 @@ _context = None ...@@ -23,12 +23,8 @@ _context = None
def load_site(): def load_site():
"""Load the appropriate ZCML file. """Load a Five/Zope site by finding and loading the appropriate site
configuration file."""
Note that this can be called multiple times, unlike in Zope 3. This
is needed because in Zope 2 we don't (yet) have a master ZCML file
which can include all the others.
"""
global _initialized global _initialized
if _initialized: if _initialized:
return return
...@@ -61,3 +57,12 @@ def load_string(s): ...@@ -61,3 +57,12 @@ def load_string(s):
global _context global _context
_context = xmlconfig.string(s, _context) _context = xmlconfig.string(s, _context)
# clean up code
def cleanUp():
global _context
_context = None
from zope.testing.cleanup import addCleanUp
addCleanUp(cleanUp)
del addCleanUp
...@@ -17,6 +17,7 @@ $Id$ ...@@ -17,6 +17,7 @@ $Id$
""" """
import re import re
import Products.Five.i18n
from DocumentTemplate.DT_Util import ustr from DocumentTemplate.DT_Util import ustr
from TAL.TALDefs import NAME_RE from TAL.TALDefs import NAME_RE
...@@ -31,15 +32,19 @@ class DummyTranslationService: ...@@ -31,15 +32,19 @@ class DummyTranslationService:
return cre.sub(repl, default or msgid) return cre.sub(repl, default or msgid)
# XXX Not all of Zope2.I18n.ITranslationService is implemented. # XXX Not all of Zope2.I18n.ITranslationService is implemented.
translationService = DummyTranslationService() #
# As of Five 1.1, we're by default using Zope 3 Message Catalogs for
# translation, but we allow fallback translation services such as PTS
# and Localizer
#
def setGlobalTranslationService(service): Products.Five.i18n._fallback_translation_service = DummyTranslationService()
"""Sets the global translation service, and returns the previous one.""" fiveTranslationService = Products.Five.i18n.FiveTranslationService()
global translationService
old_service = translationService
translationService = service
return old_service
def getGlobalTranslationService(): def getGlobalTranslationService():
"""Returns the global translation service.""" return fiveTranslationService
return translationService
def setGlobalTranslationService(newservice):
oldservice, Products.Five.i18n._fallback_translation_service = \
Products.Five.i18n._fallback_translation_service, newservice
return oldservice
...@@ -13,6 +13,11 @@ ...@@ -13,6 +13,11 @@
import os, sys, unittest import os, sys, unittest
#XXX utility service needed for Zope X3 3.0. This will go away before
# when we switch to Zope 3.2 and thus be removed before the release --
# philiKON
import zope.app.tests.placelesssetup
from Products.PageTemplates.tests import util from Products.PageTemplates.tests import util
from Products.PageTemplates.PageTemplate import PageTemplate from Products.PageTemplates.PageTemplate import PageTemplate
from Products.PageTemplates.GlobalTranslationService import \ from Products.PageTemplates.GlobalTranslationService import \
...@@ -149,12 +154,22 @@ class HTMLTests(unittest.TestCase): ...@@ -149,12 +154,22 @@ class HTMLTests(unittest.TestCase):
self.assert_expected_unicode(self.folder.t, 'CheckUnicodeInserts.html') self.assert_expected_unicode(self.folder.t, 'CheckUnicodeInserts.html')
def checkI18nTranslate(self): def checkI18nTranslate(self):
#XXX utility service needed for Zope X3 3.0. This will go
# away before when we switch to Zope 3.2 and thus be removed
# before the release -- philiKON
zope.app.tests.placelesssetup.setUp()
self.assert_expected(self.folder.t, 'CheckI18nTranslate.html') self.assert_expected(self.folder.t, 'CheckI18nTranslate.html')
zope.app.tests.placelesssetup.tearDown()
def checkI18nTranslateHooked(self): def checkI18nTranslateHooked(self):
#XXX utility service needed for Zope X3 3.0. This will go
# away before when we switch to Zope 3.2 and thus be removed
# before the release -- philiKON
zope.app.tests.placelesssetup.setUp()
old_ts = setGlobalTranslationService(TestTranslationService()) old_ts = setGlobalTranslationService(TestTranslationService())
self.assert_expected(self.folder.t, 'CheckI18nTranslateHooked.html') self.assert_expected(self.folder.t, 'CheckI18nTranslateHooked.html')
setGlobalTranslationService(old_ts) setGlobalTranslationService(old_ts)
zope.app.tests.placelesssetup.tearDown()
def test_suite(): def test_suite():
return unittest.makeSuite(HTMLTests, 'check') return unittest.makeSuite(HTMLTests, 'check')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment