Commit 465ed361 authored by Brian Lloyd's avatar Brian Lloyd

merged changes r29469:29727 from the five-integration branch

parents 1dd29315 508ace53
......@@ -56,6 +56,11 @@ Zope Changes
"javascript" as editable through the File edit form, just like
text/<foo> types
- Zope X3 3.0.0's 'src/zope' package is included now.
- Five (Zope 3 integration technology for Zope 2) is included
now in Products/Five.
Bugs fixed
- ZPublisher would fail to recognize a XML-RPC request if the
......@@ -80,6 +85,9 @@ Zope Changes
- Fixed Shared.DC.ZRDB.Results to behave with the new-style
ExtensionClass. Added a test.
- 'setup.py' did not install the new 'Zope' compatibility module
(the 'Zope' package has been renamedd to 'Zope2').
- Collector #1507: Zope now binds again to all available IP addresses if
ip-address is unset
......
Using Zope 3 Components in Zope 2 Applications
Background
Zope 3 is next major revision of Zope. It is designed to be more
'programmer-centric' and easier to learn, use and extend. Zope 3
introduces an interface-centric component architecture that makes
it easier to develop and deploy components without requiring
developers to learn and understand the entire Zope framework.
Much more information on Zope 3 is available on the Zope 3 area
of the zope.org Website at http://dev.zope.org/Zope3/.
As of Zope 2.8, the "Five" project has been integrated into the
Zope 2 core. The "Five" project implements a compatibily layer
that allows many Zope 3 components and patterns to be used in
new and existing Zope 2 applications. This provides a number of
benefits::
- availability of Zope 3 technologies in Zope 2 like the
component architecture and declarative configuration
- you can gradually evolve your Zope 2 projects to better plan
for migration to Zope 3
- you start learning about Zope 3 right now, preparing yourself
better for the future. Since Zope 3 is open to contributions,
you could even influence your future for the better ;)
Features
The Five integration layer provides support for Zope 3 interfaces,
Zope Configuration Markup Language (ZCML), adapters, views,
utilities, schema-driven content, and Zope 3 add and edit forms.
Note that the Five layer does *not* attempt provide a ZMI user
interface for Zope 3 components.
Zope 2.8+ includes the essential Zope 3 packages, so it is not
necessary to install Zope 3.
Zope 3 Interfaces
Everything in the zope.interface package should work. Zope 3 interfaces
are the foundation of the component architecture, and also the foundation
of schemas.
Historically, Zope 2 has used the ``__implements__`` class attribute for
interface declarations. Zope 2 cannot detect Zope 3 interfaces and the
Zope 3 machinery cannot detect Zope 2 interfaces. This is a good thing,
as Zope 2 has no way to deal with Zope 3 interfaces, and Zope 3 cannot
work with Zope 2 interfaces. This means you can safely make a class
declare both a Zope 2 and Zope 3 interface independently from each
other. It's a rare case where you need this - you're usually better off
just switching to ``implements()`` for your application possible.
Switching from Zope 2 interfaces to Zope 3 interfaces is easy -- just
make your interfaces inherit from ``zope.interface.Interface`` instead
of ``Interface.Interface`` (or ``Interface.Base``). Next, change all
``__implements__`` to ``implements()``.
You will also have to change any calls to ``isImplementedBy`` and such
in your application to ``providedBy``, as ``isImplementedBy`` has been
deprecated (you'll see the DeprecationWarnings in your Zope log).
ZCML
ZCML is the Zope Configuration Markup Language, an XML application.
Zope 3 code consists of a lot of components that can be plugged together
using ZCML.
If you put a 'site.zcml' in the home directory of your Zope instance,
this is the root of the ZCML tree. An example of 'site.zcml' is in
'site.zcml.in'. If you don't place a site.zcml, Five falls back on
'fallback.zcml'.
ZCML in Zope 2 with Five has a special directive, 'five:loadProducts',
to load the ZCML ('meta.zcml', 'configure.zcml') of all installed
Zope 2 products, if available.
Another special directive, 'five:loadProductsOverrides' is available to
load any overriding ZCML (overrides.zcml) in these products. In the
'overrides.zcml' you can override existing views or adapters, in this
or in other products.
The Five integration layer tries to use the Zope 3 ZCML directives where
possible, though it does sometimes support only a subset of the possible
attributes. It also introduces a few directives of its own under the
``five`` namespace. The supported directives are listed per namespace
in alphabetic order below.
zope ``http://namespaces.zope.org/zope``
========================================
adapter
-------
Hook an adapter factory to an interface.
content
-------
Declare interface and permissions on content object. Declares Zope 2
permissions.
permission
----------
Way to make Zope 2 permissions available to Five, ``title`` is
permission name.
redefinePermission
------------------
Redefine a permission in included ZCML as another one.
service
-------
Declare a global service
serviceType
-----------
Declare a type of service.
skin
----
Declare a skin, consisting of layers.
utility
-------
Declare a global utility.
browser ``http://namespaces.zope.org/browser``
==============================================
page
----
Declare a page view for an interface. Permission is a Zope 2
permission.
pages
-----
Declare multiple page views for an interface. Permissions are Zope 2
permissions.
defaultView
-----------
Declare the name of the view that should be used for the default when
viewing the object; i.e. when the object is traversed to without a view.
defaultSkin
-----------
Declare the default skin used.
interface
---------
Define an interface in ZCML.
layer
-----
Declare a layer.
menu
----
Declare a menu
menuItem, menuItems
-------------------
Declare menuItems
five ``http://namespaces.zope.org/five``
========================================
implements
----------
Make a class declare it implements an interface.
loadProducts
------------
Loads ZCML in all Zope 2 products. First processes all ``meta.zcml``
files, then processes all ``configure.zcml`` files.
loadProductsOverrides
---------------------
Loads overriding ZCML in all products (``overrides.zcml``).
traversable
-----------
Make a Zope 2 content class traversable in the Zope 3 manner using
Five. This is used to attached views, resources and other things to
Zope 2 objects.
defaultViewable
---------------
Make a Zope 2 content class use a Zope 3 default view when looking at
it without any paths appended to it. This works then instead of
``index_html`` in Zope 2.
pagesFromDirectory
------------------
Load all *.pt files in a directory as pages. Useful when you want to
share templates between Five and CMF, so you can declare pages like
this is a similar way to setting up skin folders in portal_skins.
browser:editform
----------------
Create an edit form based on a schema.
browser:addform
---------------
Create an add form based on a schema.
Adapters
Generally, adapters can now be used in Zope 2 just as they are used in
Zope 3.
Zope 3 Views
Zope 3 views work in Zope 2 with Five, including layers and skins. To
make them work however, you need to make a Zope 2 class "traversable".
This can be done by using the 'five:traversable' directive in ZCML.
Schemas and Forms
With the Five layer, it is possible to use schema-based content using
the standard Zope 3 patterns.
Security declarations
Five aims to eradicate declareProtected, ClassSecurityInfo and
initializeClass from your Zope 2 code.
In order to do this, Five provides the Zope 3 way of declaring
permissions from ZCML, but uses the Zope 2 mechanisms to actually set
them. To declare permissions for methods and templates on views you use
the permission attribute on the browser:page directive, and specify a
Zope 2 permission (given a Zope 3 name). You can find a list of these
permissions in permissions.zcml in Five. The permission check takes
place before the view is executed.
The content directive can also be used to declare permissions on Zope 2
content classes. Note however that these permissions will be ignored by
views anyway, as they are trusted -- it only serves to protect directly
exposed methods on content classes (the python scripts and the ZPublisher).
......@@ -4,4 +4,4 @@ ZOPE_BRANCH_NAME = '$Name$'[6:] or 'no-branch'
# always start prerelease branches with '0' to avoid upgrade
# issues in RPMs
VERSION_RELEASE_TAG = 'a1'
VERSION_RELEASE_TAG = 'b1'
Five Changes
============
* There is now a standard standard_macros. Five page templates can use
context/@@standard_macros/view to get the default site layout.
* The addform and editform directive now supports the widget zcml subdirective,
that previously was ignored.
* Five now supports the vocabulary zcml directive.
Five 0.3 (2005-03-11)
---------------------
* Five now uses the Zope 2 page template engine, not the Zope 3
engine. This allows better integration with Zope 2-based page
templates, such as macros.
It uses TrustedExecutables technology (thanks to Dieter Maurer) to
turn off Zope 2 security in page templates, so Five's security
behavior is very similar to what it was before.
* Five now supports the browser:menu, menuItem and menuItems
directives.
* A new Five-specific directive has been added:
five:pagesFromDirectory. This adds one page for each .pt file in a
directory to the specified interface. This is useful for Five
integration with CMF and other systems that have Page Templates
macros that need to be shared between Zope2 and Five.
* Five.security.checkPermission has been changed from a (unused)
method for checking the existence of permissions. Use
zope.app.security.permission.checkPermission if you need that
functionality.
Instead Five.security.checkPermission is now a Five version of
zope.security.checkPermission, which checks if the current user has
a permission on an object.
* Support for browser:editform. You can now use schemas for editing.
* Support for browser:addform; add forms using '+'. You can now browse
to 'container/+/addsomething.html' to get to a schema-driven add
form.
* Fixed a traversal bug which caused Zope to give the wrong error when
a page could not be found (missing docstring instead of not
found). Zope 2.7.4 (or higher) is required for this fix.
Five 0.2b (2004-09-24)
----------------------
* Added utility module, 'bridge', allowing reuse of Zope 2 interfaces
(by introspecting them to create equivalent Zope 3 interfaces).
* five:viewable was renamed to five:traversable, five:viewable still
works but is deprecated; a deprecation warning is emitted when it is
used.
* like in Zope3, an ITraverser adapter is looked up to determine what
happens when traversing into a Five traversable object.
* added five:defaultViewable to make instances of a class directly
viewable using browser:defaultView. This is hookable by the use of a
IBrowserDefault adapter
* deprecated use of Products.Five.api as public API for other products
to use, instead import directly from Products.Five. Retired
Traversable and Viewable from the public API; use ZCML directives
(five:traversable, five:defaultView) instead of mixins to make
instances of classes work with Five.
* classes that Five monkeypatches now have a __five_method__
attribute, making it easier for Five not to stomp on existing methods.
* registered absolute_url view and IAbsoluteURL adapter for *
* zope.app.traversing is registered by default, to make special
namespaces available (eg: @@, ++resource++)
* we now have resources (FileResource, ImageResource,
PageTemplateResource) and directory resources.
* Zope 3 'StandardMacros' now works with Five as well.
* browser:page now correctly handles the allow_attributes and protects
the named attributes on the view with the same permission used for
the view.
* zopeconf.py will try to find etc/zope.conf on INSTANCE_HOME. This
requires Zope 2.7.2, as earlier Zope versions have a bug in this
area which causes them to look in lib/python/Testing.
* Exposed the Zope 3 event system to Five. A class can be made to send
out event notifications using the five:sendEvents directive. Events can
be subscribed to using the subscriber directive.
* Change in findProducts so that non-filesystem products are skipped.
Five 0.1 (2004-07-30)
---------------------
Initial public release (mainly Martijn's work)
Five is distributed under the provisions of the Zope Public License
(ZPL) v2.1. See doc/ZopePublicLicense.txt for the license text.
Copyright (C) 2005 Five Contributors. See CREDITS.txt for a list of
Five contributors.
Five contains source code derived from:
- Zope 3, copyright (C) 2001-2005 by Zope Corporation. Code that
falls under this copyright is prefixed with the following header:
Copyright (c) 2001-2004 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.
- metaclass.py is derived from PEAK, copyright (C) 1996-2004 by
Phillip J. Eby and Tyler C. Sarna. PEAK may be used under the same
terms as Zope.
- TrustedExecutables. Dieter Mauer kindly allow licensing this under the
ZPL 2.1.
\ No newline at end of file
Five contributors
-----------------
- Martijn Faassen (faassen@infrae.com)
- Sidnei da Silva (sidnei@awkly.org)
- Philipp von Weitershausen (philikon@philikon.de)
- Lennart Regebro (regebro@nuxeo.com)
- Tres Seaver (tres@zope.com)
- Andy Adiwidjaja (mail@adiwidjaja.com)
- Stuart Bishop (stuart@stuartbishop.net)
- Simon Eisenmann (simon@struktur.de)
- Dieter Maurer (dieter@handshake.de)
Thank you
---------
Infrae for the initial development and continuing support.
Martijn Faassen would like to thank ETH Zurich for their support and
encouragement during the initial development of Five.
Nuxeo for significant contributions to making Five usable in the real
world.
Dieter Maurer for use of code from TrustedExecutables within Five
under the ZPL.
The Five developers would like to thank the Zope 3 developers, in
particular Jim Fulton, for the mountain to stand on.
How to install Five
-------------------
Requirements for Five 0.3
=========================
* Zope 2.7.4+ with python 2.3.x. Zope versions lower than Zope 2.7.4
may work, but no guarantees.
* Zope X3.0.0, found here: http://zope.org/Products/ZopeX3/3.0.0final/
Installing Five
===============
Installing Five is relatively straightforward.
* Select a Zope 2.7 instance.
* Download and install Zope X3.0.0. You can get it compiled and
installed by:
* Typing ``configure``. When you're experimenting, typically you
want to use the ``--prefix`` directive to install the binaries
to install it somewhere in your homedirectory.
* ``make``
* ``make install``
On windows you can install choose to use the binary release instead.
Alternatively you can check out the latest subversion version of
Zope X3.0 and typing ``make`` to produce it in-place.
* You need to make your Zope 2.7 instance aware of Zope 3 so it can
import the ``zope``, ``persistent`` and ``transaction`` packages from it.
* In non-ZEO setups, you can simply go to the ``etc/zope.conf`` of
your Zope 2.7 instance and add a ``path`` entry. If you used the
released version of Zope X3.0.0, use something like the following::
path /path/to/installed/Zope3/lib/python
If you are instead using the subversion version, use::
path /path/to/Zope3/src
If you have problems however, see the instructions for the ZEO
setup.
* In ZEO setups (or some other circumstances), Zope 3's ZEO packages
will interfere with Zope 2's. In this case you can create a new
directory, symlink the ``zope``, ``persistent`` and ``transaction``
packages in it and use this directory for the ``path`` entry in the
``etc/zope.conf`` of your Zope 2.7 instance.
* Next, install the Five product into your Zope 2.7 instance as a
product and restart Zope. Five should now be installed.
* You can also install various products in the ``demo`` subdirectory
of Five by copying them into your ``Products`` directory. In
addition, you can look at tests/products/FiveTest, which is a
product used for the Five tests, and may contain more recent
examples.
Installing the tests
====================
For information on how to install the automatic Five tests, please see
``tests/README.txt``.
Introduction
------------
"It was the dawn of the third age of Zope. The Five project was a dream
given form. Its goal: to use Zope 3 technologies in Zope 2.7 by
creating a Zope 2 product where Zope 3 and Zope 2 could work out their
differences peacefully." -- Babylon 5, creatively quoted
"The Law of Fives states simply that: ALL THINGS HAPPEN IN FIVES, OR
ARE DIVISIBLE BY OR ARE MULTIPLES OF FIVE, OR ARE SOMEHOW DIRECTLY OR
INDIRECTLY RELATED TO FIVE.
THE LAW OF FIVES IS NEVER WRONG." -- Principia Discordia
What is Five?
-------------
The goal of five is to allow Zope 2 developers to use Zope 3
technology right now, inside of Zope 2. Additionally, this allows a
gradual evolution of Zope 2 code to Zope 3.
Five already makes the following Zope 3 technologies available in Zope
2:
* Zope 3 interfaces
* ZCML (Zope Configuration Markup Language)
* Adapters
* Zope 3 views, even for standard Zope objects
* layers & skins
* schema/forms machinery, including edit and add forms.
* Zope 2 security declarations in ZCML instead of in Python code.
Together with another product, CMFonFive, Five can integrate into CMF.
For more information, see ``doc/features.txt``.
How to install Five
-------------------
See ``INSTALL.txt``.
How to use Five
---------------
Please see ``doc/manual.txt``.
##############################################################################
#
# 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.
#
##############################################################################
from new import function
def rebindFunction(f,rebindDir=None,**rebinds):
'''return *f* with some globals rebound.'''
d= {}
if rebindDir : d.update(rebindDir)
if rebinds: d.update(rebinds)
if not d: return f
f= getattr(f,'im_func',f)
fd= f.func_globals.copy()
fd.update(d)
nf= function(f.func_code,fd,f.func_name,f.func_defaults or ())
nf.__doc__= f.__doc__
if f.__dict__ is not None: nf.__dict__= f.__dict__.copy()
return nf
====
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.
##############################################################################
#
# 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.
#
##############################################################################
from sys import modules
from Products.PageTemplates.PythonExpr import PythonExpr
from Products.PageTemplates.Expressions import \
SubPathExpr, PathExpr, \
StringExpr, \
getEngine, installHandlers
from ReuseUtils import rebindFunction
class _ModuleImporter:
def __getitem__(self, module):
__import__(module)
return modules[module]
ModuleImporter = _ModuleImporter()
def trustedTraverse(ob, path, ignored,):
if not path: return self
get = getattr
has = hasattr
N = None
M = rebindFunction # artifical marker
if isinstance(path, str): path = path.split('/')
else: path=list(path)
REQUEST={'TraversalRequestNameStack': path}
path.reverse()
pop=path.pop
if len(path) > 1 and not path[0]:
# Remove trailing slash
path.pop(0)
if not path[-1]:
# If the path starts with an empty string, go to the root first.
pop()
self=ob.getPhysicalRoot()
object = ob
while path:
name=pop()
__traceback_info__ = path, name
if name == '..':
o=getattr(object, 'aq_parent', M)
if o is not M:
object=o
continue
t=get(object, '__bobo_traverse__', M)
if t is not M: o=t(REQUEST, name)
else:
o = get(object, name, M)
if o is M:
try: o = object[name]
except AttributeError: # better exception
raise AttributeError(name)
object = o
return object
class SubPathExpr(SubPathExpr):
_eval = rebindFunction(SubPathExpr._eval.im_func,
restrictedTraverse=trustedTraverse,
)
class PathExpr(PathExpr):
__init__ = rebindFunction(PathExpr.__init__.im_func,
SubPathExpr=SubPathExpr,
)
class StringExpr(StringExpr):
__init__ = rebindFunction(StringExpr.__init__.im_func,
PathExpr=PathExpr,
)
installHandlers = rebindFunction(installHandlers,
PathExpr=PathExpr,
StringExpr=StringExpr,
PythonExpr=PythonExpr,
)
_engine=None
getEngine = rebindFunction(getEngine,
_engine=_engine,
installHandlers=installHandlers
)
##############################################################################
#
# 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.
#
##############################################################################
"""Initialize the Five product
$Id: __init__.py 9796 2005-03-15 14:58:39Z efge $
"""
import Acquisition
from Globals import INSTANCE_HOME
import zcml
# public API provided by Five
# usage: from Products.Five import <something>
from browser import BrowserView, StandardMacros
def initialize(context):
zcml.load_site()
<html metal:use-macro="context/@@standard_macros/page">
<body>
<div metal:fill-slot="body">
<div metal:define-macro="addform">
<form action="." tal:attributes="action request/URL" method="post"
enctype="multipart/form-data">
<div metal:define-macro="formbody">
<h3 tal:condition="view/label"
tal:content="view/label"
metal:define-slot="heading"
>Edit something</h3>
<p tal:define="status view/update"
tal:condition="status"
tal:content="status" />
<p tal:condition="view/errors" i18n:translate="">
There are <strong tal:content="python:len(view.errors)"
i18n:name="num_errors">6</strong> input errors.
</p>
<div metal:define-slot="extra_info" tal:replace="nothing">
</div>
<div class="row" metal:define-slot="extra_top" tal:replace="nothing">
<div class="label">Extra top</div>
<div class="label"><input type="text" style="width:100%" /></div>
</div>
<div metal:use-macro="context/@@widget_macros/widget_rows" />
<div class="separator"></div>
<div class="row"
metal:define-slot="extra_bottom" tal:replace="nothing">
<div class="label">Extra bottom</div>
<div class="field"><input type="text" style="width:100%" /></div>
</div>
<div class="separator"></div>
</div>
<br/><br/>
<div class="row">
<div class="controls"><hr />
<input type='submit' value='Refresh'
i18n:attributes='value refresh-button' />
<input type='submit' value='Add' name='UPDATE_SUBMIT'
i18n:attributes='value add-button' />
<span tal:condition="context/nameAllowed|nothing" tal:omit-tag="">
&nbsp;&nbsp;<b i18n:translate="">Object Name</b>&nbsp;&nbsp;
<input type='text' name='add_input_name'
tal:attributes="value context/contentName" />
</span>
</div>
</div>
<div class="row" metal:define-slot="extra_buttons" tal:replace="nothing">
</div>
<div class="separator"></div>
</form>
</div>
</div>
</body>
</html>
<html metal:use-macro="here/five_template/macros/master">
<body>
<div metal:fill-slot="main">
<p>+ screen not yet supported by Five</p>
</div>
</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.
#
##############################################################################
"""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.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)
chooser.checkName(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):
try:
self.context._checkId(name, allow_dup=False)
except BadRequest:
raise UserError, "Id is in use or invalid"
def chooseName(self, name, object):
if not name:
name = object.__class__.__name__
dot = name.rfind('.')
if dot >= 0:
suffix = name[dot:]
name = name[:dot]
else:
suffix = ''
n = name + suffix
i = 1
while True:
try:
container._getOb(n)
except AttributeError:
pass
else:
break
i += 1
n = name + '-' + str(i) + suffix
# Make sure tha name is valid. We may have started with something bad.
self.checkName(n, object)
return n
##############################################################################
#
# 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.
#
##############################################################################
"""Convenience package for short imports
$Id: api.py 6174 2004-08-25 17:19:28Z faassen $
"""
import warnings
warnings.warn('The use of the Products.Five.api module has been deprecated. '
'Import directly from Products.Five instead for public API.',
DeprecationWarning)
from browser import BrowserView, StandardMacros
from traversable import Traversable
from viewable import Viewable
##############################################################################
#
# 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.
#
##############################################################################
""" Z2 -> Z3 bridge utilities.
$Id$
"""
from Interface._InterfaceClass import Interface as Z2_InterfaceClass
from Interface import Interface as Z2_Interface
from Interface import Attribute as Z2_Attribute
from zope.interface.interface import InterfaceClass as Z3_InterfaceClass
from zope.interface.interface import Interface as Z3_Interface
from zope.interface.interface import Attribute as Z3_Attribute
def fromZ2Interface(z2i):
""" Return a Zope 3 interface corresponding to 'z2i'.
o 'z2i' must be a Zope 2 interface.
"""
if not isinstance(z2i, Z2_InterfaceClass):
raise ValueError, 'Not a Zope 2 interface!'
if z2i is Z2_Interface: # special case; root in new hierarchy!
return Z3_Interface
name = z2i.getName()
bases = [ fromZ2Interface(x) for x in z2i.getBases() ]
attrs = {}
for k, v in z2i.namesAndDescriptions():
if isinstance(v, Z2_Attribute):
v = fromZ2Attribute(v)
attrs[k] = v
# XXX: Note that we pass the original interface's __module__;
# we may live to regret that.
return Z3_InterfaceClass(name=name,
bases=bases,
attrs=attrs,
__doc__=z2i.getDoc(),
__module__=z2i.__module__,
)
def fromZ2Attribute(z2a):
""" Return a Zope 3 interface attribute corresponding to 'z2a'.
o 'z2a' must be a Zope 2 interface attribute.
"""
if not isinstance(z2a, Z2_Attribute):
raise ValueError, 'Not a Zope 2 interface attribute!'
return Z3_Attribute(z2a.getName(), z2a.getDoc())
##############################################################################
#
# 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.
#
##############################################################################
"""Provide basic browser functionality
$Id: browser.py 9730 2005-03-10 22:50:43Z jw $
"""
# python
import sys
from datetime import datetime
# Zope 2
import Acquisition
from Acquisition import aq_inner, aq_parent, aq_base
from AccessControl import ClassSecurityInfo
from Globals import InitializeClass
# Zope 3
from interfaces import ITraversable
from zope.interface import implements
from zope.interface.common.mapping import IItemMapping
from zope.component import getView
from zope.component import getViewProviding
from zope.app.traversing.browser.interfaces import IAbsoluteURL
from zope.app.location.interfaces import ILocation
from zope.app.location import LocationProxy
from zope.app.form.utility import setUpEditWidgets, applyWidgetsChanges
from zope.app.form.browser.submit import Update
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.interfaces import IInputWidget, WidgetsError
from zope.schema.interfaces import ValidationError
from zope.app.event.objectevent import ObjectCreatedEvent, ObjectModifiedEvent
# Five
from Products.Five.pagetemplatefile import FivePageTemplateFile
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 Macros:
implements(IItemMapping)
macro_pages = ()
aliases = {
'view': 'page',
'dialog': 'page',
'addingdialog': 'page'
}
def __getitem__(self, key):
key = self.aliases.get(key, key)
context = self.context
request = self.request
for name in self.macro_pages:
page = getView(context, name, request)
try:
v = page[key]
except KeyError:
pass
else:
return v
raise KeyError, key
class StandardMacros(BrowserView, Macros):
macro_pages = ('five_template',
'widget_macros',
'form_macros',)
class EditView(BrowserView):
"""Simple edit-view base class
Subclasses should provide a schema attribute defining the schema
to be edited.
"""
errors = ()
update_status = None
label = ''
# Fall-back field names computes from schema
fieldNames = property(lambda self: getFieldNamesInOrder(self.schema))
# Fall-back template
generated_form = FivePageTemplateFile('edit.pt')
def __init__(self, context, request):
BrowserView.__init__(self, context, request)
self._setUpWidgets()
def _setUpWidgets(self):
adapted = self.schema(self.context)
if adapted is not self.context:
if not ILocation.providedBy(adapted):
adapted = LocationProxy(adapted)
adapted.__parent__ = self.context
self.adapted = adapted
setUpEditWidgets(self, self.schema, source=self.adapted,
names=self.fieldNames)
def setPrefix(self, prefix):
for widget in self.widgets():
widget.setPrefix(prefix)
def widgets(self):
return [getattr(self, name+'_widget')
for name in self.fieldNames]
def changed(self):
# This method is overridden to execute logic *after* changes
# have been made.
pass
def update(self):
if self.update_status is not None:
# We've been called before. Just return the status we previously
# computed.
return self.update_status
status = ''
content = self.adapted
if Update in self.request.form.keys():
changed = False
try:
changed = applyWidgetsChanges(self, self.schema,
target=content, names=self.fieldNames)
# We should not generate events when an adapter is used.
# That's the adapter's job.
if changed and self.context is self.adapted:
notify(ObjectModifiedEvent(content))
except WidgetsError, errors:
self.errors = errors
status = "An error occured."
get_transaction().abort()
else:
setUpEditWidgets(self, self.schema, source=self.adapted,
ignoreStickyValues=True,
names=self.fieldNames)
if changed:
self.changed()
# XXX: Needs i18n support:
# formatter = self.request.locale.dates.getFormatter(
# 'dateTime', 'medium')
# status = _("Updated on ${date_time}")
# status.mapping = {'date_time': formatter.format(
# datetime.utcnow())}
status = "Updated on %s" % str(datetime.utcnow())
self.update_status = status
return status
class AddView(EditView):
"""Simple edit-view base class.
Subclasses should provide a schema attribute defining the schema
to be edited.
"""
def _setUpWidgets(self):
setUpWidgets(self, self.schema, IInputWidget, names=self.fieldNames)
def update(self):
if self.update_status is not None:
# We've been called before. Just return the previous result.
return self.update_status
if self.request.form.has_key(Update):
self.update_status = ''
try:
data = getWidgetsData(self, self.schema, names=self.fieldNames)
self.createAndAdd(data)
except WidgetsError, errors:
self.errors = errors
self.update_status = "An error occured."
return self.update_status
self.request.response.redirect(self.nextURL())
return self.update_status
def create(self, *args, **kw):
"""Do the actual instantiation."""
# hack to please typical Zope 2 factories, which expect id and title
args = ('tmp_id', 'Temporary title') + args
return self._factory(*args, **kw)
def createAndAdd(self, data):
"""Add the desired object using the data in the data argument.
The data argument is a dictionary with the data entered in the form.
"""
args = []
if self._arguments:
for name in self._arguments:
args.append(data[name])
kw = {}
if self._keyword_arguments:
for name in self._keyword_arguments:
if name in data:
kw[str(name)] = data[name]
content = self.create(*args, **kw)
adapted = self.schema(content)
errors = []
if self._set_before_add:
for name in self._set_before_add:
if name in data:
field = self.schema[name]
try:
field.set(adapted, data[name])
except ValidationError:
errors.append(sys.exc_info()[1])
if errors:
raise WidgetsError(*errors)
notify(ObjectCreatedEvent(content))
content = self.add(content)
adapted = self.schema(content)
if self._set_after_add:
for name in self._set_after_add:
if name in data:
field = self.schema[name]
try:
field.set(adapted, data[name])
except ValidationError:
errors.append(sys.exc_info()[1])
if errors:
raise WidgetsError(*errors)
return content
def add(self, content):
return self.context.add(content)
def nextURL(self):
return self.context.nextURL()
##############################################################################
#
# 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.
#
##############################################################################
"""Browser directives
Directives to emulate the 'http://namespaces.zope.org/browser'
namespace in ZCML known from zope.app.
$Id: browserconfigure.py 9707 2005-03-08 15:14:24Z regebro $
"""
import os
from zope.interface import Interface
from zope.component import getGlobalService, ComponentLookupError
from zope.configuration.exceptions import ConfigurationError
from zope.component.servicenames import Presentation
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 view as zope_app_view
from zope.app.publisher.browser.globalbrowsermenuservice import\
menuItemDirective
from zope.app.component.metaconfigure import handler
from zope.app.component.interface import provideInterface
from zope.app.form.browser.metaconfigure import BaseFormDirective
from zope.app.container.interfaces import IAdding
from resource import FileResourceFactory, ImageResourceFactory
from resource import PageTemplateResourceFactory
from resource import DirectoryResourceFactory
from browser import BrowserView, EditView, AddView
from metaclass import makeClass
from security import getSecurityInfo, protectClass, protectName,\
initializeClass
from pagetemplatefile import ZopeTwoPageTemplateFile
import ExtensionClass
def page(_context, name, permission, for_,
layer='default', template=None, class_=None,
allowed_interface=None, allowed_attributes=None,
attribute='__call__', menu=None, title=None,
):
try:
s = getGlobalService(Presentation)
except ComponentLookupError, err:
pass
_handle_menu(_context, menu, title, [for_], name, permission)
if not (class_ or template):
raise ConfigurationError("Must specify a class or template")
if allowed_attributes is None:
allowed_attributes = []
if allowed_interface is not None:
for interface in allowed_interface:
attrs = [n for n, d in interface.namesAndDescriptions(1)]
allowed_attributes.extend(attrs)
if attribute != '__call__':
if template:
raise ConfigurationError(
"Attribute and template cannot be used together.")
if not class_:
raise ConfigurationError(
"A class must be provided if attribute is used")
if template:
template = os.path.abspath(str(_context.path(template)))
if not os.path.isfile(template):
raise ConfigurationError("No such file", template)
if class_:
# new-style classes do not work with Five. As we want to import
# packages from z3 directly, we ignore new-style classes for now.
if type(class_) == type:
return
if attribute != '__call__':
if not hasattr(class_, attribute):
raise ConfigurationError(
"The provided class doesn't have the specified attribute "
)
cdict = getSecurityInfo(class_)
if template:
new_class = makeClassForTemplate(template, bases=(class_, ),
cdict=cdict)
elif attribute != "__call__":
# we're supposed to make a page for an attribute (read:
# method) and it's not __call__. We thus need to create a
# new class using our mixin for attributes.
cdict.update({'__page_attribute__': attribute})
new_class = makeClass(class_.__name__,
(class_, ViewMixinForAttributes),
cdict)
# in case the attribute does not provide a docstring,
# ZPublisher refuses to publish it. So, as a workaround,
# we provide a stub docstring
func = getattr(new_class, attribute)
if not func.__doc__:
# cannot test for MethodType/UnboundMethod here
# because of ExtensionClass
if hasattr(func, 'im_func'):
# you can only set a docstring on functions, not
# on method objects
func = func.im_func
func.__doc__ = "Stub docstring to make ZPublisher work"
else:
# we could use the class verbatim here, but we'll execute
# some security declarations on it so we really shouldn't
# modify the original. So, instead we make a new class
# with just one base class -- the original
new_class = makeClass(class_.__name__, (class_,), cdict)
else:
# template
new_class = makeClassForTemplate(template)
_handle_for(_context, for_)
_context.action(
discriminator = ('view', for_, name, IBrowserRequest, layer),
callable = handler,
args = (Presentation, 'provideAdapter',
IBrowserRequest, new_class, name, [for_], Interface, layer,
_context.info),
)
_context.action(
discriminator = ('five:protectClass', new_class),
callable = protectClass,
args = (new_class, permission)
)
if allowed_attributes:
for attr in allowed_attributes:
_context.action(
discriminator = ('five:protectName', new_class, attr),
callable = protectName,
args = (new_class, attr, permission)
)
_context.action(
discriminator = ('five:initialize:class', new_class),
callable = initializeClass,
args = (new_class,)
)
class pages(zope_app_pages):
def page(self, _context, name, attribute='__call__', template=None,
menu=None, title=None):
return page(_context,
name=name,
attribute=attribute,
template=template,
menu=menu, title=title,
**(self.opts))
def defaultView(_context, name, for_=None):
type = IBrowserRequest
_context.action(
discriminator = ('defaultViewName', for_, type, name),
callable = handler,
args = (Presentation,
'setDefaultViewName', for_, type, name),
)
_handle_for(_context, for_)
# view (named view with pages)
class view(zope_app_view):
def __call__(self):
(_context, name, for_, permission, layer, class_,
allowed_interface, allowed_attributes) = self.args
required = {}
cdict = {}
pages = {}
for pname, attribute, template in self.pages:
try:
s = getGlobalService(Presentation)
except ComponentLookupError, err:
pass
if template:
cdict[pname] = ZopeTwoPageTemplateFile(template)
if attribute and attribute != name:
cdict[attribute] = cdict[pname]
else:
if not hasattr(class_, attribute):
raise ConfigurationError("Undefined attribute",
attribute)
attribute = attribute or pname
required[pname] = permission
pages[pname] = attribute
# This should go away, but noone seems to remember what to do. :-(
if hasattr(class_, 'publishTraverse'):
def publishTraverse(self, request, name,
pages=pages, getattr=getattr):
if name in pages:
return getattr(self, pages[name])
view = zapi.queryView(self, name, request)
if view is not None:
return view
m = class_.publishTraverse.__get__(self)
return m(request, name)
else:
def publishTraverse(self, request, name,
pages=pages, getattr=getattr):
if name in pages:
return getattr(self, pages[name])
view = zapi.queryView(self, name, request)
if view is not None:
return view
raise NotFoundError(self, name, request)
cdict['publishTraverse'] = publishTraverse
if not hasattr(class_, 'browserDefault'):
if self.default or self.pages:
default = self.default or self.pages[0][0]
cdict['browserDefault'] = (
lambda self, request, default=default:
(self, (default, ))
)
elif providesCallable(class_):
cdict['browserDefault'] = (
lambda self, request: (self, ())
)
if class_ is not None:
bases = (class_, ViewMixinForTemplates)
else:
bases = (ViewMixinForTemplates)
try:
cname = str(name)
except:
cname = "GeneratedClass"
newclass = makeClass(cname, bases, cdict)
_handle_for(_context, for_)
if self.provides is not None:
_context.action(
discriminator = None,
callable = provideInterface,
args = ('', self.provides)
)
_context.action(
discriminator = ('view', for_, name, IBrowserRequest, layer,
self.provides),
callable = handler,
args = (Presentation, 'provideAdapter',
IBrowserRequest, newclass, name, [for_], self.provides,
layer, _context.info),
)
def _handle_for(_context, for_):
if for_ is not None:
_context.action(
discriminator = None,
callable = provideInterface,
args = ('', for_)
)
def _handle_menu(_context, menu, title, for_, name, permission):
if menu or title:
if not (menu and title):
raise ConfigurationError(
"If either menu or title are specified, they must "
"both be specified.")
if len(for_) != 1:
raise ConfigurationError(
"Menus can be specified only for single-view, not for "
"multi-views.")
return menuItemDirective(
_context, menu, for_[0], '@@' + str(name), title,
permission=permission)
return []
_factory_map = {'image':{'prefix':'ImageResource',
'count':0,
'factory':ImageResourceFactory},
'file':{'prefix':'FileResource',
'count':0,
'factory':FileResourceFactory},
'template':{'prefix':'PageTemplateResource',
'count':0,
'factory':PageTemplateResourceFactory}
}
def resource(_context, name, layer='default', permission='zope.Public',
file=None, image=None, template=None):
if ((file and image) or (file and template) or
(image and template) or not (file or image or template)):
raise ConfigurationError(
"Must use exactly one of file or image or template"
"attributes for resource directives"
)
res = file or image or template
res_type = ((file and 'file') or
(image and 'image') or
(template and 'template'))
factory_info = _factory_map.get(res_type)
factory_info['count'] += 1
res_factory = factory_info['factory']
class_name = '%s%s' % (factory_info['prefix'], factory_info['count'])
new_class = makeClass(class_name, (res_factory.resource,), {})
factory = res_factory(name, res, resource_factory=new_class)
_context.action(
discriminator = ('resource', name, IBrowserRequest, layer),
callable = handler,
args = (Presentation, 'provideResource',
name, IBrowserRequest, factory, layer),
)
_context.action(
discriminator = ('five:protectClass', new_class),
callable = protectClass,
args = (new_class, permission)
)
_context.action(
discriminator = ('five:initialize:class', new_class),
callable = initializeClass,
args = (new_class,)
)
_rd_map = {ImageResourceFactory:{'prefix':'DirContainedImageResource',
'count':0},
FileResourceFactory:{'prefix':'DirContainedFileResource',
'count':0},
PageTemplateResourceFactory:{'prefix':'DirContainedPTResource',
'count':0},
DirectoryResourceFactory:{'prefix':'DirectoryResource',
'count':0}
}
def resourceDirectory(_context, name, directory, layer='default',
permission='zope.Public'):
if not os.path.isdir(directory):
raise ConfigurationError(
"Directory %s does not exist" % directory
)
resource = DirectoryResourceFactory.resource
f_cache = {}
resource_factories = dict(resource.resource_factories)
resource_factories['default'] = resource.default_factory
for ext, factory in resource_factories.items():
if f_cache.get(factory) is not None:
continue
factory_info = _rd_map.get(factory)
factory_info['count'] += 1
class_name = '%s%s' % (factory_info['prefix'], factory_info['count'])
factory_name = '%s%s' % (factory.__name__, factory_info['count'])
f_resource = makeClass(class_name, (factory.resource,), {})
f_cache[factory] = makeClass(factory_name, (factory,),
{'resource':f_resource})
for ext, factory in resource_factories.items():
resource_factories[ext] = f_cache[factory]
default_factory = resource_factories['default']
del resource_factories['default']
cdict = {'resource_factories':resource_factories,
'default_factory':default_factory}
factory_info = _rd_map.get(DirectoryResourceFactory)
factory_info['count'] += 1
class_name = '%s%s' % (factory_info['prefix'], factory_info['count'])
dir_factory = makeClass(class_name, (resource,), cdict)
factory = DirectoryResourceFactory(name, directory,
resource_factory=dir_factory)
new_classes = [dir_factory,
] + [f.resource for f in f_cache.values()]
_context.action(
discriminator = ('resource', name, IBrowserRequest, layer),
callable = handler,
args = (Presentation, 'provideResource',
name, IBrowserRequest, factory, layer),
)
for new_class in new_classes:
_context.action(
discriminator = ('five:protectClass', new_class),
callable = protectClass,
args = (new_class, permission)
)
_context.action(
discriminator = ('five:initialize:class', new_class),
callable = initializeClass,
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)
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)
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
#
class ViewMixinForAttributes(BrowserView):
# we have an attribute that we can simply tell ZPublisher to go to
def __browser_default__(self, request):
return self, (self.__page_attribute__,)
# this is technically not needed because ZPublisher finds our
# attribute through __browser_default__; but we also want to be
# able to call pages from python modules, PythonScripts or ZPT
def __call__(self, *args, **kw):
attr = self.__page_attribute__
meth = getattr(self, attr)
return meth(*args, **kw)
class ViewMixinForTemplates(BrowserView):
# short cut to get to macros more easily
def __getitem__(self, name):
if name == 'macros':
return self.index.macros
return self.index.macros[name]
# make the template publishable
def __call__(self, *args, **kw):
return self.index(self, *args, **kw)
def makeClassForTemplate(src, template=None, used_for=None,
bases=(), cdict=None):
# XXX needs to deal with security from the bases?
if cdict is None:
cdict = {}
cdict.update({'index': ZopeTwoPageTemplateFile(src, template)})
bases += (ViewMixinForTemplates,)
class_ = makeClass("SimpleViewClass from %s" % src, bases, cdict)
if used_for is not None:
class_.__used_for__ = used_for
return class_
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five">
<include file="meta.zcml" />
<include file="services.zcml" />
<include file="interfaces.zcml" />
<include file="permissions.zcml" />
<include package="zope.app.traversing" />
<include package="zope.app.form.browser" />
<!-- do 'traditional' traversing by default; needed by ZPT -->
<adapter
for="*"
factory=".traversable.FiveTraversable"
provides="zope.app.traversing.interfaces.ITraversable"
/>
<adapter
for="*"
factory="zope.app.traversing.adapters.Traverser"
provides="zope.app.traversing.interfaces.ITraverser"
/>
<adapter
for="*"
factory=".viewable.BrowserDefault"
provides=".interfaces.IBrowserDefault"
/>
<browser:page
for="*"
name="absolute_url"
class=".browser.AbsoluteURL"
permission="zope.Public"
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=".browser.StandardMacros"
allowed_interface="zope.interface.common.mapping.IItemMapping"
/>
<view
for="*"
factory=".browser.AbsoluteURL"
type="zope.publisher.interfaces.http.IHTTPRequest"
permission="zope.Public"
provides="zope.app.traversing.browser.interfaces.IAbsoluteURL"
/>
<browser:page
for="zope.app.traversing.interfaces.IContainmentRoot"
name="absolute_url"
class=".browser.SiteAbsoluteURL"
permission="zope.Public"
allowed_interface="zope.app.traversing.browser.interfaces.IAbsoluteURL"
/>
<view
for="zope.app.traversing.interfaces.IContainmentRoot"
factory=".browser.SiteAbsoluteURL"
type="zope.publisher.interfaces.http.IHTTPRequest"
permission="zope.Public"
provides="zope.app.traversing.browser.interfaces.IAbsoluteURL"
/>
<browser:view
for=".interfaces.IObjectManager"
name="+"
class=".adding.ContentAdding"
permission="zope2.ViewManagementScreens"
>
<browser:page name="index.html" template="adding.pt" />
<browser:page name="action.html" attribute="action" />
</browser:view>
<adapter
for=".interfaces.IObjectManager"
factory=".adding.ObjectManagerNameChooser"
provides="zope.app.container.interfaces.INameChooser"
/>
<!-- this is really lying, but it's to please checkContainer -->
<five:implements class="OFS.ObjectManager.ObjectManager"
interface="zope.app.container.interfaces.IContainer" />
<!-- make Zope 2's REQUEST implement the right thing -->
<five:implements class="ZPublisher.HTTPRequest.HTTPRequest"
interface="zope.publisher.interfaces.browser.IBrowserRequest"
/>
</configure>
Zope Public License (ZPL) Version 2.1
-------------------------------------
A copyright notice accompanies this license document that
identifies the copyright holders.
This license has been certified as open source. It has also
been designated as GPL compatible by the Free Software
Foundation (FSF).
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the
following conditions are met:
1. Redistributions in source code must retain the
accompanying copyright notice, this list of conditions,
and the following disclaimer.
2. Redistributions in binary form must reproduce the accompanying
copyright notice, this list of conditions, and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
3. Names of the copyright holders must not be used to
endorse or promote products derived from this software
without prior written permission from the copyright
holders.
4. The right to distribute this software or to use it for
any purpose does not give you the right to use
Servicemarks (sm) or Trademarks (tm) of the copyright
holders. Use of them is covered by separate agreement
with the copyright holders.
5. If any files are modified, you must cause the modified
files to carry prominent notices stating that you changed
the files and the date of any change.
Disclaimer
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS''
AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
NO EVENT SHALL THE COPYRIGHT HOLDERS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
=================================
ZCML Directives supported by Five
=================================
Five tries to use the Zope 3 ZCML directives where possible, though
does sometimes subset the possible attributes. It also introduces a
few directives of its own under the ``five`` namespace.
Directives are listed per namespace, in alphabetic order.
zope ``http://namespaces.zope.org/zope``
========================================
adapter
-------
Hook an adapter factory to an interface.
content
-------
Declare interface and permissions on content object. Declares Zope 2
permissions.
permission
----------
Way to make Zope 2 permissions available to Five, ``title`` is
permission name.
redefinePermission
------------------
Redefine a permission in included ZCML as another one.
service
-------
Declare a global service
serviceType
-----------
Declare a type of service.
skin
----
Declare a skin, consisting of layers.
utility
-------
Declare a global utility.
browser ``http://namespaces.zope.org/browser``
==============================================
page
----
Declare a page view for an interface. Permission is a Zope 2
permission.
pages
-----
Declare multiple page views for an interface. Permissions are Zope 2
permissions.
defaultView
-----------
Declare the name of the view that should be used for the default when viewing
the object; i.e. when the object is traversed to without a view.
defaultSkin
-----------
Declare the default skin used.
interface
---------
Define an interface in ZCML.
layer
-----
Declare a layer.
menu
----
Declare a menu
menuItem, menuItems
-------------------
Declare menuItems
five ``http://namespaces.zope.org/five``
========================================
implements
----------
Make a class declare it implements an interface.
loadProducts
------------
Loads ZCML in all Zope 2 products. First processes all ``meta.zcml``
files, then processes all ``configure.zcml`` files.
loadProductsOverrides
---------------------
Loads overriding ZCML in all products (``overrides.zcml``).
traversable
-----------
Make a Zope 2 content class traversable in the Zope 3 manner using
Five. This is used to attached views, resources and other things to
Zope 2 objects.
defaultViewable
---------------
Make a Zope 2 content class use a Zope 3 default view when looking at
it without any paths appended to it. This works then instead of
``index_html`` in Zope 2.
pagesFromDirectory
------------------
Load all *.pt files in a directory as pages. Useful when you want to
share templates between Five and CMF, so you can declare pages like
this is a similar way to setting up skin folders in portal_skins.
browser:editform
----------------
Create an edit form based on a schema.
browser:addform
---------------
Create an add form based on a schema.
=============
Five features
=============
Five features are mostly Zope 3 features, though Five has some extras,
and some limitations.
Zope 3 interfaces
=================
Everything in the ``zope.interface`` package should work. Zope 3
interfaces are the foundation of the component architecture, and also
the foundation of schemas.
ZCML
====
ZCML is the Zope Configuration Markup Language, an XML application.
Zope 3 (and Five) code consists of a lot of components that can be
plugged together using ZCML.
If you put a ``site.zcml`` in the home directory of your Zope
instance, this is the root of the ZCML tree. An example of
``site.zcml`` is in ``site.zcml.in``. If you don't place a
``site.zcml``, Five falls back on ``fallback.zcml``.
ZCML in Five has special directive, ``five:loadProducts``, to load the
ZCML (``meta.zcml``, ``configure.zcml``) of all installed Zope 2
products, if available.
Another special directive, ``five:loadProductsOverrides`` is available
to load any overriding ZCML (``overrides.zcml``) in these products. In
the ``overrides.zcml`` you can override existing views or adapters, in
this or in other products.
Adapters
========
You can use adapters in Five, just like in Zope 3.
Zope 3 views
============
Zope 3 views work in Five, including layers and skins. To make them
work however, you need to make a Zope 2 class "traversable". This can
be done by using the ``five:traversable`` directive in ZCML.
Page templates
==============
Five before release 0.3 used to use Zope 3's page template engine, but
in the interests of increased compatibility with Zope 2, we've
switched to using Zope 2's. There should be no real difference to any
code, however. We may decide to switch back to Zope 3's engine again
eventually if we can resolve the compatibility issues.
One thing to be aware of is that the page template engine runs
completely in trusted mode, just like Python code. That is, as soon as
the page template engine is running, no Zope 2 or Zope 3 security
checks are made.
Edit and add forms
==================
Five supports edit and add forms. Typical Zope 3 examples of these
should work.
Security declarations
=====================
Five aims to eradicate ``declareProtected``, ``ClassSecurityInfo`` and
``initializeClass`` from your Zope 2 code.
In order to do this, Five provides the Zope 3 way of declaring
permissions from ZCML, but uses the Zope 2 mechanisms to actually set
them. To declare permissions for methods and templates on views you
use the ``permission`` attribute on the ``browser:page`` directive,
and specify a Zope 2 permission (given a Zope 3 name). You can find a
list of these permissions in ``permissions.zcml`` in Five. The
permission check takes place before the view is executed.
The ``content`` directive can also be used to declare permissions on
Zope 2 content classes. Note however that these permissions will be
ignored by views anyway, as they are trusted -- it only serves to
protect directly exposed methods on content classes (the python
scripts and the ZPublisher).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%deffont "standard" xfont "helvetica-medium-r"
%deffont "thick" xfont "helvetica-bold-r"
%deffont "typewriter" xfont "courier-medium-r"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings per each line numbers.
%%
%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
%default 3 size 2, bar "gray70", vgap 10
%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings that are applied to TAB-indented lines.
%%
%tab 1 size 5, vgap 40, prefix " ", icon box "red" 50
%tab 2 size 4, vgap 40, prefix " ", icon arc "yellow" 50
%tab 3 size 3, vgap 40, prefix " ", icon delta3 "white" 40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Five - Zope 3 in Zope 2
%center
Martijn Faassen, Infrae
faassen@infrae.com
%page
Motto
It was the dawn of the third age of Zope. The Five project was a dream given form. Its goal: to use Zope 3 technologies in Zope 2.7 by creating a Zope 2 product where Zope 3 and Zope 2 could work out their differences peacefully.
(Babylon 5 season 1 intro, creatively quoted)
%page
Motto 2
The Law of Fives states simply that: ALL THINGS HAPPEN IN FIVES, OR ARE DIVISIBLE BY OR ARE MULTIPLES OF FIVE, OR ARE SOMEHOW DIRECTLY OR INDIRECTLY RELATED TO FIVE.
THE LAW OF FIVES IS NEVER WRONG.
(Principia Discordia)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
The problem
We're using Zope 2 in production
Zope 2 is showing its age
Zope 3 has better ways to do things
But can't just switch, we have customers!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Benefits of using Zope 3 in Zope 2
Able to use Zope 3 technologies right away
Don't reinvent the wheel/APIs
Better prepared for Zope 3 transition
Evolution, not revolution
Convergence, not divergence
%page
What works now?
Interfaces (zope.interface)
Schema (zope.schema)
ZCML (zope.configuration)
Adapters (zope.component)
Views, including layers, skins (zope.component)
%page
Brief demo
Show ZCML, adapters and views in action
%page
Next?
Utilities (global ones should work)
Forms
Views (improve the current system)
Who knows?
%page
Plans
Relicense from BSD to generic ZPL 2.1
Move from CVS at Infrae into SVN at codespeak.net
Convergence; join us!
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%deffont "standard" xfont "helvetica-medium-r"
%deffont "thick" xfont "helvetica-bold-r"
%deffont "typewriter" xfont "courier-medium-r"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings per each line numbers.
%%
%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
%default 3 size 2, bar "gray70", vgap 10
%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings that are applied to TAB-indented lines.
%%
%tab 1 size 5, vgap 40, prefix " ", icon box "red" 50
%tab 2 size 4, vgap 40, prefix " ", icon arc "yellow" 50
%tab 3 size 3, vgap 40, prefix " ", icon delta3 "white" 40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Five - Zope 3 in Zope 2
%center
Martijn Faassen, Infrae
faassen@infrae.com
Five developer
%page
Five future directions
What might happen
%page
Unique id service support
Foundation is there in form of events
Unfortunately implementation can not be the same
Objects are referenced differently in Zope 3
Local services/utilities are difficult in Zope 2
Could lead to Zope 3 catalog support
%page
Page template engine improvements
Right now we already use Zope 3 page templates
These are unicode-only (and work with plain ascii)
To support Zope 2 content, need classic non-ascii string support
Issue in Plone, not in Silva (heh heh)
%page
Zope 2.8 and ZODB 3.3
Should be able to work much better with new-style objects
Things like local services/utilities might be doable
%page
Integration with Plone, CMF, Silva, UnionCMS etc
Sharing a common base is good
Zope 3 is good
That base should be Zope 3
Five can help us start sharing today
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%deffont "standard" xfont "helvetica-medium-r"
%deffont "thick" xfont "helvetica-bold-r"
%deffont "typewriter" xfont "courier-medium-r"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings per each line numbers.
%%
%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
%default 3 size 2, bar "gray70", vgap 10
%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings that are applied to TAB-indented lines.
%%
%tab 1 size 5, vgap 40, prefix " ", icon box "red" 50
%tab 2 size 4, vgap 40, prefix " ", icon arc "yellow" 50
%tab 3 size 3, vgap 40, prefix " ", icon delta3 "white" 40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Five - Zope 3 in Zope 2
%center
Martijn Faassen, Infrae
faassen@infrae.com
Five developer
%page
Interfaces, adapters
What are interfaces?
What are adapters?
Why?
A very quick introduction
%page
Actually
This tutorial applies to Zope 3 as much as to Five
Indication Five reached its goal in this area
%page
Interface example
%size 4, fore "blue"
from zope.interface import Interface
class IElephant(Interface):
"""An elephant is a big grey animal.
"""
def getAngerLevel():
"Return anger level on scale 0 (placid) to 10 (raging)"
def trample(target):
"Trample the target."
def trumpet():
"Make loud noise with trunk."
%page
Interface example, continued
%size 4, fore "blue"
from zope.interface import implements
class AfricanElephant:
implements(IElephant)
def getAngerLevel(self):
return 5 # always pretty stroppy
def trample(self, target):
target.flatten()
def trumpet(self):
return "A terrible racket"
%page
Interfaces
Interfaces are about the what, not the how
Interfaces don't do anything, they just describe
Code can state what interfaces objects provide
Code can introspect whether objects provide interfaces
%page
Why interfaces?
They are documentation
Make multiple implementations of same interface easier
Allows you to program against published APIs
Allow glueing by interface
%page
Component architecture
zope.component part of Zope 3
allows glueing together of components in various ways
a component is an object which provides an interface
a Zope 2 object with a Zope 3 interface is a component
%page
Adapters, example
%size 4, fore "blue"
class INoiseMaker(Interface):
"""Something that makes noise.
"""
def makeNoise():
"Returns the noise that's made."
%page
Adapters, example continued
%size 4, fore "blue"
class ElephantNoiseMaker:
"""Adapts elephant to noise maker.
"""
implements(INoiseMaker)
def __init__(self, context):
self.context = context
def makeNoise(self):
return self.context.trumpet()
%page
Adapters, example continued 2
%size 4, fore "blue"
>>> elephant = AfricanElephant()
>>> noise_maker = ElephantNoiseMaker(elephant)
>>> print noise_maker.makeNoise()
'A terrible racket'
%page
Adapters
Add behavior to object without changing its class
More manageable than mixins
Define new behavior in terms of other behavior
%page
Adapters, continued
Less framework burden on adapted objects
They only need to be a component
Adapted doesn't know about the adapter
Adapter is a component itself
%page
Adapter lookup
We just manually glued the adapter to the adapted
What if we had INoiseMaker adapters for other objects?
We want a universal way to say: give me a INoiseMaker for this object
This allows use to write more generic code
%page
Adapter lookup, example
%size 4, fore "blue"
for animal in animal_farm:
noise_maker = INoiseMaker(animal)
print noise_maker.makeNoise()
%page
Adapter glueing
System need to be informed what can adapt what
Zope Configuration Markup Language (ZCML) is used for that
%page
ZCML example
%size 4, fore "blue"
<configure xmlns="http://namespaces.zope.org/zope">
<adapter
for=".module.IElephant"
provides=".module.INoiseMaker"
factory=".module.ElephantNoiseMaker" />
<adapter
for=".other.IChicken"
provides=".module.INoiseMaker"
factory=".other.ChickenNoiseMaker" />
</configure>
%page
ZCML, what we just said
The adapter ElephantNoiseMaker adapts any object that provides IElephant to a INoiseMaker
The adapter ChickenNoiseMaker adapts any object that provides IChicken to a INoiseMaker
%page
This works in Zope 2 with Five
This works in Zope 2 with Five
Your objects just need to be components (provide Zope 3 interfaces)
Your ZCML goes into configure.zcml in your product
That's it
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%deffont "standard" xfont "helvetica-medium-r"
%deffont "thick" xfont "helvetica-bold-r"
%deffont "typewriter" xfont "courier-medium-r"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings per each line numbers.
%%
%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
%default 3 size 2, bar "gray70", vgap 10
%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings that are applied to TAB-indented lines.
%%
%tab 1 size 5, vgap 40, prefix " ", icon box "red" 50
%tab 2 size 4, vgap 40, prefix " ", icon arc "yellow" 50
%tab 3 size 3, vgap 40, prefix " ", icon delta3 "white" 40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Five - Zope 3 in Zope 2
%center
Martijn Faassen, Infrae
faassen@infrae.com
Five developer
%page
An Introduction to Five
Why Five?
What is Five?
Where are we, where are we going?
%page
Motto
It was the dawn of the third age of Zope. The Five project was a dream given form. Its goal: to use Zope 3 technologies in Zope 2.7 by creating a Zope 2 product where Zope 3 and Zope 2 could work out their differences peacefully.
(Babylon 5 season 1 intro, creatively quoted)
%page
Motto 2
The Law of Fives states simply that: ALL THINGS HAPPEN IN FIVES, OR ARE DIVISIBLE BY OR ARE MULTIPLES OF FIVE, OR ARE SOMEHOW DIRECTLY OR INDIRECTLY RELATED TO FIVE.
THE LAW OF FIVES IS NEVER WRONG.
(Principia Discordia)
%page
The problem
We're using Zope 2 in production
Zope 2 is showing its age
Zope 3 has better ways to do things
But can't just switch, we have codebases, customers!
%page
Benefits of using Zope 3 in Zope 2
Able to use Zope 3 technologies right away
Don't reinvent the wheel/APIs
Better prepared for Zope 3 transition
Evolution, not revolution
Convergence, not divergence (this is important)
%page
Divergence
Infrae created Silva, Nuxeo CPS, etc
Everybody else started using Plone (why?!)
I want to use cool Plone technology
Silva is cool too, you may want to use it
I don't want to have to reinvent every wheel (just some)
%page
What's stopping us from sharing?
Zope 2 components are hard to share between apps
Even CMF components need work to share
Especially if you don't use CMF... (Silva)
Zope 2 framework burden is making it hard
Clean Python code is easier to share
%page
Convergence
Unify our diverse efforts
Zope 3 allows you to write Python, less framework sacrifices
Zope 3 allows the glueing of components
Zope 3 is the future
Five makes some of the future available today
%page
What works now? - an overview
Interfaces
Schema
ZCML
Adapters
Views, including layers, skins
%page
What works now, continued
Zope 3 page template engine
Traversal, resources
Zope 2 security from ZCML
Events
Beginnings of forms machinery
%page
Progress made since June
Initial announcement at Europython
As promised, moved to SVN at codespeak.net
Got website, mailing list
People joined the project
%page
Progress made since June, continued
Lots of excellent contributions!
Much better view infrastructure (traversal)
ZCML's interaction with Zope 2 products much improved
UnionCMS and other projects are starting to use it!
%page
The Zope 3 Base
Five is part of the Zope 3 Base
Zope 3 Base - All Your Bobobase Are Belong To Us
Possibly the cutest Zope 3 website anywhere
http://codespeak.net/z3
%page
Zope 3 Base
%center
%image "z3-banner.png"
%page
Zope 3 Base, continued
Second area of Zope 3 related development
Equivalent of Plone collective, for Zope 3
More freewheeling than dev.zope.org
Less freewheeling than Plone collective, however
Cuter than both
%page
Evolution: Five-ification
Five is not just for new Zope 2 projects
Five can interoperate with existing Zope 2 applications
Five in Plone - Flon
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%deffont "standard" xfont "helvetica-medium-r"
%deffont "thick" xfont "helvetica-bold-r"
%deffont "typewriter" xfont "courier-medium-r"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings per each line numbers.
%%
%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
%default 3 size 2, bar "gray70", vgap 10
%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings that are applied to TAB-indented lines.
%%
%tab 1 size 5, vgap 40, prefix " ", icon box "red" 50
%tab 2 size 4, vgap 40, prefix " ", icon arc "yellow" 50
%tab 3 size 3, vgap 40, prefix " ", icon delta3 "white" 40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Five - Zope 3 in Zope 2
%center
Martijn Faassen, Infrae
faassen@infrae.com
Five developer
%page
Five Misc Topics
A number of as-yet uncategorized Five-related topics
%page
Resources
Various kinds of resources available
File, image, page template resource
Accessible through ++resource++ namespace
%page
Resources, example
%size 4, fore "blue"
<browser:resource
image="z3base.png"
name="z3base.png"
permission="zope2.ViewManagementScreens"
/>
%page
Resources
Any Five traversable object now has can be used to get to resource
url: path/to/object/++resource++z3base.png
Jim says this is not exactly Zope 3 as it ruins caching
%page
ZCML
ZCML can optionally be put in etc/site.zcml
If not, Five will automatically use included zcml
This zcml is in skel
%page
ZCML continued
ZCML loads any configure.zcml in all products
This is driven by five:loadProducts in site.zcml
Overrides are possible in override.zcml
This is driven by five:loadProductsOverrides in site.zcml
%page
Bridging interfaces
"Bride of Frankenzope"
Utility functions in bridge.py
Can convert Zope 2 interface to Zope 3 interface
%page
Events
Can instruct Zope 2 object to send Zope 3 style events using five:sendEvents
These events sent upon copy/move/rename in Zope 2
IObjectMovedEvent, IObjectAddedEvent, IObjectCopiedEvent, IObjectRemovedEvent
Can set up functions to subscribe to these events
%page
Content directive and permissions
Use content directive to declare Zope 2 permissions Zope 3 style
Declare permissions from ZCML, no more declareProtected()
Your classes look cleaner as a result
%page
Macros
Zope 3 way to aggregate macros into single object
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%deffont "standard" xfont "helvetica-medium-r"
%deffont "thick" xfont "helvetica-bold-r"
%deffont "typewriter" xfont "courier-medium-r"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings per each line numbers.
%%
%default 1 area 90 90, leftfill, size 2, fore "gray20", back "white", font "standard", hgap 0
%default 2 size 7, vgap 10, prefix " ", ccolor "blue"
%default 3 size 2, bar "gray70", vgap 10
%default 4 size 5, fore "gray20", vgap 30, prefix " ", font "standard"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
%% Default settings that are applied to TAB-indented lines.
%%
%tab 1 size 5, vgap 40, prefix " ", icon box "red" 50
%tab 2 size 4, vgap 40, prefix " ", icon arc "yellow" 50
%tab 3 size 3, vgap 40, prefix " ", icon delta3 "white" 40
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%page
Five - Zope 3 in Zope 2
%center
Martijn Faassen, Infrae
faassen@infrae.com
Five developer
%page
Views with Five
What are views?
Why?
How to make them work?
%page
Actually
This tutorial contains only a few Five specific bits
Otherwise it applies to Zope 3 as much as to Five
The Five specific bits are mainly some extra ZCML directives
These are in their own ZCML namespace
%page
Page example: overview.pt
%size 4, fore "blue"
<html>
<body>
<p tal:content="context/objectIds"></p>
</body>
</html>
%page
Page example: configure.zcml
%size 4, fore "blue"
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five">
<five:traversable
class="OFS.Folder.Folder"
/>
<browser:page
for="Products.Five.interfaces.IFolder"
name="overview.html"
template="overview.pt"
permission="zope2.ViewManagementScreens"
/>
</configure>
%page
What works now
some/folder/overview.html
%page
Hooking up the page, explanation
Much like hooking up an adapter
Adapter provides new interface (API) for developer
View provides new interface (UI) for user
Only five-specific thing is making Folder Zope-3 traversable
Well, and the Zope 2 permission.
%page
Hooking up a page, with class
We need some helper methods
Very similar to the way you'd use Python scripts in Zope 2
%page
View class example: overview2.pt
%size 4, fore "blue"
<html>
<body>
<p tal:content="view/reversedIds"></p>
</body>
</html>
%page
View class example: browser.py
%size 4, fore "blue"
from Products.Five import BrowserView
class Overview(BrowserView):
def reversedIds(self):
result = []
for id in self.context.objectIds():
l = list(id)
l.reverse()
reversed_id = ''.join(l)
result.append(reversed_id)
return result
%page
Example: configure.zcml
%size 4, fore "blue"
<browser:page
for="Products.Five.interfaces.IFolder"
name="overview2.html"
template="overview2.pt"
permission="zope2.ViewManagementScreens"
class=".browser.Overview"
/>
%page
A note on security
There is none: both python code and ZPT are trusted
Only checks are happening on the outside
Performance benefit
Advantage of simplicity
%page
Publishing an attribute
Expose python method on view directly to the web
%page
Attribute example: browser.py
%size 4, fore "blue"
def directlyPublished(self):
return "This is directly published"
%page
Attribute example: configure zcml
%size 4, fore "blue"
<browser:page
for="Products.Five.interfaces.IFolder"
name="test.html"
class=".browser.Overview"
attribute="directlyPublished"
permission="zope2.ViewManagementScreens"
/>
%page
Publishing multiple pages
Convenience directive: browser:pages
%page
Multiple pages example
%size 4, fore "blue"
<browser:pages
for="Products.Five.interfaces.IFolder"
class=".browser.NewExample"
permission="zope2.ViewManagementScreens"
>
<browser:page
name="one.html"
template="one.pt"
/>
<browser:page
name="two.html"
attribute="two"
/>
</browser:pages>
%page
Default view for object
We can now set views that are named
What if we traverse to the object itself?
Use five:defaultViewable and browser:defaultView
%page
Uh oh
This doesn't seem to work yet with Zope folders. Sidnei, help!
So we'll try it with a custom SimpleItem-based object
%page
DefaultView example
%size 4, fore "blue"
<five:defaultViewable class=".democontent.DemoContent" />
<browser:defaultView
for=".democontent.IDemoContent"
name="someview.html" />
%page
Conclusions
This works much the same way as Zope 3 does too
Can supplement existing view systems in Zope 2
Five specific code is mostly isolated in five:traversable and five:defaultViewable
\ No newline at end of file
Five, the Zope 3 in Zope 2 project
==================================
What is Five?
-------------
Five is a Zope 2 product that allows you to integrate Zope 3
technologies into Zope 2, today. Five right now allows you to use the
following Zope 3 technologies in Zope 2:
* Zope 3 interfaces
* adapters
* pages (views), including skins and layers, and edit and add forms
* ZCML
It is possible to add Zope 3 style views to your own Zope 2 objects,
or to existing ones, even normal Folders!
Five works with a straight Zope 2.7 installation, as long as Zope 3
has been installed. See Five's INSTALL.txt for more information on how
to set it up.
We're in the process of evaluating lots more Zope 3 technologies for
integration into Zope 2. This is the right moment for interested Zope
2 and Zope 3 developers to jump in. We're looking for cooperation
between different Zope 2 projects so that this can be a foundational
system for us all.
Download
--------
2005-003-11 -- We have released Five 0.3! Download it here:
http://codespeak.net/z3/five/release/Five-0.3.tgz
And changes here:
http://codespeak.net/z3/five/CHANGES.html
2004-09-24 -- Five 0.2b is released. Download it here:
http://codespeak.net/z3/five/release/Five-0.2b.tgz
2004-07-30 -- We have released Five 0.1! Download it here:
http://codespeak.net/z3/five/release/Five-0.1.tgz
Joining the project
-------------------
Five is kindly hosted on codespeak.net, and is part of the larger
*Zope 3 Base* project that offers an approachable area for
developers of Zope 3 related software.
Five has a mailing list:
http://codespeak.net/mailman/listinfo/z3-five
We're also active on IRC, at ``#z3-base`` on freenode.
Five is hosted in a subversion repository on codespeak.net. You can
browse this on the web here:
http://codespeak.net/svn/z3/Five/
You can check out Five using the following subversion command::
svn co http://codespeak.net/svn/z3/Five/trunk Five
There's also a checkins mailing list for the Z3 project, here:
http://codespeak.net/mailman/listinfo/z3-checkins
If you want checkin access, please join the z3-five mailing list or
the ``#z3-base`` IRC channel, and ask us there.
We hope to hear from you!
===========
Five Manual
===========
Introduction
------------
Five's goal is to let you, the Zope 2 developer, use Zope 3 code in
Zope 2. Our aim is to make as much of Zope 3 code work in Zope 2 as
possible, while integrating it with Zope 2.
Five can be used inside your current Zope 2 project. The benefits are:
* availability of Zope 3 technologies in Zope 2 like the component
architecture and declarative configuration.
* you can gradually evolve your Zope 2 project so it is better
positioned for the migration to Zope 3.
* you start learning about Zope 3 right now, preparing yourself better
for the future. Since Zope 3 is open to contributions, you could
even influence your future for the better.
Five can also be used to develop new Zope 2 products, though depending
on your deployment requirements it might in that case make more sense
to develop for Zope 3 directly.
Five is only useful on the Python (Product) level in Zope 2, not from
within the Zope Management Interface. Five makes no attempt to provide
a user interface, but is aimed squarely at the Python developer.
Zope 3 interfaces
-----------------
Interfaces?
===========
An interface is simply a description of what an object provides to the
world, i.e. its public attribute and methods. It looks very much like
a class, but contains no implementation::
from zope.interface import Interface
# by convention, all interfaces are prefixed with ``I``
class IElephant(Interface):
"""An elephant is a big object that barely fits in the cupboard.
"""
def getAngerLevel():
"""Anger level, maximum of 100.
The longer the elephant has been in the cupboard, the angrier.
"""
def isInCupboard():
"""Returns true if the elephant is indeed in cupboard.
"""
def trunkSmash(target):
"""Smash the target with trunk.
The anger level determines the force of the hit.
"""
def trample(target):
"""Trample the target.
The anger level determines the rate of flattening of the target.
"""
A concrete class somewhere can now claim that it implements the
interface (i.e. its instance will provide the interface)::
class PinkElephant:
# this says all instances of this class provide IElephant
implements(IElephant)
def getAngerLevel(self):
return 0 # this elephant is peaceful
def isInCupboard(self):
return False # it's never in a cupboard but can be found in bottles
def trunkSmash(self, target):
target.tickle()
def trample(self, target):
target.patOnHead()
Interfaces themselves are good for a number of reasons:
* They provide API documentation.
* They help you make explicit the design of your application,
hopefully improving it.
* If an object provides an interface, that object is considered to be
a *component*. This means you can use Zope 3's component
architecture with these objects.
In order to use Five, you'll have to make your objects provide
interfaces. Sometimes, you cannot change the code of class (as you are
not the maintainer), but you still want to make it implement an
interface. Five provides a ZCML directive to do this::
<five:implements class="tolkien.Oliphant"
implements="interfaces.IElephant" />
Interfaces in Zope 2 versus Zope 3
==================================
You may be familiar with Zope 2's way of declaring interfaces. Zope 2
has used the ``__implements__`` class attribute for interface
declarations. Zope 2 cannot detect Zope 3 interfaces and the Zope 3
machinery cannot detect Zope 2 interfaces. This is a good thing, as
Zope 2 has no way to deal with Zope 3 interfaces, and Zope 3 cannot
comprehend Zope 2 interfaces. This means you can safely make a class
declare both a Zope 2 and Zope 3 interface independently from each
other. It's a rare case where you need this though; you're usually
better off just switching to ``implements()`` for your application if
you are using Five.
Switching from Zope 2 interfaces to Zope 3 interfaces is easy -- just
make your interfaces inherit from ``zope.interface.Interface`` instead
of ``Interface.Interface`` (or ``Interface.Base``). Next, change all
``__implements__`` to ``implements()``.
This should get you going and your application may very well still
work. Later on, you will also have to change calls to
``isImplementedBy`` and such in your application to ``providedBy``, as
``isImplementedBy`` has been deprecated (you'll see the
DeprecationWarnings in your Zope log).
Adapters
--------
From a Python programmer's perspective, the immediate thing that Five
brings to do the table are adapters. This section goes through some
demo code to explain how everything is tied
together. ``demo/FiveDemo`` is a demo Product you can install and
examine that has all the presented here together.
Zope 3 adapters depend on Zope 3 interfaces. To create a Zope 3
interface you need to subclass it from
``zope.interface.Interface``. Here is an example::
from zope.interface import Interface
class IMyInterface(Interface):
"""This is a Zope 3 interface.
"""
def someMethod():
"""This method does amazing stuff.
"""
Now to make some class declare that it implements this interface, you
need to use the ``implements()`` function in the class::
from zope.interface import implements
from interfaces import IMyInterface
class MyClass:
implements(IMyInterface)
def someMethod(self):
return "I am alive! Alive!"
For an explanation of the relation of Zope 3 interfaces to Zope 2
interfaces, see below.
Now let's set up the interface that we are adapting to::
class INewInterface(Interface):
"""The interface we adapt to.
"""
def anotherMethod():
"""This method does more stuff.
"""
Next we'll work on the class that implements the adapter. The
requirement to make a class that is an adapter is very simple; you
only need to take a context object as the constructor. The context
object is the object being adapted. An example::
from zope.interface import implements
from interfaces import INewInterface
class MyAdapter:
implements(INewInterface)
def __init__(self, context):
self.context = context
def anotherMethod(self):
return "We have adapted: %s" % self.context.someMethod()
Next, we hook it all up using zcml. If the classes are in a module
called ``classes.py`` and the interfaces in a module called
``interfaces.py``, we can declare ``MyAdapter`` to be an adapter for
``IMyInterface`` to ``INewInterface`` like this (in a file called
``configure.zcml``)::
<configure xmlns="http://namespaces.zope.org/zope">
<adapter
for=".interfaces.IMyInterface"
provides=".interfaces.INewInterface"
factory=".classes.MyAdapter" />
</configure>
Five will automatically pickup ``configure.zcml`` when it's placed in
the product's directory. Any object that provides ``INewInterface``
can now be adapted to ``INewInterface``, like this::
from classes import MyClass
from interfaces import INewInterface
object = MyClass()
adapted = INewInterface(object)
print adapted.anotherMethod()
Views in Five
-------------
This section will give a brief introduction on how to use the five
view system. ``demo/FiveViewsDemo`` is a demo Product you can install
and examine that has all the presented here tied together, please
consult it for more details. ``tests/products/FiveTest`` actually
contains a more detailed set of test views, trying a number of
features. Finally, read up on the way Zope 3 does it. While Five is a
subset of Zope 3 functionality and has been adapted to work with Zope
2, much of Zope 3's documentation still works.
Five enables you to create views for your own objects, or even built-in
Zope objects, as long as two things are the case:
* The object provides an Zope 3 interface, typically through its class.
* The object (typically its class) is made Zope 3 traversable. This
allows Zope 3 views, resources and other things to be attached to a
Zope 2 object.
Typically you give your classes an interface using the ``implements``
directive in the class body::
class MyClass:
implements(ISomeInterface)
For existing objects that you cannot modify this is not
possible. Instead, we provide a ZCML directive to accomplish this. As
an example, to make Zope's ``Folder`` (and all its subclasses)
implement ``IFolder`` (an interface you defined), you can do the
following in ZCML::
<five:implements class="OFS.Folder.Folder"
interface=".interfaces.IFolder" />
``five`` in this case refers to the XML namespace for Five,
``http://namespace.zope.org/five``.
We've provided another ZCML directive to make an object
traversable. To make your MyClass traversable, let's assume it is in
``mymodule``, in the same package as the zcml file we are editing::
<five:traversable class=".mymodule.MyClass" />
To continue our example, to make Zope's ``Folder`` traversable through
Five, you need to declare this in ZCML as well:
<five:traversable class="OFS.Folder.Folder"/>
This makes Folder traverse in the Zope 3 way first, looking up views
and other things, and then if they cannot be found, fall back on the
regular Zope 2 traversal. It does this by overriding the
``__bobo_traverse__`` hook. Old hooks that are already in place in an
object will be stored and become the secondary fallback. This allows
the ZMI to work still, but new views can be added on the fly.
Note that at the point of writing it is only possible to make an object
viewable through ZCML if this object does not already provide its own
``__bobo_traverse__`` method.
Views in Five are simple classes. The only requirements for a Five
view class are:
* They need an ``__init__()`` that take a context and a request
attribute. Typically this comes from a base class, such as
``BrowserView``.
* They need to be initialized with the Zope 2 security system, as
otherwise you cannot use the view.
* This also means they need to be part of the Zope 2 acquisition
system, as this is a requirement for Zope 2 security to
function. The ``BrowserView`` base class, available from
``Products.Five``, already inherits from ``Acquisition.Explicit`` to
make this be the case. Acquisition is explicit so no attributes can
be acquired by accident.
An example of a simple view::
from Products.Five import BrowserView
class SimpleFolderView(BrowserView):
security = ClassSecurityInfo()
security.declarePublic('eagle')
def eagle(self):
"""Test
"""
return "The eagle has landed: %s" % self.context.objectIds()
InitializeClass(SimpleFolderView)
Note that it is not a good idea to give a view class its own
``index_html``, as this confuses Five's view lookup machinery.
As you can see, the class is initialized with the Zope 2 security
system. This view uses methods in Python, but you can also use other
Zope 2 mechanisms such as ``PageTemplateFile``.
Finally, we need to hook up the pages through ZCML::
<browser:page
for=".interfaces.IFolder"
class=".browser.SimpleFolderView"
attribute="eagle"
name="eagle.txt"
permission="zope2.ViewManagementScreens"
/>
``browser`` in this refers to the XML namespace of Zope 3 for browser
related things; it's
``http://namespace.zope.org/browser``. ``permission`` declares the
Zope 2 permission needs in order to access this view. The file
``permissions.zcml`` in Five contains a mapping of Zope 2 permissions
to their Zope 3 names.
##############################################################################
#
# 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.
#
##############################################################################
# this is a package.
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five"
>
<five:traversable class="OFS.Folder.Folder" />
<browser:resource
image="z3base.png"
name="z3base.png"
permission="zope2.ViewManagementScreens"
/>
</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.
#
##############################################################################
import module, other
def initialize(context):
print "*" * 70
module.demo_manual_adaptation()
other.demo_animal_farm()
print "*" * 70
<configure xmlns="http://namespaces.zope.org/zope">
<adapter
for=".module.IElephant"
provides=".module.INoiseMaker"
factory=".module.ElephantNoiseMaker" />
<adapter
for=".other.IChicken"
provides=".module.INoiseMaker"
factory=".other.ChickenNoiseMaker" />
</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.
#
##############################################################################
from zope.interface import Interface, implements
class IElephant(Interface):
"""An elephant is a big grey animal.
"""
def getAngerLevel():
"Return anger level on scale 0 (placid) to 10 (raging)"
def trample(target):
"Trample the target."
def trumpet():
"Make loud noise with trunk."
def terribleRacket():
return "A terrible racket"
class AfricanElephant:
implements(IElephant)
def getAngerLevel(self):
return 5 # always pretty stroppy
def trample(self, target):
target.flatten()
def trumpet(self):
return "A terrible racket"
class INoiseMaker(Interface):
"""Something that makes noise.
"""
def makeNoise():
"Returns the noise that's made."
class ElephantNoiseMaker:
"""Adapts elephant to noise maker.
"""
implements(INoiseMaker)
def __init__(self, context):
self.context = context
def makeNoise(self):
return self.context.trumpet()
def demo_manual_adaptation():
elephant = AfricanElephant()
noise_maker = ElephantNoiseMaker(elephant)
print noise_maker.makeNoise()
##############################################################################
#
# 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.
#
##############################################################################
from zope.interface import Interface, implements
from module import INoiseMaker, IElephant, AfricanElephant
class IChicken(Interface):
def getConfusionLevel():
"Get the confusion level of this chicken, 0 asleep, 10 frantic"
def cluck():
"""Return clucking sound of the chicken.
"""
class Chicken:
implements(IChicken)
def __init__(self, confusion_level):
self._confusion_level = confusion_level
def getConfusionLevel(self):
return self._confusion_level
def cluck(self):
return ' '.join(["cluck"] * self.getConfusionLevel())
class IndianElephant:
implements(IElephant)
def __init__(self, anger_level):
self._anger_level = anger_level
def hit(self):
"""Hit the indian elephant with a stick.
"""
if self._anger_level <= 10:
self._anger_level += 1
def getAngerLevel(self):
return self._anger_level
def trumpet(self):
return "t" + ("o" * self._anger_level) + "t"
class ChickenNoiseMaker:
implements(INoiseMaker)
def __init__(self, context):
self.context = context
def makeNoise(self):
return self.context.cluck()
def demo_animal_farm():
animal_farm = [Chicken(5), AfricanElephant(),
Chicken(3), IndianElephant(3)]
for animal in animal_farm:
noise_maker = INoiseMaker(animal)
print noise_maker.makeNoise()
This directory contains Five tutorial products.
##############################################################################
#
# 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.
#
##############################################################################
import democontent
def initialize(context):
context.registerClass(
democontent.DemoContent,
constructors = (democontent.manage_addDemoContentForm,
democontent.manage_addDemoContent),
)
##############################################################################
#
# 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.
#
##############################################################################
from Products.Five import BrowserView
import random
class Overview(BrowserView):
def reversedIds(self):
result = []
for id in self.context.objectIds():
l = list(id)
l.reverse()
reversed_id = ''.join(l)
result.append(reversed_id)
return result
def directlyPublished(self):
return "This is directly published"
class NewExample(BrowserView):
def helpsWithOne(self):
return random.randrange(10)
def two(self):
return "Two got called"
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser"
xmlns:five="http://namespaces.zope.org/five">
<five:traversable
class="OFS.Folder.Folder"
/>
<browser:page
for="Products.Five.interfaces.IFolder"
name="overview.html"
template="overview.pt"
permission="zope2.ViewManagementScreens"
/>
<browser:page
for="Products.Five.interfaces.IFolder"
name="overview2.html"
template="overview2.pt"
permission="zope2.ViewManagementScreens"
class=".browser.Overview"
/>
<browser:page
for="Products.Five.interfaces.IFolder"
name="test.html"
class=".browser.Overview"
attribute="directlyPublished"
permission="zope2.ViewManagementScreens"
/>
<browser:pages
for="Products.Five.interfaces.IFolder"
class=".browser.NewExample"
permission="zope2.ViewManagementScreens"
>
<browser:page
name="one.html"
template="one.pt"
/>
<browser:page
name="two.html"
attribute="two"
/>
</browser:pages>
<five:traversable class=".democontent.DemoContent" />
<browser:page
for=".democontent.IDemoContent"
name="someview.html"
template="someview.pt"
permission="zope2.ViewManagementScreens"
/>
<five:defaultViewable class=".democontent.DemoContent" />
<browser:defaultView
for=".democontent.IDemoContent"
name="someview.html" />
</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.
#
##############################################################################
from zope.interface import Interface, implements
from OFS.SimpleItem import SimpleItem
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
class IDemoContent(Interface):
def mymethod():
"Return some text"
class DemoContent(SimpleItem):
implements(IDemoContent)
meta_type = 'Five Demo Content'
def __init__(self, id, title):
self.id = id
self.title = title
def mymethod(self):
return "Hello world"
manage_addDemoContentForm = PageTemplateFile(
"www/demoContentAdd", globals(),
__name__ = 'manage_addDemoContentForm')
def manage_addDemoContent(self, id, title, REQUEST=None):
"""Add the demo content."""
id = self._setObject(id, DemoContent(id, title))
if REQUEST is None:
return
REQUEST.RESPONSE.redirect(REQUEST['URL1'] + '/manage_main')
<html>
<body>
<p>The random number is <span tal:replace="view/helpsWithOne">0</span></p>
</body>
</html>
<html>
<body>
<p tal:content="context/objectIds"></p>
</body>
</html>
<html>
<body>
<p tal:content="view/reversedIds"></p>
</body>
</html>
<html>
<body>
<p>Some view</p>
</body>
</html>
<h1 tal:replace="structure here/manage_page_header">Header</h1>
<h2 tal:define="form_title string:Add Demo Content"
tal:replace="structure here/manage_form_title">Form Title</h2>
<p class="form-help">
Add Demo Content
</p>
<form action="manage_addDemoContent" 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>
<tal:tag condition="view/update"/>
<html metal:use-macro="context/@@standard_macros/page">
<body metal:fill-slot="body">
<div metal:define-macro="body">
<form action="." tal:attributes="action request/URL" method="POST"
enctype="multipart/form-data">
<div metal:define-macro="formbody">
<h3 tal:condition="view/label"
tal:content="view/label"
metal:define-slot="heading"
>Edit something</h3>
<p tal:define="status view/update"
tal:condition="status"
tal:content="status" />
<p tal:condition="view/errors" i18n:translate="">
There are <strong tal:content="python:len(view.errors)"
i18n:name="num_errors">6</strong> input errors.
</p>
<div metal:define-slot="extra_info" tal:replace="nothing">
</div>
<div class="row"
metal:define-slot="extra_top" tal:replace="nothing">
<div class="label">Extra top</div>
<div class="field"><input type="text" style="width:100%" /></div>
</div>
<div metal:use-macro="context/@@widget_macros/widget_rows" />
<div class="separator"></div>
<div class="row"
metal:define-slot="extra_bottom" tal:replace="nothing">
<div class="label">Extra bottom</div>
<div class="field"><input type="text" style="width:100%" /></div>
</div>
<div class="separator"></div>
</div>
<div class="row">
<div class="controls">
<input type="submit" value="Refresh"
i18n:attributes="value refresh-button" />
<input type="submit" name="UPDATE_SUBMIT" value="Change"
i18n:attributes="value submit-button"/>
</div>
</div>
<div class="row" metal:define-slot="extra_buttons" tal:replace="nothing">
</div>
<div class="separator"></div>
</form>
</div>
</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.
#
##############################################################################
"""
Use 'structured monkey patching' to enable zope.app.container event sending for
Zope 2 objects.
"""
from Products.Five.fiveconfigure import isFiveMethod
from zope.event import notify
from zope.interface import implements
from zope.app.container.interfaces import IObjectAddedEvent,\
IObjectRemovedEvent
from zope.app.container.contained import ObjectMovedEvent
from zope.app.event.objectevent import ObjectCopiedEvent
# ObjectAddedEvent and ObjectRemovedEvent are different in Zope 2
class ObjectAddedEvent(ObjectMovedEvent):
implements(IObjectAddedEvent)
def __init__(self, object, newParent=None, newName=None):
if newParent is None:
newParent = object.aq_inner.aq_parent
if newName is None:
newName = object.id
ObjectMovedEvent.__init__(self, object, None, None, newParent, newName)
class ObjectRemovedEvent(ObjectMovedEvent):
implements(IObjectRemovedEvent)
def __init__(self, object, oldParent=None, oldName=None):
if oldParent is None:
oldParent = object.aq_inner.aq_parent
if oldName is None:
oldName = object.id
ObjectMovedEvent.__init__(self, object, oldParent, oldName, None, None)
def manage_afterAdd(self, item, container):
original_location_path = getattr(self, '__five_location_path__', None)
self.__five_location_path__ = self.getPhysicalPath()
# if there still is an object in the original location, we're copied
# we cannot rely on manage_afterClone, as this gets triggered only
# *after* a manage_afterAdd. This logic might fail in the case where
# something *is* somehow left in the original location that can
# be traversed to.
is_copied = original_location_path and (self.unrestrictedTraverse(
original_location_path, None) is not None)
if is_copied:
notify(ObjectCopiedEvent(self))
if original_location_path is None or is_copied:
notify(ObjectAddedEvent(self))
else:
original_location = self.unrestrictedTraverse(
original_location_path[:-1])
notify(ObjectMovedEvent(self,
original_location, original_location_path[-1],
container, self.id))
# call original
method = getattr(self, '__five_original_manage_afterAdd', None)
if method is not None:
self.__five_original_manage_afterAdd(item, container)
manage_afterAdd.__five_method__ = None
def manage_beforeDelete(self, item, container):
notify(ObjectRemovedEvent(self))
# call original
method = getattr(self, '__five_manage_beforeDelete', None)
if method is not None:
self._five_original_manage_beforeDelete(item, container)
manage_beforeDelete.__five_method__ = None
def classSendEvents(class_):
"""Make instances of the class send Object*Event.
"""
# tuck away original methods if necessary
for name in ['manage_afterAdd', 'manage_beforeDelete']:
method = getattr(class_, name, None)
if not isFiveMethod(method):
# if we haven't alread overridden this, tuck away originals
setattr(class_, '__five_original_' + name, method)
class_.manage_afterAdd = manage_afterAdd
class_.manage_beforeDelete = manage_beforeDelete
def sendEvents(_context, class_):
_context.action(
discriminator = ('five:sendEvents', class_),
callable = classSendEvents,
args=(class_,)
)
<html metal:define-macro="page">
<head>
<metal:block define-slot="css_slot">
</metal:block>
</head>
<body>
<metal:block define-slot="body">
</metal:block>
</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.
#
##############################################################################
"""Five-specific directive handlers
These directives are specific to Five and have no equivalents in Zope 3.
$Id: fiveconfigure.py 8255 2005-01-13 14:05:46Z regebro $
"""
import os
import glob
import warnings
from zope.interface import classImplements
from zope.configuration import xmlconfig
from zope.app.component.interface import provideInterface
from viewable import Viewable
from traversable import Traversable
from bridge import fromZ2Interface
from browserconfigure import page
def findProducts():
import Products
from types import ModuleType
products = []
for name in dir(Products):
product = getattr(Products, name)
if isinstance(product, ModuleType) and hasattr(product, '__file__'):
products.append(product)
return products
def loadProducts(_context):
products = findProducts()
# first load meta.zcml files
for product in products:
zcml = os.path.join(os.path.dirname(product.__file__), 'meta.zcml')
if os.path.isfile(zcml):
xmlconfig.include(_context, zcml, package=product)
# now load their configure.zcml
for product in products:
zcml = os.path.join(os.path.dirname(product.__file__),
'configure.zcml')
if os.path.isfile(zcml):
xmlconfig.include(_context, zcml, package=product)
def loadProductsOverrides(_context):
for product in findProducts():
zcml = os.path.join(os.path.dirname(product.__file__),
'overrides.zcml')
if os.path.isfile(zcml):
xmlconfig.includeOverrides(_context, zcml, package=product)
def implements(_context, class_, interface):
for interface in interface:
_context.action(
discriminator = None,
callable = classImplements,
args = (class_, interface)
)
_context.action(
discriminator = None,
callable = provideInterface,
args = (interface.__module__ + '.' + interface.getName(),
interface)
)
def isFiveMethod(m):
return hasattr(m, '__five_method__')
def classTraversable(class_):
# If a class already has this attribute, it means it is either a
# subclass of Traversable or was already processed with this
# directive; in either case, do nothing... except in the case were
# the class overrides __bobo_traverse__ instead of getting it from
# a base class. In this case, we suppose that the class probably
# didn't bother with the base classes __bobo_traverse__ anyway and
# we step __fallback_traverse__.
if hasattr(class_, '__five_traversable__'):
if (hasattr(class_, '__bobo_traverse__') and
isFiveMethod(class_.__bobo_traverse__)):
return
if hasattr(class_, '__bobo_traverse__'):
if not isFiveMethod(class_.__bobo_traverse__):
# if there's an existing bobo_traverse hook already, use that
# as the traversal fallback method
setattr(class_, '__fallback_traverse__', class_.__bobo_traverse__)
if not hasattr(class_, '__fallback_traverse__'):
setattr(class_, '__fallback_traverse__',
Traversable.__fallback_traverse__.im_func)
setattr(class_, '__bobo_traverse__',
Traversable.__bobo_traverse__.im_func)
setattr(class_, '__five_traversable__', True)
def traversable(_context, class_):
_context.action(
discriminator = None,
callable = classTraversable,
args = (class_,)
)
def classDefaultViewable(class_):
# If a class already has this attribute, it means it is either a
# subclass of DefaultViewable or was already processed with this
# directive; in either case, do nothing... except in the case were
# the class overrides the attribute instead of getting it from
# a base class. In this case, we suppose that the class probably
# didn't bother with the base classes attribute anyway.
if hasattr(class_, '__five_viewable__'):
if (hasattr(class_, '__browser_default__') and
isFiveMethod(class_.__browser_default__)):
return
if hasattr(class_, '__browser_default__'):
# if there's an existing __browser_default__ hook already, use that
# as the fallback
if not isFiveMethod(class_.__browser_default__):
setattr(class_, '__fallback_default__', class_.__browser_default__)
if not hasattr(class_, '__fallback_default__'):
setattr(class_, '__fallback_default__',
Viewable.__fallback_default__.im_func)
setattr(class_, '__browser_default__',
Viewable.__browser_default__.im_func)
setattr(class_, '__five_viewable__', True)
def defaultViewable(_context, class_):
_context.action(
discriminator = None,
callable = classDefaultViewable,
args = (class_,)
)
def viewable(_context, class_):
# XXX do not need to mark where this is used, as simple search
# should find all instances easily
warnings.warn(
'The five:viewable directive has been deprecated. '
'Please use the five:traversable directive instead.',
DeprecationWarning)
_context.action(
discriminator = None,
callable = classTraversable,
args=(class_,)
)
def createZope2Bridge(zope2, package, name):
# Map a Zope 2 interface into a Zope3 interface, seated within 'package'
# as 'name'.
z3i = fromZ2Interface(zope2)
if name is not None:
z3i.__dict__['__name__'] = name
z3i.__dict__['__module__'] = package.__name__
setattr(package, z3i.getName(), z3i)
def bridge(_context, zope2, package, name=None):
# Directive handler for <five:bridge> directive.
# N.B.: We have to do the work early, or else we won't be able
# to use the synthesized interface in other ZCML directives.
createZope2Bridge(zope2, package, name)
# Faux action, only for conflict resolution.
_context.action(
discriminator = (zope2,),
)
def pagesFromDirectory(_context, directory, module, for_=None,
layer='default', permission='zope.Public'):
if isinstance(module, basestring):
module = _context.resolve(module)
_prefix = os.path.dirname(module.__file__)
directory = os.path.join(_prefix, directory)
if not os.path.isdir(directory):
raise ConfigurationError(
"Directory %s does not exist" % directory
)
for fname in glob.glob(os.path.join(directory, '*.pt')):
name = os.path.splitext(os.path.basename(fname))[0]
page(_context, name=name, permission=permission,
layer=layer, for_=for_, template=fname)
##############################################################################
#
# 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.
#
##############################################################################
"""Five ZCML directive schemas
$Id: fivedirectives.py 8255 2005-01-13 14:05:46Z regebro $
"""
from zope.interface import Interface
from zope.app.publisher.browser.metadirectives import IBasicResourceInformation
from zope.configuration.fields import GlobalObject, Tokens, PythonIdentifier
from zope.schema import TextLine
class IImplementsDirective(Interface):
"""State that a class implements something.
"""
class_ = GlobalObject(
title=u"Class",
required=True
)
interface = Tokens(
title=u"One or more interfaces",
required=True,
value_type=GlobalObject()
)
class ITraversableDirective(Interface):
"""Make instances of class traversable publically.
This can be used to browse to pages, resources, etc.
Traversal can be controlled by registering an ITraverser adapter.
"""
class_ = GlobalObject(
title=u"Class",
required=True
)
class IDefaultViewableDirective(Interface):
"""Make instances of class viewable publically.
The default view is looked up using a IBrowserDefault adapter.
"""
class_ = GlobalObject(
title=u"Class",
required=True
)
class ISendEventsDirective(Interface):
"""Make instances of class send events.
"""
class_ = GlobalObject(
title=u"Class",
required=True
)
class IBridgeDirective(Interface):
"""Bridge from a Zope 2 interface to an equivalent Zope3 interface.
"""
zope2 = GlobalObject(
title=u"Zope2",
required=True
)
package = GlobalObject(
title=u"Target package",
required=True
)
name = PythonIdentifier(
title=u"Zope3 Interface name",
description=u"If not supplied, the new interface will have the same "
u"name as the source interface.",
required=False
)
class IPagesFromDirectoryDirective(IBasicResourceInformation):
"""Register each file in a skin directory as a page resource
"""
for_ = GlobalObject(
title=u"The interface this view is for.",
required=False
)
module = GlobalObject(
title=u"Module",
required=True
)
directory = TextLine(
title=u"Directory",
description=u"The directory containing the resource data.",
required=True
)
##############################################################################
#
# Copyright (c) 2000-2003 Zope Corporation and Contributors.
# Copyright (c) 2004 Five 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 interfaces
$Id: interfaces.py 8253 2005-01-13 12:49:07Z regebro $
"""
from zope.interface import Interface, Attribute
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):
"""Provide a hook for deciding about the default view for an object"""
def defaultView(self, request):
"""Return the object to be published
(usually self) and a sequence of names to traverse to
find the method to be published.
"""
class IAcquisitionWrapper(Interface):
def acquire(name, filter=0, extra=None, expl=0, default=0,
explicit=1, containment=0):
"""Get an attribute, acquiring it if necessary"""
aq_acquire = acquire
def aq_inContextOf(obj, inner=1):
"""Test whether the object is currently in the context of the
argument"""
class IAcquisition(Interface):
def __of__(context):
"""Return the object in a context"""
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"""
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():
"""Dispatch to first interface in manage_options"""
def tabs_path_default(REQUEST):
""" """
def tabs_path_info(script, path,):
""" """
def class_manage_path(self):
""" """
manage_options = Tuple(
title=u"Manage options",
)
manage_tabs = Attribute("""Management tabs""")
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."""
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. """
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(EQUEST, 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(self, 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():
""" """
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(self, 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(self, id):
"""Called to set the new id of a copied object."""
def cb_isCopyable(self):
"""Is object copyable? Returns 0 or 1"""
def cb_isMoveable(self):
"""Is object moveable? Returns 0 or 1"""
def cb_userHasCopyOrMovePermission(self):
""" """
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"""
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(self):
""" """
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(self):
""" """
def manage_fixupOwnershipAfterAdd(self):
""" """
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):
""" """
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",
)
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 _setId(id):
"""Set the id"""
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"""
class IItemWithName(IItem):
"""Item with name"""
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."""
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"""
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():
""" """
class ISimpleItem(IItem, IPersistent, IAcquisition, IRoleManager):
"""Not-so-simple item"""
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(self, REQUEST):
""" """
def manage_CopyContainerAllItems(self, REQUEST):
return map(lambda i, s=self: s._getOb(i), tuple(REQUEST['ids']))
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)."""
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(""" """))
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():
""" """
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):
""" """
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'."""
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"""
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."""
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
"""
class IOrderedFolder(IOrderedContainer, IFolder):
"""Ordered folder"""
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():
"""Check the global (zclass) registry for problems, which can
be caused by things like disk-based products being deleted.
Return true if a problem is found"""
class IMenuItemType(IInterface):
"""Menu item type
Menu item types are interfaces that define classes of
menu items.
"""
\ No newline at end of file
<configure xmlns="http://namespaces.zope.org/five">
<!-- IPersistent, IPersistentExtra -->
<!-- Acquisition -->
<implements
class="Acquisition.ImplicitAcquisitionWrapper"
interface=".interfaces.IAcquisitionWrapper"
/>
<implements
class="Acquisition.ExplicitAcquisitionWrapper"
interface=".interfaces.IAcquisitionWrapper"
/>
<implements
class="Acquisition.Implicit"
interface=".interfaces.IAcquisition"
/>
<implements
class="Acquisition.Explicit"
interface=".interfaces.IAcquisition"
/>
<!-- DAV -->
<implements
class="webdav.Lockable.LockableItem"
interface=".interfaces.IWriteLock"
/>
<implements
class="webdav.Resource.Resource"
interface=".interfaces.IDAVResource"
/>
<implements
class="webdav.Collection.Collection"
interface=".interfaces.IDAVCollection"
/>
<!-- OFS -->
<implements
class="OFS.CopySupport.CopySource"
interface=".interfaces.ICopySource"
/>
<implements
class="OFS.CopySupport.CopyContainer"
interface=".interfaces.ICopyContainer"
/>
<implements
class="OFS.Traversable.Traversable"
interface=".interfaces.ITraversable"
/>
<implements
class="OFS.SimpleItem.Item"
interface=".interfaces.IItem"
/>
<implements
class="OFS.SimpleItem.Item_w__name__"
interface=".interfaces.IItemWithName"
/>
<implements
class="OFS.SimpleItem.SimpleItem"
interface=".interfaces.ISimpleItem"
/>
<implements
class="OFS.ObjectManager.ObjectManager"
interface=".interfaces.IObjectManager"
/>
<implements
class="OFS.PropertyManager.PropertyManager"
interface=".interfaces.IPropertyManager"
/>
<implements
class="OFS.FindSupport.FindSupport"
interface=".interfaces.IFindSupport"
/>
<implements
class="OFS.Folder.Folder"
interface=".interfaces.IFolder"
/>
<implements
class="OFS.OrderSupport.OrderSupport"
interface=".interfaces.IOrderedContainer"
/>
<implements
class="OFS.OrderedFolder.OrderedFolder"
interface=".interfaces.IOrderedFolder"
/>
<implements
class="OFS.Application.Application"
interface=".interfaces.IApplication"
/>
<!-- App -->
<implements
class="App.Undo.UndoSupport"
interface=".interfaces.IUndoSupport"
/>
<implements
class="App.Management.Navigation"
interface=".interfaces.INavigation"
/>
<!-- AccessControl -->
<implements
class="AccessControl.Owned.Owned"
interface=".interfaces.IOwned"
/>
<implements
class="AccessControl.PermissionMapping.RoleManager"
interface=".interfaces.IPermissionMapping"
/>
<implements
class="AccessControl.Role.RoleManager"
interface=".interfaces.IRoleManager"
/>
</configure>
NAME=Five
PYTHON="/usr/bin/python"
TMPDIR=~/tmp
CURDIR=~/src/projects/Five
BASE_DIR=${CURDIR}/..
SOFTWARE_HOME=~/src/zope/2_7/lib/python
INSTANCE_HOME=~/src/instance/five
ZOPE_CONFIG=~/src/instance/five/etc/zope.conf
Z3=~/src/z3/five
.PHONY : clean test reindent reindent_clean
.PHONY : default
# default: The default step (invoked when make is called without a target)
default: clean test
clean :
find . \( -name '*~' -o -name '*.py[co]' -o -name '*.bak' -o -name '\.#*' \) -exec rm {} \; -print
reindent :
~/src/reindent.py -r -v .
test :
export INSTANCE_HOME=${INSTANCE_HOME}; \
export SOFTWARE_HOME=${SOFTWARE_HOME}; \
export PYTHONPATH=${Z3}; \
export ZOPE_CONFIG=${ZOPE_CONFIG}; \
cd ${CURDIR}/tests && ${PYTHON} -O runalltests.py
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<meta:directives namespace="http://namespaces.zope.org/zope">
<meta:directive
name="permission"
schema="zope.app.security.metadirectives.IDefinePermissionDirective"
handler="zope.app.security.metaconfigure.definePermission"
/>
<meta:directive
name="redefinePermission"
schema="zope.app.security.metadirectives.IRedefinePermission"
handler="zope.app.security.metaconfigure.redefinePermission"
/>
<meta:directive
name="interface"
schema="zope.app.component.metadirectives.IInterfaceDirective"
handler="zope.app.component.metaconfigure.interface"
/>
<meta:directive
name="view"
schema="zope.app.component.metadirectives.IViewDirective"
handler="zope.app.component.metaconfigure.view"
/>
<meta:directive
name="adapter"
schema="zope.app.component.metadirectives.IAdapterDirective"
handler="zope.app.component.metaconfigure.adapter"
/>
<meta:directive
name="subscriber"
schema="zope.app.component.metadirectives.ISubscriberDirective"
handler="zope.app.component.metaconfigure.subscriber"
/>
<meta:directive
name="utility"
schema="zope.app.component.metadirectives.IUtilityDirective"
handler="zope.app.component.metaconfigure.utility"
/>
<meta:directive
name="serviceType"
schema="zope.app.component.metadirectives.IServiceTypeDirective"
handler="zope.app.component.metaconfigure.serviceType"
/>
<meta:directive
name="service"
schema="zope.app.component.metadirectives.IServiceDirective"
handler="zope.app.component.metaconfigure.service"
/>
<meta:complexDirective
name="content"
schema="zope.app.component.metadirectives.IClassDirective"
handler=".metaconfigure.ContentDirective"
>
<meta:subdirective
name="implements"
schema="zope.app.component.metadirectives.IImplementsSubdirective"
/>
<meta:subdirective
name="require"
schema="zope.app.component.metadirectives.IRequireSubdirective"
/>
<meta:subdirective
name="allow"
schema="zope.app.component.metadirectives.IAllowSubdirective"
/>
</meta:complexDirective>
<meta:directive
name="vocabulary"
schema="zope.app.schema.metadirectives.IVocabularyDirective"
handler="zope.app.schema.metaconfigure.vocabulary"
/>
</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">
<!-- specific to Five -->
<meta:directive
name="loadProducts"
schema="zope.interface.Interface"
handler=".fiveconfigure.loadProducts"
/>
<meta:directive
name="loadProductsOverrides"
schema="zope.interface.Interface"
handler=".fiveconfigure.loadProductsOverrides"
/>
<meta:directive
name="implements"
schema=".fivedirectives.IImplementsDirective"
handler=".fiveconfigure.implements"
/>
<meta:directive
name="defaultViewable"
schema=".fivedirectives.IDefaultViewableDirective"
handler=".fiveconfigure.defaultViewable"
/>
<meta:directive
name="traversable"
schema=".fivedirectives.ITraversableDirective"
handler=".fiveconfigure.traversable"
/>
<meta:directive
name="sendEvents"
schema=".fivedirectives.ISendEventsDirective"
handler=".eventconfigure.sendEvents"
/>
<meta:directive
name="pagesFromDirectory"
schema=".fivedirectives.IPagesFromDirectoryDirective"
handler=".fiveconfigure.pagesFromDirectory"
/>
<!-- viewable is deprecated, use traversable instead -->
<meta:directive
name="viewable"
schema=".fivedirectives.ITraversableDirective"
handler=".fiveconfigure.viewable"
/>
<meta:directive
name="bridge"
schema=".fivedirectives.IBridgeDirective"
handler=".fiveconfigure.bridge"
/>
</meta:directives>
</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.
#
##############################################################################
# metaclass taken from PEAK.
# Martijn doesn't pretend to understand this.
from weakref import WeakValueDictionary
from types import ClassType
def makeClass(name,bases,dict):
"""makeClass(name, bases, dict) - enhanced class creation
"""
# Create either a "classic" Python class, an ExtensionClass, or a new-style
# class with autogenerated metaclass, based on the nature of the base
# classes involved
name = str(name) # De-unicode
metaclasses = [getattr(b,'__class__',type(b)) for b in bases]
if dict.has_key('__metaclass__'):
metaclasses.insert(0,dict['__metaclass__'])
if dict.has_key('__metaclasses__'):
metaclasses[0:0] = list(dict['__metaclasses__'])
metaclasses = normalizeBases(metaclasses)
if metaclasses:
# If we have metaclasses, it's not a classic class, so derive a
# single metaclass, and ask it to create the class.
if len(metaclasses)==1:
metaclass = metaclasses[0]
else:
metaclass = derivedMeta(metaclasses)
return metaclass(name,bases,dict)
# No metaclasses, it's a classic class, so use 'new.classobj'
from new import classobj; return classobj(name,bases,dict)
def normalizeBases(allBases):
return minimalBases([b for b in allBases if b is not makeClass])
def minimalBases(classes):
"""Reduce a list of base classes to its ordered minimum equivalent"""
classes = [c for c in classes if c is not ClassType]
candidates = []
for m in classes:
for n in classes:
if issubclass(n,m) and m is not n:
break
else:
# m has no subclasses in 'classes'
if m in candidates:
candidates.remove(m) # ensure that we're later in the list
candidates.append(m)
return candidates
metaReg = WeakValueDictionary()
def derivedMeta(metaclasses):
metaclasses = tuple(metaclasses)
derived = metaReg.get(metaclasses)
if derived is None:
normalized = tuple(normalizeBases(metaclasses))
derived = metaReg.get(normalized)
if derived is None:
if len(normalized)==1:
derived = normalized[0]
else:
derived = metaFromBases(normalized)(
'_'.join([n.__name__ for n in normalized]),
metaclasses, {}
)
try: metaReg[normalized] = derived
except TypeError: pass # Some metatypes can't be weakref'd
try: metaReg[metaclasses] = derived
except TypeError: pass
return derived
def metaFromBases(bases):
meta = tuple([getattr(b,'__class__',type(b)) for b in bases])
if meta==bases: raise TypeError("Incompatible root metatypes",bases)
return derivedMeta(meta)
##############################################################################
#
# 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.
#
##############################################################################
"""Generic Components ZCML Handlers
$Id: metaconfigure.py 5287 2004-06-25 11:42:27Z philikon $
"""
from types import ModuleType
from zope.interface import classImplements
from zope.configuration.exceptions import ConfigurationError
from security import CheckerPublic
from security import protectName, initializeClass
class ContentDirective:
def __init__(self, _context, class_):
self.__class = class_
if isinstance(self.__class, ModuleType):
raise ConfigurationError('Content class attribute must be a class')
self.__context = _context
def implements(self, _context, interface):
for interface in interface:
_context.action(
discriminator = (
'five::directive:content', self.__class, object()),
callable = classImplements,
args = (self.__class, interface),
)
interface(_context, interface)
def require(self, _context, permission=None,
attributes=None, interface=None):
"""Require a the permission to access a specific aspect"""
if not (interface or attributes):
raise ConfigurationError("Nothing required")
if interface:
for i in interface:
if i:
self.__protectByInterface(i, permission)
if attributes:
self.__protectNames(attributes, permission)
def allow(self, _context, attributes=None, interface=None):
"""Like require, but with permission_id zope.Public"""
return self.require(_context, CheckerPublic, attributes, interface)
def __protectByInterface(self, interface, permission_id):
"Set a permission on names in an interface."
for n, d in interface.namesAndDescriptions(1):
self.__protectName(n, permission_id)
interface(self.__context, interface)
def __protectName(self, name, permission_id):
"Set a permission on a particular name."
self.__context.action(
discriminator = ('five:protectName', self.__class, name),
callable = protectName,
args = (self.__class, name, permission_id)
)
def __protectNames(self, names, permission_id):
"Set a permission on a bunch of names."
for name in names:
self.__protectName(name, permission_id)
def __call__(self):
"Handle empty/simple declaration."
return self.__context.action(
discriminator = ('five:initialize:class', self.__class),
callable = initializeClass,
args = (self.__class,)
)
##############################################################################
#
# 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.
#
##############################################################################
'''A 'PageTemplateFile' without security restrictions.'''
import os, sys
# Zope 2
from Globals import package_home
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
# Zope 3
from zope.app.pagetemplate.viewpagetemplatefile import ViewMapper
from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
# Five
from ReuseUtils import rebindFunction
from TrustedExpression import getEngine, ModuleImporter
class ZopeTwoPageTemplateFile(PageTemplateFile):
"""A strange hybrid between Zope 2 and Zope 3 page template.
Uses Zope 2's engine, but with security disabled and with some
initialization and API from Zope 3.
"""
def __init__(self, filename, _prefix=None, content_type=None):
# XXX doesn't use content_type yet
self.ZBindings_edit(self._default_bindings)
path = self.get_path_from_prefix(_prefix)
self.filename = os.path.join(path, filename)
if not os.path.isfile(self.filename):
raise ValueError("No such file", self.filename)
basepath, ext = os.path.splitext(self.filename)
self.__name__ = os.path.basename(basepath)
def get_path_from_prefix(self, _prefix):
if isinstance(_prefix, str):
path = _prefix
else:
if _prefix is None:
_prefix = sys._getframe(2).f_globals
path = package_home(_prefix)
return path
_cook = rebindFunction(PageTemplateFile._cook,
getEngine=getEngine)
pt_render = rebindFunction(PageTemplateFile.pt_render,
getEngine=getEngine)
def _pt_getContext(self):
view = self._getContext()
try:
root = self.getPhysicalRoot()
here = view.context
except AttributeError:
# self has no attribute getPhysicalRoot.
# This typically happens when the template has
# no proper acquisition context. That means it has no view,
# since that's the normal context for a template in Five. /regebro
root = self.context.getPhysicalRoot()
here = self.context
view = None
request = getattr(root, 'REQUEST', None)
c = {'template': self,
'here': here,
'context': here,
'container': self._getContainer(),
'nothing': None,
'options': {},
'root': root,
'request': request,
'modules': ModuleImporter,
}
if view:
c['view'] = view
c['views'] = ViewMapper(here, request)
return c
pt_getContext = rebindFunction(_pt_getContext,
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
<configure xmlns="http://namespaces.zope.org/zope"
i18n_domain="Five">
<!-- Give common Zope2 and CMF permissions a permission ID
The title of the permission is what Zope 2 knows it under -->
<permission
id="zope2.Public"
title="Public, everyone can access"
/>
<permission
id="zope2.Private"
title="Private, only accessible from trusted code"
/>
<permission
id="zope2.AccessContentsInformation"
title="Access contents information"
/>
<permission
id="zope2.ChangeImagesFiles"
title="Change Images and Files"
/>
<permission
id="zope2.ChangeConfig"
title="Change configuration"
/>
<permission
id="zope2.ChangePermissions"
title="Change permissions"
/>
<permission
id="zope2.CopyOrMove"
title="Copy or Move"
/>
<permission
id="zope2.DefinePermissions"
title="Define permissions"
/>
<permission
id="zope2.DeleteObjects"
title="Delete objects"
/>
<permission
id="zope2.FTPAccess"
title="FTP access"
/>
<permission
id="zope2.ImportExport"
title="Import/Export objects"
/>
<permission
id="zope2.ManageProperties"
title="Manage properties"
/>
<permission
id="zope2.ManageUsers"
title="Manage users"
/>
<permission
id="zope2.Undo"
title="Undo changes"
/>
<permission
id="zope2.View"
title="View"
/>
<permission
id="zope2.ViewHistory"
title="View History"
/>
<permission
id="zope2.ViewManagementScreens"
title="View management screens"
/>
<permission
id="zope2.WebDAVLock"
title="WebDAV Lock items"
/>
<permission
id="zope2.WebDAVUnlock"
title="WebDAV Unlock items"
/>
<permission
id="zope2.WebDAVAccess"
title="WebDAV access"
/>
<!-- CMF Core Permissions -->
<permission
id="cmf.ListFolderContents"
title="List folder contents"
/>
<permission
id="cmf.ListUndoableChanges"
title="List undoable changes"
/>
<permission
id="cmf.AccessInactivePortalContent"
title="Access inactive portal content"
/>
<permission
id="cmf.ManagePortal"
title="Manage portal"
/>
<permission
id="cmf.ModifyPortalContent"
title="Modify portal content"
/>
<permission
id="cmf.ManageProperties"
title="Manage properties"
/>
<permission
id="cmf.ListPortalMembers"
title="List portal members"
/>
<permission
id="cmf.AddPortalFolders"
title="Add portal folders"
/>
<permission
id="cmf.AddPortalContent"
title="Add portal content"
/>
<permission
id="cmf.AddPortalMember"
title="Add portal member"
/>
<permission
id="cmf.SetOwnPassword"
title="Set own password"
/>
<permission
id="cmf.SetOwnProperties"
title="Set own properties"
/>
<permission
id="cmf.MailForgottonPassword"
title="Mail forgotten password"
/>
<permission
id="cmf.RequestReview"
title="Request review"
/>
<permission
id="cmf.ReviewPortalContent"
title="Review portal content"
/>
<permission
id="cmf.AccessFuturePortalContent"
title="Access future portal content"
/>
</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.
#
##############################################################################
"""Provide basic resource functionality
$Id: browser.py 5259 2004-06-23 15:59:52Z philikon $
"""
import os
import urllib
from Acquisition import Explicit, aq_inner, aq_parent
from ComputedAttribute import ComputedAttribute
from browser import BrowserView
from OFS.Traversable import Traversable as OFSTraversable
from zope.exceptions import NotFoundError
from zope.interface import implements
from zope.component.interfaces import IResource
from zope.component import getViewProviding
from zope.publisher.interfaces.browser import IBrowserPublisher
from zope.app.traversing.browser.interfaces import IAbsoluteURL
from zope.app.datetimeutils import time as timeFromDateTimeString
from zope.app.publisher.fileresource import File, Image
from zope.app.publisher.pagetemplateresource import PageTemplate
from zope.app.publisher.browser.resources import empty
_marker = []
class Resource(Explicit):
"""A publishable resource
"""
implements(IResource)
def __init__(self, request):
self.request = request
def __call__(self):
name = self.__name__
container = self.__parent__
url = str(getViewProviding(container, IAbsoluteURL, self.request))
url = urllib.unquote(url)
if not isinstance(container, DirectoryResource):
name = '++resource++%s' % name
return "%s/%s" % (url, name)
class PageTemplateResource(BrowserView, Resource):
#implements(IBrowserPublisher)
def __browser_default__(self, request):
return self, ()
def __call__(self):
pt = self.context
return pt(self.request)
class FileResource(BrowserView, Resource):
"""A publishable file-based resource"""
#implements(IBrowserPublisher)
def __browser_default__(self, request):
return self, (request.REQUEST_METHOD,)
def GET(self):
"""Default content"""
file = self.context
request = self.request
response = request.response
# HTTP If-Modified-Since header handling. This is duplicated
# from OFS.Image.Image - it really should be consolidated
# somewhere...
header = request.environ.get('If-Modified-Since', None)
if header is not None:
header = header.split(';')[0]
# Some proxies seem to send invalid date strings for this
# header. If the date string is not valid, we ignore it
# rather than raise an error to be generally consistent
# with common servers such as Apache (which can usually
# understand the screwy date string as a lucky side effect
# of the way they parse it).
try: mod_since=long(timeFromDateTimeString(header))
except: mod_since=None
if mod_since is not None:
if getattr(file, 'lmt', None):
last_mod = long(file.lmt)
else:
last_mod = long(0)
if last_mod > 0 and last_mod <= mod_since:
response.setStatus(304)
return ''
response.setHeader('Content-Type', file.content_type)
response.setHeader('Last-Modified', file.lmh)
# Cache for one day
response.setHeader('Cache-Control', 'public,max-age=86400')
f = open(file.path, 'rb')
data = f.read()
f.close()
return data
def HEAD(self):
file = self.context
response = self.request.response
response = self.request.response
response.setHeader('Content-Type', file.content_type)
response.setHeader('Last-Modified', file.lmh)
# Cache for one day
response.setHeader('Cache-Control', 'public,max-age=86400')
return ''
class ResourceFactory:
factory = None
resource = None
def __init__(self, name, path, resource_factory=None):
self.__name = name
self.__rsrc = self.factory(path, name)
if resource_factory is not None:
self.resource = resource_factory
def __call__(self, request):
resource = self.resource(self.__rsrc, request)
return resource
def _PageTemplate(self, path, name):
# PageTemplate doesn't take a name parameter,
# which makes it different from FileResource.
# This is probably an error.
template = PageTemplate(path)
template.__name__ = name
return template
class PageTemplateResourceFactory(ResourceFactory):
"""A factory for Page Template resources"""
factory = _PageTemplate
resource = PageTemplateResource
class FileResourceFactory(ResourceFactory):
"""A factory for File resources"""
factory = File
resource = FileResource
class ImageResourceFactory(ResourceFactory):
"""A factory for Image resources"""
factory = Image
resource = FileResource
# we only need this class a context for DirectoryResource
class Directory:
def __init__(self, path, name):
self.path = path
self.__name__ = name
class DirectoryResource(BrowserView, Resource, OFSTraversable):
#implements(IBrowserPublisher)
resource_factories = {
'gif': ImageResourceFactory,
'png': ImageResourceFactory,
'jpg': ImageResourceFactory,
'pt': PageTemplateResourceFactory,
'zpt': PageTemplateResourceFactory,
'html': PageTemplateResourceFactory,
}
default_factory = FileResourceFactory
def getId(self):
name = self.__name__
if not name.startswith('++resource++'):
name = '++resource++%s' % self.__name__
return name
def __browser_default__(self, request):
'''See interface IBrowserPublisher'''
return empty, ()
def __getitem__(self, name):
res = self.get(name, None)
if res is None:
raise KeyError, name
return res
def get(self, name, default=_marker):
path = self.context.path
filename = os.path.join(path, name)
if not os.path.isfile(filename):
if default is _marker:
raise NotFoundError(name)
return default
ext = name.split('.')[-1]
factory = self.resource_factories.get(ext, self.default_factory)
resource = factory(name, filename)(self.request)
resource.__name__ = name
resource.__parent__ = self
# XXX __of__ wrapping is usually done on traversal.
# However, we don't want to subclass Traversable (or do we?)
# The right thing should probably be a specific (and very simple)
# traverser that does __getitem__ and __of__.
return resource.__of__(self)
class DirectoryResourceFactory(ResourceFactory):
factory = Directory
resource = DirectoryResource
##############################################################################
#
# 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.
#
##############################################################################
"""Five security handling
$Id: security.py 8281 2005-01-14 18:20:18Z regebro $
"""
from zope.interface import implements
from zope.component import queryUtility, getUtility
from zope.app.security.interfaces import IPermission
from zope.app import zapi
from AccessControl import ClassSecurityInfo, getSecurityManager
from Globals import InitializeClass
from types import StringTypes
CheckerPublicId = 'zope.Public'
CheckerPrivateId = 'zope2.Private'
from zope.security.checker import CheckerPublic
def getSecurityInfo(klass):
sec = {}
info = vars(klass)
if info.has_key('__ac_permissions__'):
sec['__ac_permissions__'] = info['__ac_permissions__']
for k, v in info.items():
if k.endswith('__roles__'):
sec[k] = v
return sec
def clearSecurityInfo(klass):
sec = {}
info = vars(klass)
if info.has_key('__ac_permissions__'):
delattr(klass, '__ac_permissions__')
for k, v in info.items():
if k.endswith('__roles__'):
delattr(klass, k)
def checkPermission(permission, object, interaction=None):
"""Return whether security policy allows permission on object.
Arguments:
permission -- A permission name
object -- The object being accessed according to the permission
interaction -- This Zope 3 concept has no equivalent in Zope 2,
and is ignored.
checkPermission is guaranteed to return True if permission is
CheckerPublic or None.
"""
if (permission in ('zope.Public', 'zope2.Public') or
permission is None or permission is CheckerPublic):
return True
if isinstance(permission, StringTypes):
permission = zapi.queryUtility(IPermission, unicode(permission))
if permission is None:
return False
if getSecurityManager().checkPermission(permission.title, object):
return True
return False
def initializeClass(klass):
InitializeClass(klass)
def _getSecurity(klass):
# a Zope 2 class can contain some attribute that is an instance
# of ClassSecurityInfo. Zope 2 scans through things looking for
# an attribute that has the name __security_info__ first
info = vars(klass)
for k, v in info.items():
if hasattr(v, '__security_info__'):
return v
# we stuff the name ourselves as __security__, not security, as this
# could theoretically lead to name clashes, and doesn't matter for
# zope 2 anyway.
security = ClassSecurityInfo()
setattr(klass, '__security__', security)
return security
def protectName(klass, name, permission_id):
"""Protect the attribute 'name' on 'klass' using the given
permission"""
security = _getSecurity(klass)
# XXX: Sometimes, the object CheckerPublic is used instead of the
# string zope.Public. I haven't ben able to figure out why, or if
# it is correct, or a bug. So this is a workaround.
if permission_id is CheckerPublic:
security.declarePublic(name)
return
# Zope 2 uses string, not unicode yet
name = str(name)
if permission_id == CheckerPublicId:
security.declarePublic(name)
elif permission_id == CheckerPrivateId:
security.declarePrivate(name)
else:
permission = getUtility(IPermission, name=permission_id)
# Zope 2 uses string, not unicode yet
perm = str(permission.title)
security.declareProtected(perm, name)
def protectClass(klass, permission_id):
"""Protect the whole class with the given permission"""
security = _getSecurity(klass)
if permission_id == CheckerPublicId:
security.declareObjectPublic()
elif permission_id == CheckerPrivateId:
security.declareObjectPrivate()
else:
permission = getUtility(IPermission, name=permission_id)
# Zope 2 uses string, not unicode yet
perm = str(permission.title)
security.declareObjectProtected(perm)
<configure xmlns="http://namespaces.zope.org/zope">
<serviceType
id="Utilities"
interface="zope.component.interfaces.IUtilityService" />
<service
serviceType="Utilities"
factory="zope.component.utility.GlobalUtilityService" />
<serviceType
id="Adapters"
interface="zope.component.interfaces.IAdapterService" />
<service
serviceType="Adapters"
factory="zope.component.adapter.GlobalAdapterService" />
<serviceType
id="Presentation"
interface="zope.component.interfaces.IPresentationService" />
<service
serviceType="Presentation"
factory="zope.component.presentation.GlobalPresentationService" />
</configure>
This directory contains Zope3-style instance configuration files:
* ``site.zcml`` is the root node of the instance's ZCML
configuration tree.
* ``package-includes`` may contain Zope3-style ZCML slugs to enable
3rd party packages that are not dropped into ``Products``.
Copy these files to your ``$INSTANCE_HOME/etc`` directory for
customization. If Five cannot find them there, it falls back to these
skeleton files.
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:five="http://namespaces.zope.org/five">
<!-- Copy this file to your ``INSTANCE_HOME/etc`` directory -->
<include package="Products.Five" />
<redefinePermission from="zope2.Public" to="zope.Public" />
<include files="package-includes/*-meta.zcml" />
<include files="package-includes/*-configure.zcml" />
<five:loadProducts />
<five:loadProductsOverrides />
</configure>
Five tests
==========
The tests need all products in the ``tests/products`` subdirectory
installed in your Zope instance's Products directory. On unixy
systems, this can be most simply done by a symlink::
cd myinstance/Products
ln -s Five/tests/products/FiveTest .
and so on for each product in tests/products. On other platforms, you
could manually copy these directories (though you'd need to do that
each time you change the tests).
The tests also require ZopeTestCase to be installed. ZopeTestCase can
be downloaded from here:
http://zope.org/Members/shh/ZopeTestCase
it needs to be installed in your Zope software's lib/python/Testing
directory.
Finally, if you have Zope 2.7.3 or better all you have to do is type::
./bin/zopectl test --dir Products/Five
to run the Five tests. For older versions of Zope you need to set the
following environment variables::
export INSTANCE_HOME=/path/to/instance
export SOFTWARE_HOME=/path/to/software/lib/python
Then you should be able to run the tests by typing::
python2.3 runalltests.py
If you have troubles running the tests because zope.conf is looked for
in lib/Testing/etc/zope.conf, then you are running a Zope version
older than Zope 2.7.2. Please upgrade to Zope 2.7.2.
##############################################################################
#
# 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.
#
##############################################################################
##############################################################################
#
# 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.
#
##############################################################################
from zope.interface import Interface, implements
from Products.Five import BrowserView
from AccessControl import ClassSecurityInfo
class IDummy(Interface):
"""Just a marker interface"""
class DummyView(BrowserView):
"""A dummy view"""
def foo(self):
"""A foo"""
return 'A foo view'
class Dummy1:
implements(IDummy)
def foo(self): pass
def bar(self): pass
def baz(self): pass
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')
##############################################################################
#
# 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.
#
##############################################################################
# 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.
#
# 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.
#
# 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)
# zope.conf must be read before 'import Testing'
import zopeconf
zopeconf.process()
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) 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.
#
##############################################################################
#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,)
# )
<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>
##############################################################################
#
# 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.
#
##############################################################################
from Products.Five import BrowserView
from Products.Five import StandardMacros as BaseMacros
class SimpleContentView(BrowserView):
"""More docstring. Please Zope"""
def eagle(self):
"""Docstring"""
return "The eagle has landed"
def mouse(self):
"""Docstring"""
return "The mouse has been eaten by the eagle"
class FancyContentView(BrowserView):
"""Fancy, fancy stuff"""
def view(self):
return "Fancy, fancy"
class CallableNoDocstring:
def __call__(self):
return "No docstring"
def function_no_docstring(self):
return "No docstring"
class NoDocstringView(BrowserView):
def method(self):
return "No docstring"
function = function_no_docstring
object = CallableNoDocstring()
class NewStyleClass(object):
"""
This is a testclass to verify that new style classes are ignored
in browser:page
"""
def __init__(self, context, request):
"""Docstring"""
self.context = context
self.request = request
def method(self):
"""Docstring"""
return
class StandardMacros(BaseMacros):
macro_pages = ('bird_macros', 'dog_macros')
aliases = {'flying':'birdmacro',
'walking':'dogmacro'}
##############################################################################
#
# 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.
#
##############################################################################
from zope.interface import implements
from interfaces import IAdaptable, IAdapted, IOrigin, IDestination
class Adaptable:
implements(IAdaptable)
def method(self):
return "The method"
class Adapter:
implements(IAdapted)
def __init__(self, context):
self.context = context
def adaptedMethod(self):
return "Adapted: %s" % self.context.method()
class Origin:
implements(IOrigin)
class OriginalAdapter:
implements(IDestination)
def __init__(self, context):
self.context = context
def method(self):
return "Original"
class OverrideAdapter:
implements(IDestination)
def __init__(self, context):
self.context = context
def method(self):
return "Overridden"
<p>Have you ever seen a cockatiel?</p>
<p tal:content="string:maybe">dunno</p>
<p tal:content="context/mymethod">Alpha</p>
<p tal:content="view/eagle">Beta</p>
<div tal:replace="structure views/flamingo.html">Gamma</div>
\ No newline at end of file
<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"
/>
<!-- 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="addsimplecontent.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>
<!-- 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"
/>
<!-- 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>
<html metal:define-macro="dogmacro"><head><title>dog macro</title></head><body>Breed: <metal:block define-slot="breed" /></body></html>
<p>The falcon has taken flight</p>
\ No newline at end of file
##############################################################################
#
# 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.
#
##############################################################################
import Acquisition
from AccessControl import ClassSecurityInfo
from OFS.SimpleItem import SimpleItem
from Globals import InitializeClass
from zope.interface import implements
from interfaces import IFancyContent
class FancyAttribute(Acquisition.Explicit):
"""Doc test fanatics"""
def __init__(self, name):
self.name = name
security = ClassSecurityInfo()
security.declarePublic('index_html')
def index_html(self, REQUEST):
"""Doc test fanatics"""
return self.name
InitializeClass(FancyAttribute)
class FancyContent(SimpleItem):
"""A class that already comes with its own __bobo_traverse__ handler.
Quite fancy indeed."""
implements(IFancyContent)
meta_type = "Fancy Content"
security = ClassSecurityInfo()
def __bobo_traverse__(self, REQUEST, name):
return FancyAttribute(name).__of__(self)
InitializeClass(FancyContent)
def manage_addFancyContent(self, id, REQUEST=None):
"""Add the fancy fancy content."""
id = self._setObject(id, FancyContent(id))
return ''
<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
##############################################################################
#
# 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.
#
##############################################################################
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
class NoVerifyPasteFolder(Folder):
"""Folder that does not perform paste verification.
Used by test_events
"""
def _verifyObjectPaste(self, object, validate_src=1):
pass
def manage_addNoVerifyPasteFolder(container, id, title=''):
container._setObject(id, NoVerifyPasteFolder())
folder = container[id]
folder.id = id
folder.title = title
class FiveTraversableFolder(Folder):
"""Folder that is declared Five traversable, see configure.zcml
"""
pass
def manage_addFiveTraversableFolder(container, id, title=''):
container._setObject(id, FiveTraversableFolder())
folder = container[id]
folder.id = id
folder.title = title
##############################################################################
#
# 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.
#
##############################################################################
from zope.interface import Interface
from zope.schema import Text, TextLine
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 ISimpleContent(Interface):
pass
class ICallableSimpleContent(ISimpleContent):
pass
class IIndexSimpleContent(ISimpleContent):
pass
class IFancyContent(Interface):
pass
class IFieldSimpleContent(ISimpleContent):
title = TextLine(
title=u"Title",
description=u"A short description of the event.",
default=u"",
required=True)
description = Text(
title=u"Description",
description=u"A long description of the event.",
default=u"",
required=False)
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:meta="http://namespaces.zope.org/meta">
<meta:directives namespace="http://namespaces.zope.org/five">
<meta:directive
name="parrot"
schema=".metaconfigure.IParrotDirective"
handler=".metaconfigure.parrot"
/>
</meta:directives>
</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.
#
##############################################################################
"""Parrot directive and support classes
$Id: metaconfigure.py 5287 2004-06-25 11:42:27Z philikon $
"""
from zope.interface import Interface
from zope.configuration.fields import GlobalObject
from zope.schema import TextLine
class IParrotDirective(Interface):
"""State that a class implements something.
"""
class_ = GlobalObject(
title=u"Class",
required=True
)
name = TextLine(
title=u"Name",
description=u"The parrots name.",
required=True
)
def parrot(_context, class_, name):
parrot = class_()
parrot.pineForFjords()
class NorwegianBlue(object):
def pineForFjords(self):
return "This parrot is no more!"
<ul>
<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="item"/>
</ul>
\ No newline at end of file
<ul>
<li tal:repeat="item python:['Alpha', 'Beta', 'Gamma']" tal:content="python:repeat['item'].index"/>
</ul>
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<!-- mouse instead of eagle -->
<browser:page
for=".interfaces.ISimpleContent"
class=".browser.SimpleContentView"
attribute="mouse"
name="overridden_view"
permission="zope2.Public"
/>
<!-- OverrideAdapter instead of OriginalAdapter -->
<adapter
for=".interfaces.IOrigin"
provides=".interfaces.IDestination"
factory=".classes.OverrideAdapter"
/>
</configure>
<p tal:content="python:1+1">Some content</p>
\ 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>
<div tal:define="comment string:Testing unrestricted code"
tal:content="python:None.__class__.__name__" />
<div tal:define="comment string:Testing unrestricted modules access;
smtpd nocall:modules/smtpd"
tal:content="python:smtpd.__name__" />
##############################################################################
#
# 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.
#
##############################################################################
from OFS.SimpleItem import SimpleItem
from Globals import InitializeClass
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):
implements(ISimpleContent)
meta_type = 'Five SimpleContent'
security = ClassSecurityInfo()
def __init__(self, id, title):
self.id = id
self.title = title
security.declarePublic('mymethod')
def mymethod(self):
return "Hello world"
security.declarePublic('direct')
def direct(self):
"""Should be able to traverse directly to this as there is no view.
"""
return "Direct traversal worked"
InitializeClass(SimpleContent)
class CallableSimpleContent(SimpleItem):
"""A Viewable piece of content"""
implements(ICallableSimpleContent)
meta_type = "Five CallableSimpleContent"
def __call__(self, *args, **kw):
""" """
return "Default __call__ called"
InitializeClass(CallableSimpleContent)
class IndexSimpleContent(SimpleItem):
"""A Viewable piece of content"""
implements(IIndexSimpleContent)
meta_type = 'Five IndexSimpleContent'
def index_html(self, *args, **kw):
""" """
return "Default index_html called"
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):
"""Add the simple content."""
id = self._setObject(id, SimpleContent(id, title))
add_and_edit(self, id, REQUEST)
return ''
def manage_addCallableSimpleContent(self, id, title, REQUEST=None):
"""Add the viewable simple content."""
id = self._setObject(id, CallableSimpleContent(id, title))
add_and_edit(self, id, REQUEST)
return ''
def manage_addIndexSimpleContent(self, id, title, REQUEST=None):
"""Add the viewable simple content."""
id = 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 ''
##############################################################################
#
# 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.
#
##############################################################################
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()
<p tal:content="context/non_existent_thingie/fubared|context/getId">dunno</p>
<p tal:content="context/test_folder_1_/test_folder_1_/getId">dunno</p>
<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>
##############################################################################
#
# 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.
#
##############################################################################
# Five.tests.products
##############################################################################
#
# 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.
#
##############################################################################
# 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)
##############################################################################
#
# 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.
#
##############################################################################
""" Unit tests for Z2 -> Z3 bridge utilities.
$Id:$
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
import unittest
#------------------------------------------------------------------------------
# Running these tests
# ===================
#
# I (Tres) can't figure out your testing framework. These tests run
# in a "normal" Z27 + Z3 + Five instance home via the following:
#
# $ bin/zopectl run Products/Five/tests/test_bridge.py
#------------------------------------------------------------------------------
from Interface import Interface as Z2_Interface
from Interface import Attribute as Z2_Attribute
from zope.interface import Interface as Z3_Interface
from zope.interface import Attribute as Z3_Attribute
from zope.interface import Attribute as Z3_Method
class BridgeTests(unittest.TestCase):
def test_fromZ2Interface_invalid(self):
from Products.Five.bridge import fromZ2Interface
self.assertRaises(ValueError, fromZ2Interface, None)
self.assertRaises(ValueError, fromZ2Interface, object())
class IZ3_NotAllowed(Z3_Interface):
pass
self.assertRaises(ValueError, fromZ2Interface, IZ3_NotAllowed)
def test_fromZ2Interface_empty(self):
from Products.Five.bridge import fromZ2Interface
class IEmpty(Z2_Interface):
pass
converted = fromZ2Interface(IEmpty)
self.failUnless(Z3_Interface.isEqualOrExtendedBy(converted))
self.assertEqual(len(converted.names()), 0)
def test_fromZ2Interface_attributes(self):
from Products.Five.bridge import fromZ2Interface
class IAttributes(Z2_Interface):
one = Z2_Attribute('one', 'One attribute')
another = Z2_Attribute('another', 'Another attribute')
converted = fromZ2Interface(IAttributes)
self.failUnless(Z3_Interface.isEqualOrExtendedBy(converted))
self.assertEqual(len(converted.names()), 2)
self.failUnless('one' in converted.names())
self.failUnless('another' in converted.names())
one = converted.getDescriptionFor('one')
self.failUnless(isinstance(one, Z3_Attribute))
self.assertEqual(one.getName(), 'one')
self.assertEqual(one.getDoc(), 'One attribute')
another = converted.getDescriptionFor('another')
self.failUnless(isinstance(another, Z3_Attribute))
self.assertEqual(another.getName(), 'another')
self.assertEqual(another.getDoc(), 'Another attribute')
def test_fromZ2Interface_methods(self):
from Products.Five.bridge import fromZ2Interface
class IMethods(Z2_Interface):
def one():
"""One method."""
def another(arg1, arg2):
"""Another method, taking arguments."""
converted = fromZ2Interface(IMethods)
self.failUnless(Z3_Interface.isEqualOrExtendedBy(converted))
self.assertEqual(len(converted.names()), 2)
self.failUnless('one' in converted.names())
self.failUnless('another' in converted.names())
one = converted.getDescriptionFor('one')
self.failUnless(isinstance(one, Z3_Method))
self.assertEqual(one.getName(), 'one')
self.assertEqual(one.getDoc(), 'One method.')
another = converted.getDescriptionFor('another')
self.failUnless(isinstance(another, Z3_Method))
self.assertEqual(another.getName(), 'another')
self.assertEqual(another.getDoc(), 'Another method, taking arguments.')
def test_suite():
return unittest.defaultTestLoader.loadTestsFromTestCase( BridgeTests )
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import *
from AccessControl import Unauthorized
from zope.app.form.browser.submit import Update
from Products.Five.tests.products.FiveTest.simplecontent import manage_addFieldSimpleContent
from Products.Five.tests.products.FiveTest.helpers import manage_addFiveTraversableFolder
class EditFormTest(Functional, FiveTestCase):
def afterSetUp(self):
manage_addFieldSimpleContent(self.folder, 'edittest', 'Test')
uf = self.folder.acl_users
uf._doAddUser('viewer', 'secret', [], [])
uf._doAddUser('manager', 'r00t', ['Manager'], [])
def test_editform(self):
response = self.publish('/test_folder_1_/edittest/edit.html',
basic='manager:r00t')
# we're using a GET request to post variables, but seems to be
# the easiest..
response = self.publish(
'/test_folder_1_/edittest/edit.html?%s=1&field.title=FooTitle&field.description=FooDescription' % Update,
basic='manager:r00t')
self.assertEquals('FooTitle', self.folder.edittest.title)
self.assertEquals('FooDescription', self.folder.edittest.description)
def test_editform_invalid(self):
# missing title, which is required
self.folder.edittest.description = ''
response = self.publish(
'/test_folder_1_/edittest/edit.html?%s=1&field.title=&field.description=BarDescription' % Update,
basic='manager:r00t')
# we expect that we get a 200 Ok
self.assertEqual(200, response.getStatus())
self.assertEquals('Test', self.folder.edittest.title)
self.assertEquals('', self.folder.edittest.description)
def test_addform(self):
manage_addFiveTraversableFolder(self.folder, 'ftf')
self.folder = self.folder.ftf
response = self.publish('/test_folder_1_/ftf/+/addsimplecontent.html',
basic='manager:r00t')
# we're using a GET request to post variables, but seems to be
# the easiest..
response = self.publish(
'/test_folder_1_/ftf/+/addsimplecontent.html?%s=1&add_input_name=alpha&field.title=FooTitle&field.description=FooDescription' % Update,
basic='manager:r00t')
# we expect to get a 302 (redirect)
self.assertEquals(302, response.getStatus())
# we expect the object to be there with the right id
self.assertEquals('alpha', self.folder.alpha.id)
self.assertEquals('FooTitle', self.folder.alpha.title)
self.assertEquals('FooDescription', self.folder.alpha.description)
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(EditFormTest))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# 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 events triggered by Five
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.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
get_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
get_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
get_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():
from unittest import TestSuite, makeSuite
suite = TestSuite()
suite.addTest(makeSuite(EventTest))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
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 = """\
<p>Have you ever seen a cockatiel?</p>
<p>maybe</p>
"""
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 = 'test_folder_1_/%s' % base
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)
abs_url = self.folder.unrestrictedTraverse(base % '')()
expected = 'http://nohost/test_folder_1_/testoid/++resource++fivetest_resources'
self.assertEquals(abs_url, expected)
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())
# 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_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'])
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))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import *
from zope.component import getView
from zope.testing.cleanup import CleanUp
from Products.Five import zcml
from Products.Five.traversable import FakeRequest
from Products.Five.security import clearSecurityInfo, checkPermission
from Products.Five.tests.dummy import Dummy1, Dummy2
from Globals import InitializeClass
class PageSecurityTest(CleanUp, FiveTestCase):
def setUp(self):
super(PageSecurityTest, self).setUp()
zcml.reset()
zcml.load_site()
self.dummy1 = Dummy1
def tearDown(self):
super(PageSecurityTest, self).tearDown()
zcml.reset()
clearSecurityInfo(self.dummy1)
def test_page_security(self):
self.failIf(hasattr(self.dummy1, '__ac_permissions__'))
decl = """
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<browser:page
for="Products.Five.tests.dummy.IDummy"
class="Products.Five.tests.dummy.DummyView"
attribute="foo"
name="foo.txt"
permission="zope2.ViewManagementScreens"
/>
</configure>
"""
zcml.string(decl)
request = FakeRequest()
view = getView(Dummy1(), 'foo.txt', request)
ac = getattr(view, '__ac_permissions__')
ex_ac = (('View management screens', ('foo',)),)
self.assertEquals(ac, ex_ac)
foo_roles = getattr(view, 'foo__roles__', None)
self.failIf(foo_roles is None)
self.failIf(foo_roles == ())
self.assertEquals(foo_roles.__of__(view), ('Manager',))
class SecurityEquivalenceTest(CleanUp, FiveTestCase):
def setUp(self):
super(SecurityEquivalenceTest, self).setUp()
zcml.reset()
zcml.initialize()
self.dummy1 = Dummy1
self.dummy2 = Dummy2
def tearDown(self):
zcml.reset()
super(SecurityEquivalenceTest, self).tearDown()
clearSecurityInfo(self.dummy1)
clearSecurityInfo(self.dummy2)
def test_equivalence(self):
self.failIf(hasattr(self.dummy1, '__ac_permissions__'))
self.failIf(hasattr(self.dummy2, '__ac_permissions__'))
decl = """
<configure xmlns="http://namespaces.zope.org/zope">
<content
class="Products.Five.tests.dummy.Dummy1">
<allow attributes="foo" />
<!-- XXX not yet supported
<deny attributes="baz" />
-->
<require attributes="bar keg"
permission="zope2.ViewManagementScreens"
/>
</content>
</configure>
"""
zcml.string(decl)
InitializeClass(self.dummy2)
ac1 = getattr(self.dummy1, '__ac_permissions__')
ac2 = getattr(self.dummy2, '__ac_permissions__')
self.assertEquals(ac1, ac2)
bar_roles1 = getattr(self.dummy1, 'bar__roles__').__of__(self.dummy1)
self.assertEquals(bar_roles1.__of__(self.dummy1), ('Manager',))
keg_roles1 = getattr(self.dummy1, 'keg__roles__').__of__(self.dummy1)
self.assertEquals(keg_roles1.__of__(self.dummy1), ('Manager',))
foo_roles1 = getattr(self.dummy1, 'foo__roles__')
self.assertEquals(foo_roles1, None)
# XXX Not yet supported.
# baz_roles1 = getattr(self.dummy1, 'baz__roles__')
# self.assertEquals(baz_roles1, ())
bar_roles2 = getattr(self.dummy2, 'bar__roles__').__of__(self.dummy2)
self.assertEquals(bar_roles2.__of__(self.dummy2), ('Manager',))
keg_roles2 = getattr(self.dummy2, 'keg__roles__').__of__(self.dummy2)
self.assertEquals(keg_roles2.__of__(self.dummy2), ('Manager',))
foo_roles2 = getattr(self.dummy2, 'foo__roles__')
self.assertEquals(foo_roles2, None)
baz_roles2 = getattr(self.dummy2, 'baz__roles__')
self.assertEquals(baz_roles2, ())
class CheckPermissionTest(FiveTestCase):
def test_publicPermissionId(self):
#import pdb;pdb.set_trace()
self.failUnless(checkPermission('zope2.Public', self.folder))
def test_privatePermissionId(self):
self.failIf(checkPermission('zope.Private', self.folder))
self.failIf(checkPermission('zope2.Private', self.folder))
def test_accessPermissionId(self):
self.failUnless(checkPermission('zope2.AccessContentsInformation', self.folder))
def test_invalidPermissionId(self):
self.failIf(checkPermission('notapermission', self.folder))
def test_suite():
from unittest import TestSuite, makeSuite
suite = TestSuite()
#suite.addTest(makeSuite(SecurityEquivalenceTest))
#suite.addTest(makeSuite(PageSecurityTest))
suite.addTest(makeSuite(CheckPermissionTest))
return suite
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from Products.Five.tests.fivetest import *
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']
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
self.assertEqual(response.getStatus(), 401)
def test_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)
self.assertEqual(response.getStatus(), 200)
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) 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.
#
##############################################################################
import os
from os.path import join, abspath, dirname, split, exists
def process():
"""Read in zope.conf configuration file.
This is a hack but there doesn't seem to be a better way.
"""
_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')
if exists(_config):
from Zope2 import configure
configure(_config)
##############################################################################
#
# 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.
#
##############################################################################
"""Machinery for making things traversable through adaptation
$Id: traversable.py 9786 2005-03-15 12:53:39Z efge $
"""
from zExceptions import NotFound
from zope.exceptions import NotFoundError
from zope.component import getView, ComponentLookupError
from zope.interface import implements
from zope.publisher.interfaces.browser import IBrowserRequest
from zope.app.traversing.interfaces import ITraverser, ITraversable
from zope.app.traversing.adapters import DefaultTraversable
from zope.app.traversing.adapters import traversePathElement
from zope.security.management import thread_local
from AccessControl import getSecurityManager
_marker = object
class FakeRequest:
implements(IBrowserRequest)
def getPresentationSkin(self):
return None
def has_key(self, key):
return False
def newInteraction():
"""Con Zope 3 to use Zope 2's checkPermission.
Zope 3 when it does a checkPermission will turn around and
ask the thread local interaction for the checkPermission method.
By making the interaction *be* Zope 2's security manager, we can
con Zope 3 into using Zope 2's checker...
"""
if getattr(thread_local, 'interaction', None) is None:
thread_local.interaction = getSecurityManager()
class Traversable:
"""A mixin to make an object traversable using an ITraverser adapter.
"""
__five_traversable__ = True
def __fallback_traverse__(self, REQUEST, name):
"""Method hook for fallback traversal
This method is called by __bobo_traverse___ when Zope3-style
ITraverser traversal fails.
Just raise a AttributeError to indicate traversal has failed
and let Zope do it's job.
"""
raise AttributeError, name
def __bobo_traverse__(self, REQUEST, name):
"""Hook for Zope 2 traversal
This method is called by Zope 2's ZPublisher upon traversal.
It allows us to trick it into faking the Zope 3 traversal system
by using an ITraverser adapter.
"""
if not IBrowserRequest.providedBy(REQUEST):
# Try to get the REQUEST by acquisition
REQUEST = getattr(self, 'REQUEST', None)
if not IBrowserRequest.providedBy(REQUEST):
REQUEST = FakeRequest()
# con Zope 3 into using Zope 2's checkPermission
newInteraction()
try:
return ITraverser(self).traverse(
path=[name], request=REQUEST).__of__(self)
except (ComponentLookupError, NotFoundError,
AttributeError, KeyError, NotFound):
pass
try:
return getattr(self, name)
except AttributeError:
pass
try:
return self[name]
except (AttributeError, KeyError):
pass
return self.__fallback_traverse__(REQUEST, name)
__bobo_traverse__.__five_method__ = True
class FiveTraversable(DefaultTraversable):
def traverse(self, name, furtherPath):
context = self._subject
__traceback_info__ = (context, name, furtherPath)
# Find the REQUEST
REQUEST = getattr(context, 'REQUEST', None)
if not IBrowserRequest.providedBy(REQUEST):
REQUEST = FakeRequest()
# Try to lookup a view first
try:
return getView(context, name, REQUEST)
except ComponentLookupError:
pass
# If a view can't be found, then use default traversable
return super(FiveTraversable, self).traverse(name, furtherPath)
##############################################################################
#
# 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.
#
##############################################################################
"""Machinery for making things viewable
$Id: traversable.py 5763 2004-07-28 20:15:11Z dreamcatcher $
"""
import inspect
from zExceptions import NotFound
from zope.exceptions import NotFoundError
from zope.component import getView, getDefaultViewName, ComponentLookupError
from zope.interface import implements
from zope.publisher.interfaces.browser import IBrowserRequest
from traversable import FakeRequest
from interfaces import IBrowserDefault
_marker = object
class Viewable:
"""A mixin to make an object viewable.
"""
__five_viewable__ = True
def __fallback_default__(self, request):
"""Try to dispatch to existing index_html or __call__"""
if getattr(self, 'index_html', None):
return self, ('index_html',)
if getattr(self, 'fallback_call__', None):
return self, ('fallback_call__',)
# XXX Should never get this far. But if it does?
# def fallback_call__(self, *args, **kw):
# """By default, return self"""
# return self
# we have a default view, tell zpublisher to go there
def __browser_default__(self, request):
obj = self
path = None
try:
obj, path = IBrowserDefault(self).defaultView(request)
except ComponentLookupError:
pass
if path:
if len(path) == 1 and path[0] == '__call__':
return obj, ('fallback_call__',)
return obj, path
return self.__fallback_default__(request)
__browser_default__.__five_method__ = True
# this is technically not needed because ZPublisher finds our
# attribute through __browser_default__; but we also want to be
# able to call pages from python modules, PythonScripts or ZPT
# def __call__(self, *args, **kw):
# """ """
# request = kw.get('REQUEST')
# if not IBrowserRequest.providedBy(request):
# request = getattr(self, 'REQUEST', None)
# if not IBrowserRequest.providedBy(request):
# request = FakeRequest()
# obj, path = self.__browser_default__(request)
# if path and not simpleRecursion():
# meth = obj.unrestrictedTraverse(path)
# if meth is not None:
# return meth(*args, **kw)
# return self.fallback_call__(*args, **kw)
# __call__.__five_method__ = True
# def simpleRecursion():
# # This tests for simple recursion, which can easily happen
# # in CMF, like the following:
# # - Object has a method named 'view'
# # - 'view' method calls '__call__'
# # - five:viewable overrides call to use '__browser_default__'
# # to find a default view and call it
# # - defaultView is set to 'view'
# # Bang. Infinite recursion.
# stack = inspect.stack()
# try:
# if len(stack) < 4:
# return False
# if stack[2][1:4] == stack[4][1:4]:
# return True
# finally:
# del stack
# return False
class BrowserDefault(object):
implements(IBrowserDefault)
def __init__(self, context):
self.context = context
def defaultView(self, request):
context = self.context
name = None
try:
name = getDefaultViewName(context, request)
except ComponentLookupError:
pass
return context, [name,]
##############################################################################
#
# 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.
#
##############################################################################
"""ZCML machinery
$Id: zcml.py 9855 2005-03-17 16:41:09Z shh $
"""
import os
from zope.configuration import xmlconfig
_initialized = False
_context = None
def load_site():
"""Load the appropriate ZCML 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
if _initialized:
return
_initialized = True
# load instance site configuration file
site_zcml = os.path.join(INSTANCE_HOME, "etc", "site.zcml")
if os.path.exists(site_zcml):
file = site_zcml
else:
file = os.path.join(os.path.dirname(__file__), "skel", "site.zcml")
global _context
_context = xmlconfig.file(file)
def load_config(file, package=None):
"""Load an additional ZCML file into the context.
Use with extreme care.
"""
global _context
_context = xmlconfig.file(file, package, _context)
##############################################################################
#
# Abstract base test case for working with CMF-style portals
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# This base class maintains a fixture consisting of:
#
# - a portal object (self.portal)
# - a user folder inside the portal
# - a default user with role 'Member' inside the user folder
# - the default user's memberarea (self.folder)
# - the default user is logged in
#
# The twist is that the portal object itself is *not* created
# by the PortalTestCase class! Subclasses must make sure
# getPortal() returns a usable portal object to the setup code.
# 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.
#
##############################################################################
"""Abstract base test case for working with CMF-style portals
This class maintains a fixture consisting of:
- a portal object (self.portal)
- a user folder inside the portal
- a default user with role 'Member' inside the user folder
- the default user's memberarea (self.folder)
- the default user is logged in
The twist is that the portal object itself is *not* created
by the PortalTestCase class! Subclasses must make sure
getPortal() returns a usable portal object to the setup code.
# $Id: PortalTestCase.py,v 1.38 2005/02/09 12:42:40 shh42 Exp $
$Id: PortalTestCase.py,v 1.38 2005/02/09 12:42:40 shh42 Exp $
"""
import base
import interfaces
......@@ -135,18 +146,3 @@ class PortalTestCase(base.TestCase):
'''Logs out.'''
noSecurityManager()
# b/w compatibility methods
def _setRoles(self, roles, name=user_name):
self.setRoles(roles, name)
def _setPermissions(self, permissions, role='Member'):
self.setPermissions(permissions, role)
def _login(self, name=user_name):
self.login(name)
def _logout(self):
self.logout()
# b/w compatibility names
_portal_name = portal_name
##############################################################################
#
# Lightweight Zope startup
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# Fast Zope startup is achieved by not installing (m)any
# products. If your tests require a product you must
# install it yourself using installProduct().
#
# Typically used as in
#
# import ZopeLite as Zope2
# Zope2.installProduct('SomeProduct')
# app = Zope2.app()
# 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.
#
##############################################################################
"""Lightweight Zope startup
Fast Zope startup is achieved by not installing (m)any
products. If your tests require a product you must
install it yourself using installProduct().
Typically used as in
# $Id: ZopeLite.py,v 1.24 2004/08/18 09:28:54 shh42 Exp $
import ZopeLite as Zope2
Zope2.installProduct('SomeProduct')
app = Zope2.app()
$Id: ZopeLite.py,v 1.24 2004/08/18 09:28:54 shh42 Exp $
"""
import os, sys, time
......@@ -45,19 +56,30 @@ def _exec(cmd):
_write('Loading Zope, please stand by ')
_start = time.time()
# Configure logging
if not sys.modules.has_key('logging'):
import logging
logging.basicConfig()
# Debug mode is dog slow ...
import App.config
config = App.config.getConfiguration()
config.debug_mode = 0
App.config.setConfiguration(config)
def _configure_logging():
# Initialize the logging module
if not sys.modules.has_key('logging'):
import logging
logging.basicConfig()
def _configure_debug_mode():
# Switch off debug mode
import App.config
config = App.config.getConfiguration()
config.debug_mode = 0
App.config.setConfiguration(config)
def _configure_client_cache():
# Make sure we use a temporary client cache
import App.config
config = App.config.getConfiguration()
config.zeo_client_name = None
App.config.setConfiguration(config)
_configure_logging()
_configure_debug_mode()
_configure_client_cache()
# Need to import Zope2 early on as the
# ZTUtils package relies on it
_exec('import Zope2')
import Zope2
_exec('import ZODB')
......@@ -75,26 +97,31 @@ _write('.')
_exec('import OFS.Application')
import OFS.Application
import App.ProductContext
_write('.')
# Avoid expensive product import
def _null_import_products(): pass
OFS.Application.import_products = _null_import_products
# Avoid expensive product installation
def _null_initialize(app): pass
OFS.Application.initialize = _null_initialize
# Avoid expensive help registration
def _null_register_topic(self,id,topic): pass
App.ProductContext.ProductContext.registerHelpTopic = _null_register_topic
def _null_register_title(self,title): pass
App.ProductContext.ProductContext.registerHelpTitle = _null_register_title
def _null_register_help(self,directory='',clear=1,title_re=None): pass
App.ProductContext.ProductContext.registerHelp = _null_register_help
# Make sure to use a temporary client cache
if os.environ.get('ZEO_CLIENT'): del os.environ['ZEO_CLIENT']
def _apply_patches():
# Avoid expensive product import
def null_import_products(): pass
OFS.Application.import_products = null_import_products
# Avoid expensive product installation
def null_initialize(app): pass
OFS.Application.initialize = null_initialize
# Avoid expensive help registration
def null_register_topic(self,id,topic): pass
App.ProductContext.ProductContext.registerHelpTopic = null_register_topic
def null_register_title(self,title): pass
App.ProductContext.ProductContext.registerHelpTitle = null_register_title
def null_register_help(self,directory='',clear=1,title_re=None): pass
App.ProductContext.ProductContext.registerHelp = null_register_help
# Do not patch a running Zope
if not Zope2._began_startup:
_apply_patches()
# Allow test authors to install Zope products into the test environment. Note
# that installProduct() must be called at module level -- never from tests.
from OFS.Application import get_folder_permissions, get_products, install_product
from OFS.Folder import Folder
import Products
......@@ -103,13 +130,12 @@ _theApp = Zope2.app()
_installedProducts = {}
def hasProduct(name):
'''Tests if a product can be found along Products.__path__'''
'''Checks if a product can be found along Products.__path__'''
return name in [n[1] for n in get_products()]
def installProduct(name, quiet=0):
'''Installs a Zope product.'''
start = time.time()
app = _theApp
meta_types = []
if not _installedProducts.has_key(name):
for priority, product_name, index, product_dir in get_products():
......@@ -117,7 +143,7 @@ def installProduct(name, quiet=0):
if not quiet: _print('Installing %s ... ' % product_name)
# We want to fail immediately if a product throws an exception
# during install, so we set the raise_exc flag.
install_product(app, product_dir, product_name, meta_types,
install_product(_theApp, product_dir, product_name, meta_types,
get_folder_permissions(), raise_exc=1)
_installedProducts[product_name] = 1
Products.meta_types = Products.meta_types + tuple(meta_types)
......@@ -128,23 +154,27 @@ def installProduct(name, quiet=0):
if name != 'SomeProduct': # Ignore the skeleton tests :-P
if not quiet: _print('Installing %s ... NOT FOUND\n' % name)
# Loading the Control_Panel of an existing ZODB may take
# a while; print another dot if it does.
_s = time.time(); _max = (_s - _start) / 4
_exec('_theApp.Control_Panel')
_cp = _theApp.Control_Panel
if hasattr(_cp, 'initialize_cache'):
_cp.initialize_cache()
if (time.time() - _s) > _max:
_write('.')
installProduct('PluginIndexes', 1) # Must install first
installProduct('OFSP', 1)
#installProduct('ExternalMethod', 1)
#installProduct('ZSQLMethods', 1)
#installProduct('ZGadflyDA', 1)
#installProduct('MIMETools', 1)
#installProduct('MailHost', 1)
def _load_control_panel():
# Loading the Control_Panel of an existing ZODB may take
# a while; print another dot if it does.
start = time.time()
max = (start - _start) / 4
_exec('_theApp.Control_Panel')
_theApp.Control_Panel
if (time.time() - start) > max:
_write('.')
def _install_products():
installProduct('PluginIndexes', 1) # Must install first
installProduct('OFSP', 1)
#installProduct('ExternalMethod', 1)
#installProduct('ZSQLMethods', 1)
#installProduct('ZGadflyDA', 1)
#installProduct('MIMETools', 1)
#installProduct('MailHost', 1)
_load_control_panel()
_install_products()
# So people can use ZopeLite.app()
app = Zope2.app
......@@ -153,9 +183,11 @@ DB = Zope2.DB
configure = Zope2.configure
def startup(): pass
# ZODB sandbox factory
from ZODB.DemoStorage import DemoStorage
def sandbox(base=None):
'''Returns what amounts to a sandbox copy of the base ZODB.'''
'''Returns a sandbox copy of the base ZODB.'''
if base is None: base = Zope2.DB
base_storage = base._storage
quota = getattr(base_storage, '_quota', None)
......
##############################################################################
#
# Default test case & fixture for Zope testing
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# The fixture consists of:
#
# - a folder (self.folder)
# - a user folder inside that folder
# - a default user inside the user folder
#
# The default user is logged in and has the 'Access contents information'
# and 'View' permissions given to his role.
# 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.
#
##############################################################################
"""Default test case & fixture for Zope testing
The fixture consists of:
- a folder (self.folder)
- a user folder inside that folder
- a default user inside the user folder
# $Id: ZopeTestCase.py,v 1.29 2005/02/09 12:42:40 shh42 Exp $
The default user is logged in and has the 'Access contents information'
and 'View' permissions given to his role.
$Id: ZopeTestCase.py,v 1.29 2005/02/09 12:42:40 shh42 Exp $
"""
import base
import functional
......@@ -100,17 +111,6 @@ class ZopeTestCase(base.TestCase):
'''Logs out.'''
noSecurityManager()
# b/w compatibility methods
def _setRoles(self, roles, name=user_name):
self.setRoles(roles, name)
def _setPermissions(self, permissions, role=user_role):
self.setPermissions(permissions, role)
def _login(self, name=user_name):
self.login(name)
def _logout(self):
self.logout()
class FunctionalTestCase(functional.Functional, ZopeTestCase):
'''Base class for functional Zope tests'''
......@@ -119,11 +119,6 @@ class FunctionalTestCase(functional.Functional, ZopeTestCase):
ZopeTestCase.__implements__)
# b/w compatibility names
_folder_name = folder_name
_user_name = user_name
_user_role = user_role
_standard_permissions = standard_permissions
from base import app
from base import close
from base import closeConnections
......
##############################################################################
#
# Names exported by the ZopeTestCase package
# 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.
#
##############################################################################
"""Names exported by the ZopeTestCase package
# $Id: __init__.py,v 1.25 2005/02/22 14:59:16 shh42 Exp $
$Id: __init__.py,v 1.25 2005/02/22 14:59:16 shh42 Exp $
"""
import ZopeLite as Zope2
import utils
......@@ -30,18 +41,11 @@ from profiler import Profiled
from sandbox import Sandboxed
from functional import Functional
from warnhook import WarningsHook
from ZODB.tests.warnhook import WarningsHook
from unittest import main
# TODO
#from doctest import ZopeDocFileSuite
#from doctest import FunctionalDocFileSuite
# b/w compatibility names
_folder_name = folder_name
_user_name = user_name
_user_role = user_role
_standard_permissions = standard_permissions
_portal_name = portal_name
from base import closeConnections
from zopedoctest import ZopeDocTestSuite
from zopedoctest import ZopeDocFileSuite
from zopedoctest import FunctionalDocTestSuite
from zopedoctest import FunctionalDocFileSuite
##############################################################################
#
# Test case for Zope testing
# 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.
#
##############################################################################
"""TestCase for Zope testing
# $Id: base.py,v 1.1 2004/08/19 13:59:41 shh42 Exp $
$Id: base.py,v 1.1 2004/08/19 13:59:41 shh42 Exp $
"""
import ZopeLite as Zope2
......
......@@ -55,8 +55,6 @@ Module Testing.ZopeTestCase
Functional
WarningsHook
Modules
ZopeLite as Zope
......@@ -173,8 +171,8 @@ Class ZopeTestCase
Class FunctionalTestCase
Base class for functional unit tests
(derived from ZopeTestCase)
Convenience class for functional unit testing
(derived from Functional and ZopeTestCase)
__implements__ = (Functional.__implements__,
ZopeTestCase.__implements__)
......@@ -300,34 +298,6 @@ Class Functional
Module warnhook
Support for capturing Python warning messages
Classes
WarningsHook
Class WarningsHook
Facility to capture warnings generated by Python
Attributes
warnings
Methods
install()
uninstall()
clear()
Module utils
Utility functions to extend the test environment
......
0.9.7 (Zope 2.8 edition)
- Renamed 'doctest' package to 'zopedoctest' because of name-shadowing
issues discovered during integration into Zope 2.8.
- Greatly improved the doctest story. ZopeTestCase now implements four test
suite factories: ZopeDocTestSuite, ZopeDocFileSuite, FunctionalDocTestSuite,
and FunctionalDocFileSuite.
- Removed warnhook.py, we now use the one from ZODB.tests.
- Removed doctest.py, we now use the one from zope.testing.
- Removed dochttp.py + test, we now use the one from zope.app.tests.
- ZopeLite now takes care not to monkey patch an already started Zope.
0.9.6
- Dropped support for Zope 2.5 as it lacks the setSecurityManager() API.
- Moved interfaces from doc section to interfaces.py module.
......
......@@ -91,11 +91,11 @@ What You Get
Hooks for controlling transactions:
- **'beforeSetUp'** is called before the ZODB connection is opened, at the start of setUp.
The default behaviour of this hook is to call 'get_transaction().begin()'.
The default behaviour of this hook is to call 'transaction.begin()'.
You will rarely want to override this.
- **'beforeClose'** is called before the ZODB connection is closed, at the end of
tearDown. By default this method calls 'get_transaction().abort()' to discard
tearDown. By default this method calls 'transaction.abort()' to discard
any changes made by the test. In some situations you may need to override
this hook and commit the transaction instead. Make sure you really know what
you are doing though.
......
##############################################################################
#
# ZopeTestCase
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# 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()
# 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 framework
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()
$Id:$
"""
__version__ = '0.2.3'
__version__ = '0.2.4'
# Save start state
#
......
##############################################################################
#
# Support for functional unit testing in ZTC
# After Marius Gedminas' functional.py module for Zope3.
# 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.
#
##############################################################################
"""Support for functional unit testing in ZTC
After Marius Gedminas' functional.py module for Zope 3.
# $Id: functional.py,v 1.3 2004/09/12 16:49:59 shh42 Exp $
$Id: functional.py,v 1.3 2004/09/12 16:49:59 shh42 Exp $
"""
import sys, re, base64
import transaction
......
##############################################################################
#
# ZopeTestCase interfaces
# 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.
#
##############################################################################
"""ZopeTestCase interfaces
# $Id: interfaces.py,v 1.5 2005/02/07 21:59:35 shh42 Exp $
$Id: interfaces.py,v 1.5 2005/02/07 21:59:35 shh42 Exp $
"""
try:
from Interface import Interface
......
##############################################################################
#
# Profiling support for ZTC
# 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.
#
##############################################################################
"""Profiling support for ZTC
# $Id: profiler.py,v 1.3 2005/01/01 14:02:44 shh42 Exp $
$Id: profiler.py,v 1.3 2005/01/01 14:02:44 shh42 Exp $
"""
import os, sys
import interfaces
......
##############################################################################
#
# Runs all tests in the current directory [and below]
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# Execute like:
# python runalltests.py [-R]
#
# Alternatively use the testrunner:
# python /path/to/Zope/bin/testrunner.py -qa
# 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.
#
##############################################################################
"""Runs all tests in the current directory [and below]
Execute like:
python runalltests.py [-R]
$Id:$
"""
__version__ = '0.2.1'
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Support for ZODB sandboxes in ZTC
# 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.
#
##############################################################################
"""Support for ZODB sandboxes in ZTC
# $Id: sandbox.py,v 1.2 2004/08/19 15:31:26 shh42 Exp $
$Id: sandbox.py,v 1.2 2004/08/19 15:31:26 shh42 Exp $
"""
import ZopeLite as Zope2
import transaction
......
##############################################################################
#
# Tests the base.TestCase class
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# NOTE: This is *not* an example TestCase. Do not
# use this file as a blueprint for your own tests!
#
# See testPythonScript.py and testShoppingCart.py for
# example test cases. See testSkeleton.py for a quick
# way of getting started.
# 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.
#
##############################################################################
"""Tests the base.TestCase class
NOTE: This is *not* an example TestCase. Do not
use this file as a blueprint for your own tests!
See testPythonScript.py and testShoppingCart.py for
example test cases. See testSkeleton.py for a quick
way of getting started.
# $Id: testBaseTestCase.py,v 1.7 2005/02/09 12:42:40 shh42 Exp $
$Id: testBaseTestCase.py,v 1.7 2005/02/09 12:42:40 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Example functional ZopeTestCase
# 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.
#
##############################################################################
"""Example functional ZopeTestCase
# $Id: testFunctional.py,v 1.16 2005/02/12 13:13:04 shh42 Exp $
$Id: testFunctional.py,v 1.16 2005/02/12 13:13:04 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Interface tests
# 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.
#
##############################################################################
"""Interface tests
# $Id: testInterfaces.py,v 1.3 2005/01/01 20:38:16 shh42 Exp $
$Id: testInterfaces.py,v 1.3 2005/01/01 20:38:16 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Tests the PortalTestCase
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# NOTE: This is *not* an example TestCase. Do not
# use this file as a blueprint for your own tests!
#
# See testPythonScript.py and testShoppingCart.py for
# example test cases. See testSkeleton.py for a quick
# way of getting started.
# 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.
#
##############################################################################
"""Tests the PortalTestCase
NOTE: This is *not* an example TestCase. Do not
use this file as a blueprint for your own tests!
See testPythonScript.py and testShoppingCart.py for
example test cases. See testSkeleton.py for a quick
way of getting started.
# $Id: testPortalTestCase.py,v 1.30 2005/01/30 14:22:48 shh42 Exp $
$Id: testPortalTestCase.py,v 1.30 2005/01/30 14:22:48 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Example ZopeTestCase testing a PythonScript object in the default fixture.
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# Note that you are encouraged to call any of the following methods
# from your own tests to modify the test user's security credentials:
#
# - setRoles()
# - setPermissions()
# - login()
# - logout()
# 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.
#
##############################################################################
"""Example ZopeTestCase testing a PythonScript object in the default fixture
Note that you are encouraged to call any of the following methods
from your own tests to modify the test user's security credentials:
- setRoles()
- setPermissions()
- login()
- logout()
# $Id: testPythonScript.py,v 1.9 2004/04/09 12:38:37 shh42 Exp $
$Id: testPythonScript.py,v 1.9 2004/04/09 12:38:37 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Example ZopeTestCase testing the ShoppingCart example application.
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# Note the use of sessions and how the SESSION object is added to
# the REQUEST in afterSetUp().
#
# You can use zLOG.LOG() if you set up the event log variables first.
# Handy for debugging and tracing your tests.
# 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.
#
##############################################################################
"""Example ZopeTestCase testing the ShoppingCart example application.
Note the use of sessions and how the SESSION object is added to
the REQUEST in afterSetUp().
You can use zLOG.LOG() if you set up the event log variables first.
Handy for debugging and tracing your tests.
# $Id: testShoppingCart.py,v 1.11 2005/02/23 17:14:56 shh42 Exp $
$Id: testShoppingCart.py,v 1.11 2005/02/23 17:14:56 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Skeleton ZopeTestCase
# 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.
#
##############################################################################
"""Skeleton ZopeTestCase
$Id:$
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Example ZopeTestCase testing web access to a freshly started ZServer.
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# Note that we need to set up the error_log before starting the ZServer.
#
# Note further that the test thread needs to explicitly commit its
# transactions, so the ZServer threads can see modifications made to
# the ZODB.
#
# IF YOU THINK YOU NEED THE WEBSERVER STARTED YOU ARE PROBABLY WRONG!
# This is only required in very special cases, like when testing
# ZopeXMLMethods where XSLT processing is done by external tools that
# need to URL-call back into the Zope server.
#
# If you want to write functional unit tests, see the testFunctional.py
# example instead.
# 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.
#
##############################################################################
"""Example ZopeTestCase testing web access to a freshly started ZServer
Note that we need to set up the error_log before starting the ZServer.
Note further that the test thread needs to explicitly commit its
transactions, so the ZServer threads can see modifications made to
the ZODB.
IF YOU THINK YOU NEED THE WEBSERVER STARTED YOU ARE PROBABLY WRONG!
This is only required in very special cases, like when testing
ZopeXMLMethods where XSLT processing is done by external tools that
need to URL-call back into the Zope server.
If you want to write functional unit tests, see the testFunctional.py
example instead.
# $Id: testWebserver.py,v 1.16 2005/02/12 13:11:10 shh42 Exp $
$Id: testWebserver.py,v 1.16 2005/02/12 13:11:10 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Tests ZODB behavior in ZopeTestCase
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# Demonstrates that cut/copy/paste/clone/rename and import/export
# work in ZopeTestCase if a subtransaction is commited before performing
# the respective operations.
# 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.
#
##############################################################################
"""Tests ZODB behavior in ZopeTestCase
# $Id: testZODBCompat.py,v 1.17 2004/04/09 12:38:37 shh42 Exp $
Demonstrates that cut/copy/paste/clone/rename and import/export
work in ZopeTestCase if a subtransaction is commited before performing
the respective operations.
$Id: testZODBCompat.py,v 1.17 2004/04/09 12:38:37 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Tests the ZopeTestCase, eating its own dogfood
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# NOTE: This is *not* an example TestCase. Do not
# use this file as a blueprint for your own tests!
#
# See testPythonScript.py and testShoppingCart.py for
# example test cases. See testSkeleton.py for a quick
# way of getting started.
# 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.
#
##############################################################################
"""Tests the ZopeTestCase, eating its own dogfood
NOTE: This is *not* an example TestCase. Do not
use this file as a blueprint for your own tests!
See testPythonScript.py and testShoppingCart.py for
example test cases. See testSkeleton.py for a quick
way of getting started.
# $Id: testZopeTestCase.py,v 1.25 2005/01/30 14:22:48 shh42 Exp $
$Id: testZopeTestCase.py,v 1.25 2005/01/30 14:22:48 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
......
##############################################################################
#
# Parts of ZServer support are in this module so they can
# be imported more selectively.
# 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.
#
##############################################################################
"""Parts of ZServer support are in this module so they can
be imported more selectively.
# $Id: threadutils.py,v 1.6 2004/08/19 15:31:26 shh42 Exp $
$Id: threadutils.py,v 1.6 2004/08/19 15:31:26 shh42 Exp $
"""
from threading import Thread
from StringIO import StringIO
......
##############################################################################
#
# Utility functions
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# These functions are designed to be imported and run at
# module level to add functionality to the test environment.
# 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.
#
##############################################################################
"""Utility functions
# $Id: utils.py,v 1.21 2005/02/11 09:00:21 shh42 Exp $
These functions are designed to be imported and run at
module level to add functionality to the test environment.
$Id: utils.py,v 1.21 2005/02/11 09:00:21 shh42 Exp $
"""
import os
import sys
import time
import random
import transaction
def setupCoreSessions(app=None):
'''Sets up the session_data_manager e.a.'''
from Acquisition import aq_base
......@@ -19,7 +35,7 @@ def setupCoreSessions(app=None):
if not hasattr(app, 'temp_folder'):
from Products.TemporaryFolder.TemporaryFolder import MountedTemporaryFolder
tf = MountedTemporaryFolder('temp_folder','Temporary Folder')
tf = MountedTemporaryFolder('temp_folder', 'Temporary Folder')
app._setObject('temp_folder', tf)
commit = 1
......@@ -79,8 +95,6 @@ def setupSiteErrorLog(app=None):
transaction.commit()
import os, time
def importObjectFromFile(container, filename, quiet=0):
'''Imports an object from a (.zexp) file into the given container.'''
from ZopeLite import _print
......@@ -98,7 +112,6 @@ def startZServer(number_of_threads=1, log=None):
'''Starts an HTTP ZServer thread.'''
global _Z2HOST, _Z2PORT
if _Z2HOST is None:
import random
_Z2HOST = '127.0.0.1'
_Z2PORT = random.choice(range(55000, 55500))
from ZServer import setNumberOfThreads
......@@ -111,8 +124,6 @@ def startZServer(number_of_threads=1, log=None):
return _Z2HOST, _Z2PORT
import sys
def makerequest(app, stdout=sys.stdout):
'''Wraps the app into a fresh REQUEST.'''
from ZPublisher.BaseRequest import RequestContainer
......
##############################################################################
#
# Copyright (c) 2004 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.
#
##############################################################################
import warnings
class WarningsHook:
"""Hook to capture warnings generated by Python.
The function warnings.showwarning() is designed to be hooked by
application code, allowing the application to customize the way it
handles warnings.
This hook captures the unformatted warning information and stored
it in a list. A test can inspect this list after the test is over.
Issues:
The warnings module has lots of delicate internal state. If
a warning has been reported once, it won't be reported again. It
may be necessary to extend this class with a mechanism for
modifying the internal state so that we can be guaranteed a
warning will be reported.
If Python is run with a warnings filter, e.g. python -Werror,
then a test that is trying to inspect a particular warning will
fail. Perhaps this class can be extended to install more-specific
filters the test to work anyway.
"""
def __init__(self):
self.original = None
self.warnings = []
def install(self):
self.original = warnings.showwarning
warnings.showwarning = self.showwarning
def uninstall(self):
assert self.original is not None
warnings.showwarning = self.original
self.original = None
def showwarning(self, message, category, filename, lineno):
self.warnings.append((str(message), category, filename, lineno))
def clear(self):
self.warnings = []
====================
Functional doc tests
====================
Doctests are a way to write tests while documenting the thing that is
tested at the same time. As an example, this file both documents
functional doc tests *and* tests them.
Doctests look like regular interactive interpreter sessions. That
makes them very easy to create. Doctests can either occur in an
object's or method's docstring or in a separate file. Use either
``DocTestSuite`` or ``DocFileSuite`` in these cases.
Creating functional doctests
----------------------------
Creating functional doctests is just as easy. Obviously, you cannot
simply use an interpreter shell for the initial test creation.
Instead, you can use the `tcpwatch` program to record browser sessions
and turn them into tests:
1. Start out with a clean ZODB database.
- Create a folder named `test_folder_1_` in the root folder.
- Create a user in the root user folder called `test_user_1_` with
the password `secret`.
- Create a role `test_role_1_` and grant the role to the test
user. Grant the permissions 'Access contents information' and
'View' to the role.
2. Install tcpwatch. You can get a recent version from Zope CVS:
http://cvs.zope.org/Packages/tcpwatch/
3. Create a temporary directory to record tcpwatch output.
4. Run tcpwatch using:
tcpwatch.py -L 8081:8080 -s -r tmpdir
(the ports are the listening port and forwarded-to port; the
second port must match the Zope configuration)
5. In a browser, connect to the listening port and do whatever needs
to be recorded.
6. Shut down tcpwatch.
7. Run the script at Zope/lib/python/Testing/ZopeTestCase/doctest/dochttp.py
python2.3 dochttp.py tmpdir > .../mytest.txt
8. Edit the generated text file to add explanations and elide
uninteresting portions of the output.
9. In a functional test module (usually ftests.py), import
``FunctionalDocFileSuite`` and instantiate it, passing the name of the
text file containing the test. For example:
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from unittest import TestSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
def test_suite():
return TestSuite((
FunctionalDocFileSuite('FunctionalDocTest.txt'),
))
if __name__ == '__main__':
framework()
Examples
--------
Test Publish Document
>>> print http(r"""
... GET /test_folder_1_/index_html HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain
<BLANKLINE>
index
Test Publish Script
>>> print http(r"""
... GET /test_folder_1_/script HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 1
Content-Type: text/plain
<BLANKLINE>
1
Test Publish Script with Argument
>>> print http(r"""
... GET /test_folder_1_/script?a:int=2 HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 1
Content-Type: text/plain
<BLANKLINE>
3
Test Server Error
>>> print http(r"""
... GET /test_folder_1_/script?a=2 HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 500 Internal Server Error
...Content-Type: text/html...exceptions.TypeError...
Test Unauthorized
>>> self.folder.index_html.manage_permission('View', ['Owner'])
>>> print http(r"""
... GET /test_folder_1_/index_html HTTP/1.1
... """, handle_errors=False)
HTTP/1.1 401 Unauthorized
...
Test Basic Authentication
>>> from AccessControl.Permissions import manage_properties
>>> self.setPermissions([manage_properties])
>>> print http(r"""
... GET /test_folder_1_/index_html/change_title?title=Foo HTTP/1.1
... Authorization: Basic %s
... """ % user_auth, handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 0
...
>>> self.folder.index_html.title_or_id()
'Foo'
Test passing in non-base64-encoded login/pass
>>> from Testing.ZopeTestCase import user_name, user_password
>>> print http(r"""
... GET /test_folder_1_/index_html/change_title?title=Baz HTTP/1.1
... Authorization: Basic %s:%s
... """ % (user_name, user_password), handle_errors=False)
HTTP/1.1 200 OK
Content-Length: 0
...
>>> self.folder.index_html.title_or_id()
'Baz'
Doc test support for ZopeTestCase
=================================
Backport of functional doc tests from Zope 3 by
Sidnei da Silva. See 'FunctionalDocTest.txt' for
documentation.
You can learn more about doc tests here:
http://docs.python.org/lib/module-doctest.html
Dummy Test
==========
This dummy test is used to verify that passing in a ``test_class``
that doesn't subclass ``ZopeTestCase.Functional`` still works but
issues a warning.
>>> from Testing import ZopeTestCase as zopetest
>>> hook = zopetest.WarningsHook()
>>> hook.install()
>>> suite = zopetest.FunctionalDocFileSuite('WarningsTest.txt',
... package=zopetest.zopedoctest,
... test_class=zopetest.ZopeTestCase)
>>> len(hook.warnings)
1
>>> message, category, filename, lineno = hook.warnings[0]
>>> message
"The test_class you are using doesn't subclass from ZopeTestCase.Functional. Please fix that."
>>> category.__name__
'UserWarning'
We have to uninstall the hook so that other warnings don't get lost.
>>> hook.uninstall()
==============
Zope doc tests
==============
Doctests are a way to write tests while documenting the thing that is
tested at the same time. As an example, this file both documents
Zope doc tests *and* tests them.
Doctests look like regular interactive interpreter sessions. That
makes them very easy to create. Doctests can either occur in an
object's or method's docstring or in a separate file. Use either
``DocTestSuite`` or ``DocFileSuite`` in these cases.
Creating Zope doc tests
-----------------------
Creating doc tests for Zope is easy. While we cannot simply use an
interpeter shell to write our tests, we can reuse the ZopeTestCase
infrastructure to set up the necessary environment.
1. Create a text file, just like this one, containing prose
documentation interspersed with doctest ``Examples``.
2. In a test module import ``ZopeDocFileSuite`` and instantiate it,
passing the name of the text file containing the tests.
For example:
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from unittest import TestSuite
from Testing.ZopeTestCase import ZopeDocFileSuite
def test_suite():
return TestSuite((
ZopeDocFileSuite('ZopeDocTest.txt'),
))
if __name__ == '__main__':
framework()
Examples
--------
Here we are going to demonstrate that everything we know about
ZopeTestCase is still true for doc tests. In particular, the default
fixture is set up for doc tests just like for unit tests.
>>> from Testing.ZopeTestCase import folder_name, user_name
>>> from AccessControl import getSecurityManager
There should be a folder:
>>> folder_name in self.app.objectIds()
True
Containing a user folder:
>>> 'acl_users' in self.folder.objectIds()
True
Containing the default user:
>>> user = self.folder.acl_users.getUserById(user_name)
>>> user is None
False
The default user should be logged in:
>>> getSecurityManager().getUser().getId() == user_name
True
The custom setUp method should have been run as well. See
testZopeDocTest.py for its definition.
>>> 'object' in self.folder.objectIds()
True
Now let's manipulate our test objects a bit:
>>> ob = self.folder.object
>>> print ob.title_or_id()
object
>>> ob.manage_changeProperties(title='Foo')
>>> print ob.title_or_id()
Foo
>>> self.folder.manage_delObjects('object')
>>> 'object' in self.folder.objectIds()
False
##############################################################################
#
# 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.
#
##############################################################################
"""ZopeTestCase doctest support
$Id: __init__.py,v 1.2 2005/03/26 18:07:08 shh42 Exp $
"""
__version__ = '0.9.7'
from zope.testing.doctest import *
from functional import *
##############################################################################
#
# 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.
#
##############################################################################
"""ZopeTestCase framework
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()
$Id:$
"""
__version__ = '0.2.4'
# 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) 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.
#
##############################################################################
"""Support for (functional) doc tests
$Id: functional.py,v 1.2 2005/03/26 18:07:08 shh42 Exp $
"""
import sys, re, base64
import warnings
import transaction
from zope.testing import doctest
from Testing.ZopeTestCase import ZopeTestCase
from Testing.ZopeTestCase import FunctionalTestCase
from Testing.ZopeTestCase import Functional
from Testing.ZopeTestCase import folder_name
from Testing.ZopeTestCase import user_name
from Testing.ZopeTestCase import user_password
from Testing.ZopeTestCase import user_role
from Testing.ZopeTestCase import standard_permissions
from Testing.ZopeTestCase.sandbox import AppZapper
from Testing.ZopeTestCase.functional import ResponseWrapper
class HTTPHeaderOutput:
# zope.interface.implements(zope.server.interfaces.IHeaderOutput)
def __init__(self, protocol, omit):
self.headers = {}
self.headersl = []
self.protocol = protocol
self.omit = omit
def setResponseStatus(self, status, reason):
self.status, self.reason = status, reason
def setResponseHeaders(self, mapping):
self.headers.update(dict(
[('-'.join([s.capitalize() for s in name.split('-')]), v)
for name, v in mapping.items()
if name.lower() not in self.omit]
))
def appendResponseHeaders(self, lst):
headers = [split_header(header) for header in lst]
self.headersl.extend(
[('-'.join([s.capitalize() for s in name.split('-')]), v)
for name, v in headers
if name.lower() not in self.omit]
)
def __str__(self):
out = ["%s: %s" % header for header in self.headers.items()]
out.extend(["%s: %s" % header for header in self.headersl])
out.sort()
out.insert(0, "%s %s %s" % (self.protocol, self.status, self.reason))
return '\n'.join(out)
class DocResponseWrapper(ResponseWrapper):
"""Response Wrapper for use in doctests
"""
def __init__(self, response, outstream, path, header_output):
ResponseWrapper.__init__(self, response, outstream, path)
self.header_output = header_output
def __str__(self):
body = self.getBody()
if body:
return "%s\n\n%s" % (self.header_output, body)
return "%s\n" % (self.header_output)
headerre = re.compile('(\S+): (.+)$')
def split_header(header):
return headerre.match(header).group(1, 2)
basicre = re.compile('Basic (.+)?:(.+)?$')
def auth_header(header):
match = basicre.match(header)
if match:
import base64
u, p = match.group(1, 2)
if u is None:
u = ''
if p is None:
p = ''
auth = base64.encodestring('%s:%s' % (u, p))
return 'Basic %s' % auth[:-1]
return header
def getRootFolder():
return AppZapper().app()
def sync():
getRootFolder()._p_jar.sync()
def http(request_string, handle_errors=True):
"""Execute an HTTP request string via the publisher
This is used for HTTP doc tests.
"""
import urllib
import rfc822
from cStringIO import StringIO
from ZPublisher.Response import Response
from ZPublisher.Test import publish_module
from AccessControl.SecurityManagement import getSecurityManager
from AccessControl.SecurityManagement import setSecurityManager
# Save current Security Manager
old_sm = getSecurityManager()
# Commit work done by previous python code.
transaction.commit()
# Discard leading white space to make call layout simpler
request_string = request_string.lstrip()
# split off and parse the command line
l = request_string.find('\n')
command_line = request_string[:l].rstrip()
request_string = request_string[l+1:]
method, path, protocol = command_line.split()
path = urllib.unquote(path)
instream = StringIO(request_string)
env = {"HTTP_HOST": 'localhost',
"HTTP_REFERER": 'localhost',
"REQUEST_METHOD": method,
"SERVER_PROTOCOL": protocol,
}
p = path.split('?')
if len(p) == 1:
env['PATH_INFO'] = p[0]
elif len(p) == 2:
[env['PATH_INFO'], env['QUERY_STRING']] = p
else:
raise TypeError, ''
header_output = HTTPHeaderOutput(
protocol, ('x-content-type-warning', 'x-powered-by',
'bobo-exception-type', 'bobo-exception-file',
'bobo-exception-value', 'bobo-exception-line'))
headers = [split_header(header)
for header in rfc822.Message(instream).headers]
for name, value in headers:
name = ('_'.join(name.upper().split('-')))
if name not in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
name = 'HTTP_' + name
env[name] = value.rstrip()
if env.has_key('HTTP_AUTHORIZATION'):
env['HTTP_AUTHORIZATION'] = auth_header(env['HTTP_AUTHORIZATION'])
outstream = StringIO()
response = Response(stdout=outstream, stderr=sys.stderr)
publish_module('Zope2', stdin=instream,
response=response,
environ=env)
header_output.setResponseStatus(response.getStatus(), response.errmsg)
header_output.setResponseHeaders(response.headers)
# Restore previous security manager, which may have been changed
# by calling the publish method above
setSecurityManager(old_sm)
# Sync connection
sync()
return DocResponseWrapper(response, outstream, path, header_output)
class ZopeSuiteFactory:
def __init__(self, *args, **kw):
self._args = args
self._kw = kw
self.setup_globs()
self.setup_test_class()
self.setup_optionflags()
def doctestsuite(self):
return doctest.DocTestSuite(*self._args, **self._kw)
def docfilesuite(self):
return doctest.DocFileSuite(*self._args, **self._kw)
def setup_globs(self):
globs = self._kw.setdefault('globs', {})
globs['folder_name'] = folder_name
globs['user_name'] = user_name
globs['user_password'] = user_password
globs['user_role'] = user_role
globs['standard_permissions'] = standard_permissions
def setup_test_class(self):
test_class = self._kw.get('test_class', ZopeTestCase)
if 'test_class' in self._kw:
del self._kw['test_class']
# If the test_class does not have a runTest method, we add
# a dummy attribute so that TestCase construction works.
if not hasattr(test_class, 'runTest'):
setattr(test_class, 'runTest', None)
# Create a TestCase instance which will be used to execute
# the setUp and tearDown methods, as well as be passed into
# the test globals as 'self'.
test_instance = test_class()
kwsetUp = self._kw.get('setUp')
def setUp(test):
test_instance.setUp()
test.globs['test'] = test
test.globs['self'] = test_instance
if hasattr(test_instance, 'app'):
test.globs['app'] = test_instance.app
if hasattr(test_instance, 'folder'):
test.globs['folder'] = test_instance.folder
if hasattr(test_instance, 'portal'):
test.globs['portal'] = test_instance.portal
test.globs['portal_name'] = test_instance.portal.getId()
if kwsetUp is not None:
kwsetUp(test_instance)
self._kw['setUp'] = setUp
kwtearDown = self._kw.get('tearDown')
def tearDown(test):
if kwtearDown is not None:
kwtearDown(test_instance)
test_instance.tearDown()
self._kw['tearDown'] = tearDown
def setup_optionflags(self):
if 'optionflags' not in self._kw:
self._kw['optionflags'] = (doctest.ELLIPSIS
| doctest.NORMALIZE_WHITESPACE)
class FunctionalSuiteFactory(ZopeSuiteFactory):
def setup_globs(self):
ZopeSuiteFactory.setup_globs(self)
globs = self._kw.setdefault('globs', {})
globs['http'] = http
globs['getRootFolder'] = getRootFolder
globs['sync'] = sync
globs['user_auth'] = base64.encodestring('%s:%s' % (user_name, user_password))
def setup_test_class(self):
test_class = self._kw.get('test_class', FunctionalTestCase)
# If the passed-in test_class doesn't subclass Functional,
# we mix it in for you, but we will issue a warning.
if not issubclass(test_class, Functional):
name = test_class.__name__
warnings.warn(("The test_class you are using doesn't "
"subclass from ZopeTestCase.Functional. "
"Please fix that."), UserWarning, 2)
if not 'Functional' in name:
name = 'Functional%s' % name
test_class = type(name, (Functional, test_class), {})
self._kw['test_class'] = test_class
ZopeSuiteFactory.setup_test_class(self)
def setup_optionflags(self):
if 'optionflags' not in self._kw:
self._kw['optionflags'] = (doctest.ELLIPSIS
| doctest.REPORT_NDIFF
| doctest.NORMALIZE_WHITESPACE)
def ZopeDocTestSuite(module=None, **kw):
module = doctest._normalize_module(module)
return ZopeSuiteFactory(module, **kw).doctestsuite()
def ZopeDocFileSuite(*paths, **kw):
if kw.get('module_relative', True):
kw['package'] = doctest._normalize_module(kw.get('package'))
return ZopeSuiteFactory(*paths, **kw).docfilesuite()
def FunctionalDocTestSuite(module=None, **kw):
module = doctest._normalize_module(module)
return FunctionalSuiteFactory(module, **kw).doctestsuite()
def FunctionalDocFileSuite(*paths, **kw):
if kw.get('module_relative', True):
kw['package'] = doctest._normalize_module(kw.get('package'))
return FunctionalSuiteFactory(*paths, **kw).docfilesuite()
__all__ = [
'ZopeDocTestSuite',
'ZopeDocFileSuite',
'FunctionalDocTestSuite',
'FunctionalDocFileSuite',
]
GET /@@contents.html HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
GET /@@contents.html HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/pl.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:04 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@singleBranchTree.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/mi.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /++etc++site/@@manage HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /@@/site_management.css HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/css,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/++etc++site/@@tasks.html
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope3.css HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/css,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/onlinehelp.js HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/xmltree.js HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/favicon.png HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
If-Modified-Since: Thu, 19 Aug 2004 10:10:06 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope3logo.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:04 GMT
Authorization: Basic bWdyOm1ncnB3
GET /@@singleBranchTree.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope-app-folder-interfaces-IFolder-zmi_icon.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/@@contents.html
If-Modified-Since: Thu, 19 Aug 2004 10:10:08 GMT
Authorization: Basic bWdyOm1ncnB3
GET / HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@children.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
GET /@@/zope-app-site-interfaces-ISiteManager-zmi_icon.gif HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /++etc++site/@@tasks.html HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Referer: http://localhost:8081/
Authorization: Basic bWdyOm1ncnB3
GET /++etc++site/@@singleBranchTree.xml HTTP/1.1
Host: localhost:8081
User-Agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.4.1) Gecko/20031114
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Authorization: Basic bWdyOm1ncnB3
##############################################################################
#
# 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.
#
##############################################################################
"""Runs all tests in the current directory [and below]
Execute like:
python runalltests.py [-R]
$Id:$
"""
__version__ = '0.2.1'
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
import unittest, imp
TestRunner = unittest.TextTestRunner
suite = unittest.TestSuite()
def visitor(recursive, dir, names):
tests = [n[:-3] for n in names if n.startswith('test') and n.endswith('.py')]
for test in tests:
saved_syspath = sys.path[:]
sys.path.insert(0, dir)
try:
fp, path, desc = imp.find_module(test, [dir])
m = imp.load_module(test, fp, path, desc)
if hasattr(m, 'test_suite'):
suite.addTest(m.test_suite())
finally:
fp.close()
sys.path[:] = saved_syspath
if not recursive:
names[:] = []
if __name__ == '__main__':
os.path.walk(os.curdir, visitor, '-R' in sys.argv)
TestRunner().run(suite)
##############################################################################
#
# 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 for auth_header
$Id: testAuthHeaderTest.py,v 1.2 2005/03/26 18:07:08 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from unittest import TestSuite, makeSuite
from Testing.ZopeTestCase import TestCase
from Testing.ZopeTestCase import zopedoctest
auth_header = zopedoctest.functional.auth_header
class AuthHeaderTestCase(TestCase):
def test_auth_encoded(self):
header = 'Basic Z2xvYmFsbWdyOmdsb2JhbG1ncnB3'
self.assertEquals(auth_header(header), header)
def test_auth_non_encoded(self):
header = 'Basic globalmgr:globalmgrpw'
expected = 'Basic Z2xvYmFsbWdyOmdsb2JhbG1ncnB3'
self.assertEquals(auth_header(header), expected)
def test_auth_non_encoded_empty(self):
header = 'Basic globalmgr:'
expected = 'Basic Z2xvYmFsbWdyOg=='
self.assertEquals(auth_header(header), expected)
header = 'Basic :pass'
expected = 'Basic OnBhc3M='
self.assertEquals(auth_header(header), expected)
def test_auth_non_encoded_colon(self):
header = 'Basic globalmgr:pass:pass'
expected = 'Basic Z2xvYmFsbWdyOnBhc3M6cGFzcw=='
self.assertEquals(auth_header(header), expected)
def test_suite():
return TestSuite((
makeSuite(AuthHeaderTestCase),
))
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
"""Example functional doctest
$Id: testFunctionalDocTest.py,v 1.2 2005/03/26 18:07:08 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from unittest import TestSuite
from Testing.ZopeTestCase import installProduct
from Testing.ZopeTestCase import FunctionalDocTestSuite
from Testing.ZopeTestCase import FunctionalDocFileSuite
installProduct('PythonScripts')
def setUp(self):
'''This method will run after the test_class' setUp.
>>> print http(r"""
... GET /test_folder_1_/index_html HTTP/1.1
... """)
HTTP/1.1 200 OK
Content-Length: 5
Content-Type: text/plain
<BLANKLINE>
index
'''
self.folder.addDTMLDocument('index_html', file='index')
self.folder.manage_addProduct['PythonScripts'].manage_addPythonScript('script')
self.folder.script.ZPythonScript_edit(params='a=0', body='return a+1')
change_title = '''<dtml-call "manage_changeProperties(title=REQUEST.get('title'))">'''
self.folder.addDTMLMethod('change_title', file=change_title)
def test_suite():
return TestSuite((
FunctionalDocTestSuite(setUp=setUp),
FunctionalDocFileSuite('FunctionalDocTest.txt', setUp=setUp),
))
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
"""Example doctest
$Id: testWarningsTest.py,v 1.2 2005/03/26 18:07:08 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from unittest import TestSuite
from Testing.ZopeTestCase import ZopeDocFileSuite
def test_suite():
return TestSuite((
ZopeDocFileSuite('WarningsTest.txt'),
))
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
"""Example Zope doctest
$Id: testZopeDocTest.py,v 1.2 2005/03/26 18:07:08 shh42 Exp $
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from unittest import TestSuite
from Testing.ZopeTestCase import ZopeDocTestSuite
from Testing.ZopeTestCase import ZopeDocFileSuite
def setUp(self):
'''This method will run after the test_class' setUp.
>>> 'object' in folder.objectIds()
True
'''
self.folder.manage_addFolder('object', '')
def test_suite():
return TestSuite((
ZopeDocTestSuite(setUp=setUp),
ZopeDocFileSuite('ZopeDocTest.txt', setUp=setUp),
))
if __name__ == '__main__':
framework()
##############################################################################
#
# 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.
#
##############################################################################
"""ZopeTestCase public interface
$Id: __init__.py,v 1.1 2005/02/25 11:01:07 shh42 Exp $
"""
__version__ = '0.9.7'
import Testing.ZopeTestCase
__path__.extend(Testing.ZopeTestCase.__path__)
from Testing.ZopeTestCase import *
from Testing.ZopeTestCase import _print
from Testing.ZopeTestCase.utils import *
#
# ZopeTestCase public interface
#
# $Id: __init__.py,v 1.1 2005/02/25 11:01:07 shh42 Exp $
import Testing.ZopeTestCase
__path__.extend(Testing.ZopeTestCase.__path__)
from Testing.ZopeTestCase import *
from Testing.ZopeTestCase.utils import *
##############################################################################
#
# ztc_common.py
# Copyright (c) 2005 Zope Corporation and Contributors. All Rights Reserved.
#
# This file must be called from framework.py like so
#
# execfile(os.path.join(os.path.dirname(Testing.__file__),
# 'ZopeTestCase', 'ztc_common.py'))
# 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.
#
##############################################################################
"""ztc_common.py
This file must be called from framework.py like so
# $Id: ztc_common.py,v 1.14 2004/05/27 15:06:24 shh42 Exp $
execfile(os.path.join(os.path.dirname(Testing.__file__),
'ZopeTestCase', 'ztc_common.py'))
$Id: ztc_common.py,v 1.14 2004/05/27 15:06:24 shh42 Exp $
"""
# Overwrites the default framework() method to expose the
# TestRunner parameters and add profiler support.
......
......@@ -1336,6 +1336,16 @@ class HTTPRequest(BaseRequest):
def taintWrapper(self, enabled=TAINTING_ENABLED):
return enabled and TaintRequestWrapper(self) or self
# Used by Five
def getPresentationSkin(self):
"""see zope.component.interfaces.IPresentationRequest"""
return getattr(self, '_presentation_skin', None)
def setPresentationSkin(self, skin):
"""see zope.publisher.interfaces.IPublicationRequest"""
self._presentation_skin = skin
class TaintRequestWrapper:
def __init__(self, req):
......
......@@ -166,7 +166,7 @@ class ZopeDistribution(distutils.core.Distribution):
distutils.core.Distribution.__init__(self, attrs)
self.cmdclass["install"] = ZopeInstall
self.cmdclass["install_data"] = ZopeInstallData
# presumes this script lives in the base dir
BASE_DIR=os.path.dirname(os.path.abspath(sys.argv[0]))
......@@ -502,9 +502,12 @@ setup(
name='Testing',
author=AUTHOR,
packages=['Testing', 'Testing.ZopeTestCase'],
packages=['Testing', 'Testing.ZopeTestCase',
'Testing.ZopeTestCase.zopetest', 'Testing.ZopeTestCase.zopedoctest'],
data_files=[['Testing', ['Testing/README.txt']],
['Testing/var', ['Testing/var/README.txt']]],
['Testing/var', ['Testing/var/README.txt']],
['Testing/ZopeTestCase/doc', ['Testing/ZopeTestCase/doc/*']],
['Testing/ZopeTestCase/zopedoctest', ['Testing/ZopeTestCase/zopedoctest/*.txt']]],
)
# ThreadedAsync
......@@ -1082,6 +1085,221 @@ setup(
)
setup(
name='Five',
author='Martijn Faassen',
packages=['Products.Five', 'Products.Five.tests'],
data_files=[['Products/Five', ['Products/Five/*']],
['Products/Five/doc', ['Products/Five/doc/*']],
['Products/Five/skel', ['Products/Five/skel/*']],
['Products/Five/tests', ['Products/Five/tests/*']],
],
)
# Zope 3 / Five integration layer support. Note that in addition to the
# Five package itself, we also pull in several modules from the Zope 3
# heirarchy: zope, persistent, transaction. Most of this is ripped from
# the Zope 3 setup.py.
from distutils import dir_util
from distutils.command.build import build as buildcmd
from distutils.command.build_ext import build_ext
from distutils.command.install_lib import install_lib as installcmd
from distutils.core import setup
from distutils.dist import Distribution
from distutils.extension import Extension
if sys.version_info < (2, 3):
_setup = setup
def setup(**kwargs):
if kwargs.has_key("classifiers"):
del kwargs["classifiers"]
_setup(**kwargs)
# A hack to determine if Extension objects support the `depends' keyword arg,
# which only exists in Python 2.3's distutils.
if not "depends" in Extension.__init__.func_code.co_varnames:
# If it doesn't, create a local replacement that removes depends from the
# kwargs before calling the regular constructor.
_Extension = Extension
class Extension(_Extension):
def __init__(self, name, sources, **kwargs):
if "depends" in kwargs:
del kwargs["depends"]
_Extension.__init__(self, name, sources, **kwargs)
# We have to snoop for file types that distutils doesn't copy correctly when
# doing a non-build-in-place.
EXTS = ['.conf', '.css', '.dtd', '.gif', '.jpg', '.html',
'.js', '.mo', '.png', '.pt', '.stx', '.ref',
'.txt', '.xml', '.zcml', '.mar', '.in', '.sample',
]
IGNORE_NAMES = (
'CVS', '.svn', # Revision Control Directories
)
# This class serves multiple purposes. It walks the file system looking for
# auxiliary files that distutils doesn't install properly, and it actually
# copies those files (when hooked into by distutils). It also walks the file
# system looking for candidate packages for distutils to install as normal.
# The key here is that the package must have an __init__.py file.
class Finder:
def __init__(self, exts, prefix):
self._files = []
self._pkgs = {}
self._exts = exts
# We're finding packages in lib/python in the source dir, but we're
# copying them directly under build/lib.<plat>. So we need to lop off
# the prefix when calculating the package names from the file names.
prefix, rest = os.path.split(prefix)
self._plen = len(prefix) + 1
def visit(self, ignore, dir, files):
# Remove ignored filenames
for ignore in IGNORE_NAMES:
if ignore in files:
files.remove(ignore)
for file in files:
# First see if this is one of the packages we want to add, or if
# we're really skipping this package.
if '__init__.py' in files:
aspkg = dir[self._plen:].replace(os.sep, '.')
self._pkgs[aspkg] = True
# Add any extra files we're interested in
base, ext = os.path.splitext(file)
if ext in self._exts:
self._files.append(os.path.join(dir, file))
def copy_files(self, cmd, outputbase):
for file in self._files:
dest = os.path.join(outputbase, file[self._plen:])
# Make sure the destination directory exists
dir = os.path.dirname(dest)
if not os.path.exists(dir):
dir_util.mkpath(dir)
cmd.copy_file(file, dest)
def get_packages(self):
return self._pkgs.keys()
def remove_stale_bytecode(arg, dirname, names):
names = map(os.path.normcase, names)
for name in names:
if name.endswith(".pyc") or name.endswith(".pyo"):
srcname = name[:-1]
if srcname not in names:
fullname = os.path.join(dirname, name)
print "Removing stale bytecode file", fullname
os.unlink(fullname)
# This bit uses the finder to crawl through the zope3 packages we need
# and Do The Right Thing to arrange for setup for sub-packages.
z3_packages = ['zope']
packages = []
for name in z3_packages:
basedir = os.path.join(PACKAGES_ROOT, name)
finder = Finder(EXTS, basedir)
os.path.walk(basedir, finder.visit, None)
packages.extend(finder.get_packages())
# Distutils hook classes
class MyBuilder(buildcmd):
def run(self):
os.path.walk(os.curdir, remove_stale_bytecode, None)
buildcmd.run(self)
finder.copy_files(self, self.build_lib)
class MyExtBuilder(build_ext):
# Override the default build_ext to remove stale bytecodes.
# Technically, removing bytecode has nothing to do with
# building extensions, but Zope's the build_ext -i variant
# is used to build Zope in place.
def run(self):
os.path.walk(os.curdir, remove_stale_bytecode, None)
build_ext.run(self)
class MyLibInstaller(installcmd):
def run(self):
installcmd.run(self)
finder.copy_files(self, self.install_dir)
class MyDistribution(Distribution):
# To control the selection of MyLibInstaller and MyPyBuilder, we
# have to set it into the cmdclass instance variable, set in
# Distribution.__init__().
def __init__(self, *attrs):
Distribution.__init__(self, *attrs)
self.cmdclass['install'] = ZopeInstall
self.cmdclass['build'] = MyBuilder
self.cmdclass['build_ext'] = MyExtBuilder
self.cmdclass['install_lib'] = MyLibInstaller
# All Zope3 extension modules must be listed here.
ext_modules = [
Extension("zope.proxy._zope_proxy_proxy",
["zope/proxy/_zope_proxy_proxy.c"],
include_dirs = ["zope/proxy"],
depends = ["zope/proxy/proxy.h"]),
Extension("zope.security._proxy", ["zope/security/_proxy.c"],
include_dirs = ["zope/proxy"],
depends = ["zope/proxy/proxy.h"]),
Extension("zope.security._zope_security_checker",
["zope/security/_zope_security_checker.c"],
include_dirs = [],
depends = []),
Extension("zope.interface._zope_interface_coptimizations",
["zope/interface/_zope_interface_coptimizations.c"]),
Extension("zope.hookable._zope_hookable",
["zope/hookable/_zope_hookable.c"]),
Extension("zope.thread._zope_thread",
["zope/thread/_zope_thread.c"]),
Extension("zope.app.container._zope_app_container_contained",
["zope/app/container/_zope_app_container_contained.c"],
include_dirs = ["persistent",
"zope/proxy",
"zope/app/container"],
depends = [
"persistent/cPersistence.h",
"zope/proxy/_zope_proxy_proxy.c",
]),
]
# We're using the module docstring as the distutils descriptions.
doclines = __doc__.split("\n")
setup(name="zopex30",
version="X3.0",
maintainer="Zope Corporation",
maintainer_email="zope3-dev@zope.org",
url = "http://dev.zope.org/Zope3/",
ext_modules = ext_modules,
license = "http://www.zope.org/Resources/ZPL",
platforms = ["any"],
description = doclines[0],
long_description = "\n".join(doclines[2:]),
packages = packages,
distclass = MyDistribution,
)
# Call distutils setup with all lib/python packages and modules, and
# flush setup_info. Wondering why we run py_modules separately? So am I.
# Distutils won't let us specify packages and py_modules in the same call.
......@@ -1109,8 +1327,9 @@ distutils.core.setup(
os.chdir(BASE_DIR)
def skel_visit(skel, dirname, names):
if "CVS" in names:
names.remove("CVS")
for ignore in IGNORE_NAMES:
if ignore in names:
names.remove(ignore)
L = []
for name in names:
if os.path.isfile(os.path.join(dirname, name)):
......
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