Commit 0afd69d4 authored by Hanno Schlichting's avatar Hanno Schlichting

Removed deprecated ZCML directives from Five including the whole Five.site subpackage

parent 17b5a358
......@@ -9,6 +9,9 @@ Zope Changes
Restructuring
- Removed deprecated ZCML directives from Five including the whole
Five.site subpackage.
- Turned deprecation warnings for manage_afterAdd, manage_beforeDelete
and manage_afterClone methods into discouraged warnings. These methods
will not be removed in Zope 2.11, but stay for the foreseeable future.
......
......@@ -7,8 +7,6 @@ v1.2
* backport r22057 from Five-1.3 branch: fix Localizer unit test problem
* i18n domain of site/managesite.pt?
* i18n and translation of utilities/browser/*pt
* can we stop using zLOG and use the logging package? please?
......@@ -30,16 +28,12 @@ v1.2
v1.3
----
* revisit the test_localsite/test_{get|query}NextSiteManager tests
* correctly treat ZCatalog.Catalog(Path)Awareness.CatalogAware w.r.t
events (efge)
v1.4
----
- fix up locale support in Five.form/Five.formlib
- namedtemplate in Five.formlib?
- l10n (philikon)
......
......@@ -46,33 +46,7 @@ def test_default_view():
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
BBB This is a test of backwards comaptibility with Five 1.3/Zope
2.9. The deprecated directive five:defaultViewable would before
make index.html the default view. Test that this is still the
case. five:defaultViewable goes away in Zope 2.12, and this test
goes then too:
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> zcml.load_string('''
... <configure xmlns:five="http://namespaces.zope.org/five">
... <five:defaultViewable
... class="Products.Five.tests.testing.simplecontent.SimpleContent" />
... </configure>''')
>>> print http(r'''
... GET /test_folder_1_/testoid HTTP/1.1
... Authorization: Basic manager:r00t
... ''')
HTTP/1.1 200 OK
...
The eagle has landed
>>> warnings.showwarning = showwarning
But if we want to, we can specify another default view with
browser:defaultView:
We can specify another default view with browser:defaultView:
>>> zcml.load_string('''
... <configure xmlns:browser="http://namespaces.zope.org/browser">
......
......@@ -8,7 +8,6 @@
<include file="deprecated.zcml"/>
<include file="traversing.zcml"/>
<include package=".component" />
<include package=".site" />
<include package=".browser" />
<include package=".form" />
<include package=".formlib" />
......
......@@ -16,11 +16,10 @@ adapter
Hook an adapter factory to an interface.
content
-------
class
-----
Declare interface and permissions on content object. Declares Zope 2
permissions.
Declare interface and permissions on classes. Declares Zope 2 permissions.
permission
----------
......@@ -48,17 +47,6 @@ interface
Register an interface in ZCML.
factory
-------
Register an object factory.
modulealias
-----------
Provide a module under an alias name, e.g. for persistent backward
compatability.
hook
----
......@@ -134,24 +122,6 @@ loadProductsOverrides
Loads overriding ZCML in all products (``overrides.zcml``).
traversable
-----------
This statement is now deprecated, since the functionality now is on Zope Core.
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
---------------
This statement is now deprecated, since the functionality now is on Zope Core.
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.
sizable
-------
......@@ -181,11 +151,3 @@ registerClass
-------------
Registers Five content with Zope 2.
localsite
---------
Turns a class into an implementation of ``IPossibleSite`` so that its
instances can be serve as local sites. Unless otherwise specified, a
default implementation's methods will be used to make the class comply
with the ``IPossibleSite`` interface.
......@@ -5,13 +5,6 @@ Five features
Five features are mostly Zope 3 features, though Five has some extras,
and some limitations.
Zope 3 interfaces
=================
Everything from the ``zope.interface`` package works. Zope 3
interfaces are the foundation of the component architecture, and also
the foundation of schemas.
ZCML
====
......@@ -33,38 +26,6 @@ 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
=====================
......@@ -79,23 +40,8 @@ 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
The ``class`` 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
protect directly exposed methods on content classes (the Python
scripts and the ZPublisher).
Local Sites
===========
Five supports the concept of a local sites and local site managers.
See localsite.txt_ for more information.
.. _localsite.txt: localsite.html
Object events
=============
Five supports sending Zope 3 object events when objects are added,
moved, renamed, copied and deleted. The use of ``manage_afterAdd`` & co
methods is deprecated.
Local sites in Five
===================
Intro
-----
Zope 3 has a concept of local sites and site managers. They allow one
to locally override component registrations and have components and
their configuration be persisted in the ZODB as well as managed
through the web interface.
Five 1.5 supports two distinct and slightly incompatible versions of
local site support. The first one, (here known as "old" sites) was
introduced in Five 1.3 and is based on Zope 3.2s site implementation.
But the site implementation in Zope 3.2 was overly complicated, and
for 3.3 it was refactored, and Five 1.5 contains support for this new
local site support as well (known as "new" sites).
For documentation of how to use local sites, see the Zope 3.3 documentation.
This document is mostly about how the implementation of the old sites work,
and how to migrate from Five 1.3 and 1.4.
Migration from old sites to new sites
-------------------------------------
New sites are based on Zope 3.3s new site managers and persistent
component registries. Old sites are based on specific Five implementation
of site managers, and keeps utilities in a folder called "utilities".
They are used basically the same, but they are created differently,
and also, when you look up a utility with getUtility or queryUtility, in
old sites, the utility will have an acquicition context, while in the new
sites it will not.
Setting up the site
...................
The old setup of a site was done by marking the class of the site as a
possible site with five:localsite, and then either manually through the ZMI
or programmatically through enableLocalSiteHook(site) turn it into a site.
The new setup involves calling enableSite(site) and createing and setting
a site manager. The simplest way to do this manually is to go to the
"components.html" view of the object you want to make a site, and press the
"Make site" button. The easist way to do this programmatically, is to look
up the "components.html" view on the site, and calling view.makeSite():
components_view = queryMultiAdapter((self.context, self.request),
Interface, 'components.html')
components_view.makeSite()
As any ObjectManager can be a site there is no longer any need to specially
mark the class as being a possible site.
Registering local utilities
...........................
The old usage was to get the site manager, either throgh adapting with
IFiveUtilityRegistry(site) or by calling getSiteManager(). You could then
register the utilities with sitemanager.registerUtility(interface, utility)
The new way is to call getSiteManager().registerUtility(object, provided).
Note that in the old way, the first parameter is the interface name, and
the second the actual utility object, but in the new way, it is the other
way around.
Migrating the actual sites
..........................
Go to the object of the site, and add "manage_site.html" to the URL to open
the old site management view. There you have a button
"Migrate to Five.component". Press it to migrate the site.
Experimental forwards compatibility
...................................
If you have software using the old sites, and software using the new sites,
there is sligthly experimental support to make software expecting new sites
to work with old sites. Nothing is promised with this, as it is largely
untested. The best thing to do is without a doubt to convert your old site
software and your old sites to use new sites.
Old site implementation details
===============================
The rest of this document documents the details of the old site implementation.
Everything from here on concerns only the old implementation and is of mainly
hysterical interest.
By default, Zope 3 has a global site which is configured through ZCML.
It provides the fallback for all component look-up. Local sites are
typically set during traversal, when the traverser encounters an
``ISite`` object. The last encountered ``ISite`` wins. Component
look-up will cascade through all the sites in the hierarchy and fall
back to the global site where it can finally fail.
Five also supports local sites, however by default only local
utilities. Local adapters, such as ZODB-based views, could be
supported with a custom implementation of the local site manager and
local adapter registry. This is not the focus of local sites in Five,
though.
Turning possible sites into sites
---------------------------------
Five uses the same technique as Zope 3 for determining local sites:
sites are found during URL traversal. However, since the Zope 2
ZPublisher doesn't emit the necessary events by default, Five needs to
set a ``BeforeTraverse`` hook on site objects.
Setting this hook needs to be done an object-per-object basis and can
be performed through the ``manage_site.html`` browser page. This view
operates on ``IPossibleSite`` objects (in other words, objects that
*can* be sites but aren't yet). It sets the traversal hook on the
object and then marks it with the ``ISite`` interface to indicate that
it is a real site now, not just a possible site.
Note that unlike the Zope 3 equivalent of this view, it does not set
the site manager to site; it is assumed that the site already knows
how to get its site manager.
Custom site implementations
---------------------------
Anything can be a site, there are no restrictions (sites don't have to
be folders, for examples). Sites can also be nested. For all the
Component Architecture cares, every object in your URL graph could be
a site.
The only requirement are two interfaces:
``IPossibleSite``
Objects that can potentially be turned into a site need to provide
this interface. That requires them to have a ``setSiteManager()``
and ``getSiteManager()`` method for setting and getting the local
site manager of that site. The site manager is the registry that
takes care of local component look-up.
``IFiveSiteManager``
This interface is a slight extension of the ``IServiceService`` or
``ISiteManager`` interface, respectively (the former in Zope X3
3.0, the latter in later versions). It defines the API of a local
site manager that is to be used in a Five environment. The site's
``getSiteManager()`` method should return an object providing this
interface.
Five's default site manager
----------------------------
If you want to instantly make your custom class an ``IPossibleSite``
implementation, you can use a default mix-in class from Five, e.g.::
class MySite(OFS.Folder, Products.Five.site.localsite.FiveSite):
pass
This default implementation of ``IPossibleSite`` features a site
manager implementation that knows how to register and look-up local
utilities. It does so by adapting the site to
``IFiveUtilityRegistry``.
The default adapter for this local utility registry simply stores the
utilities in a standard OFS Folder on called ``utilities`` on the site
object. You probably want to exchange that simple behaviour with
something that works better in your application. You can do so by
plugging in your own utility registry adapter, e.g.::
<adapter for=".interfaces.IMySite"
provides="Products.Five.site.interfaces.IFiveUtilityRegistry"
fatory=".module.MyUtilityRegistry" />
All this implementation needs to do is comply with the
``IFiveUtilityRegistry`` interface, which essentially means the
standard utility look-up methods like ``queryUtility()``,
``getUtilitiesFor()``, etc.
Turning existing classes into possible sites
--------------------------------------------
If you cannot or do not want to modify existing classes to mix in the
``FiveSite`` class, you can also use a structured monkey patch via
ZCML::
<five:localsite class=".module.MyClass" />
This makes ``MyClass`` an ``IPossibleSite`` and sticks ``FiveSite``'s
``getSiteManager()`` and ``setSiteManager()`` methods on the class as
well. You can also tell it to use a different site implementation's
methods for the monkey patch::
<five:localsite class=".module.MyClass"
site_class=".module.MySiteImpl" />
Just make sure that this class implements ``IPossibleSite``.
......@@ -242,7 +242,7 @@ Default view for object
What if we traverse to the object itself?
Use five:defaultViewable and browser:defaultView
Use browser:defaultView
%page
......@@ -259,8 +259,6 @@ DefaultView example
%size 4, fore "blue"
<five:defaultViewable class=".democontent.DemoContent" />
<browser:defaultView
for=".democontent.IDemoContent"
name="someview.html" />
......@@ -270,8 +268,6 @@ DefaultView example
Conclusions
This works much the same way as Zope 3 does too
This works 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
......@@ -105,22 +105,6 @@ def implements(_context, class_, interface):
interface)
)
# BBB 2006/05/01 -- to be removed after 12 months
def traversable(_context, class_):
warnings.warn("The five:traversable statement is no longer needed "
"and will be removed in Zope 2.12.",
DeprecationWarning, 2)
# BBB 2006/05/01 -- to be removed after 12 months
def defaultViewable(_context, class_):
warnings.warn("The five:defaultViewable statement is no longer "
"needed and will be removed in Zope 2.12. \n If you rely "
"on it to make 'index.html' the default view, replace it "
"with <browser:defaultView name='index.html' />",
DeprecationWarning, 2)
from Products.Five.bbb import IBrowserDefault
implements(_context, class_, (IBrowserDefault,))
def createZope2Bridge(zope2, package, name):
# Map a Zope 2 interface into a Zope3 interface, seated within 'package'
# as 'name'.
......
......@@ -36,30 +36,6 @@ class IImplementsDirective(Interface):
value_type=GlobalObject()
)
# BBB 2006/05/01 -- to be removed after 12 months
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
)
# BBB 2006/05/01 -- to be removed after 12 months
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 ISizableDirective(Interface):
"""Make instances of class send events.
"""
......
......@@ -6,10 +6,6 @@
<include package="zope.security" file="meta.zcml" />
<include package="zope.i18n" file="meta.zcml" />
<!-- BBB 2006/02/24, to be removed after 12 months -->
<include package="zope.modulealias" file="meta.zcml" />
<include package=".site" file="meta.zcml" />
<include package=".browser" file="meta.zcml" />
<include package=".form" file="meta.zcml" />
......@@ -21,13 +17,6 @@
handler="zope.app.component.metaconfigure.view"
/>
<!-- BBB 2006/02/24, to be removed after 12 months -->
<meta:directive
name="factory"
schema="zope.app.component.metadirectives.IFactoryDirective"
handler="zope.app.component.metaconfigure.factory"
/>
<meta:complexDirective
name="class"
schema="zope.app.component.metadirectives.IClassDirective"
......@@ -51,30 +40,6 @@
</meta:complexDirective>
<!-- BBB 2006/02/24, to be removed after 12 months -->
<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="securityPolicy"
schema="zope.security.zcml.ISecurityPolicyDirective"
......@@ -105,18 +70,6 @@
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="containerEvents"
schema=".fivedirectives.IContainerEventsDirective"
......
......@@ -44,13 +44,3 @@ class ClassDirective(contentdirective.ClassDirective):
callable = initializeClass,
args = (self.__class,)
)
# BBB 2006/02/24, to be removed after 12 months
class ContentDirective(contentdirective.ClassDirective):
def __init__(self, _context, class_):
warnings.warn_explicit(
"The 'content' alias for the 'class' directive has been "
"deprecated and will be removed in Zope 2.12.\n",
DeprecationWarning, _context.info.file, _context.info.line)
super(ContentDirective, self).__init__(_context, 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.
#
##############################################################################
"""Local sites browser views
$Id$
"""
from zope.app.component.interfaces import ISite
from zope.app.component.hooks import clearSite, setSite
from zope.component import getSiteManager, queryMultiAdapter
from zope.interface import Interface, providedBy
from Products.Five.browser import BrowserView
from Products.Five.site.localsite import enableLocalSiteHook, disableLocalSiteHook
class LocalSiteView(BrowserView):
"""View for convering a possible site to a site
"""
def update(self):
form = self.request.form
if form.has_key('UPDATE_MAKESITE'):
self.makeSite()
elif form.has_key('UPDATE_UNMAKESITE'):
self.unmakeSite()
elif form.has_key('UPDATE_MIGRATE'):
self.migrateToFive15()
def isSite(self):
return ISite.providedBy(self.context)
def isOldSite(self):
from Products.Five.site.interfaces import IFiveSiteManager
return self.isSite() and IFiveSiteManager.providedBy(getSiteManager())
def makeSite(self):
"""Convert a possible site to a site"""
if self.isSite():
raise ValueError('This is already a site')
enableLocalSiteHook(self.context)
return "This object is now a site"
def unmakeSite(self):
"""Convert a site to a possible site"""
if not self.isSite():
raise ValueError('This is not a site')
disableLocalSiteHook(self.context)
# disableLocalSiteHook circumcised our context so that it's
# not an ISite anymore. That can mean that certain things for
# it can't be found anymore. So, for the rest of this request
# (which will be over in about 20 CPU cycles), already clear
# the local site from the thread local.
clearSite()
return "This object is no longer a site"
def migrateToFive15(self):
all_utilities = self.context.utilities.objectItems()
self.unmakeSite()
self.context.manage_delObjects(['utilities'])
components_view = queryMultiAdapter((self.context, self.request),
Interface, 'components.html')
components_view.makeSite()
setSite(self.context)
site_manager = getSiteManager()
for id, utility in all_utilities:
info = id.split('-')
if len(info) == 1:
name = ''
else:
name = info[1]
interface_name = info[0]
for iface in providedBy(utility):
if iface.getName() == interface_name:
site_manager.registerUtility(utility, iface, name=name)
return "Migration done!"
<configure xmlns="http://namespaces.zope.org/zope"
xmlns:browser="http://namespaces.zope.org/browser">
<adapter
for="zope.app.component.interfaces.ISite"
provides=".interfaces.IFiveUtilityRegistry"
factory=".utility.SimpleLocalUtilityRegistry"
/>
<browser:page
for="zope.app.component.interfaces.IPossibleSite"
name="manage_site.html"
permission="five.ManageSite"
class=".browser.LocalSiteView"
template="managesite.pt"
/>
</configure>
##############################################################################
#
# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Five interfaces
$Id$
"""
from zope.interface import Interface, Attribute
from zope.component.interfaces import IComponentLookup
class IRegisterUtilitySimply(Interface):
"""Register utilities simply
Allow local registrations of utilities, in a much simpler
manner than Zope 3 does it currently.
Note: The name of this interface is expressed as a verb
(describing the action it expresses, namely registering
utilities). The reason for that is that the names *utility
registry* (successor of the Zope 3 utility service) and *utility
registration* (object in a registration stack, part of the
complicated registration framework in Zope 3) have different
connotations in Zope 3 than we want to express here.
"""
def registerUtility(self, interface, utility, name=''):
"""Registers a utility in the local context"""
# TODO Define an exception than is to be thrown when a local
# utility of that interface and name is already registered.
next = Attribute("The next local registry in the tree. This attribute "
"represents the parent of this registry node. If the "
"value is ``None``, then this registry represents the "
"root of the tree")
class IFiveUtilityRegistry(IRegisterUtilitySimply):
"""Look up and register utilities"""
def getUtility(interface, name='', context=None):
"""Get the utility that provides interface
Returns the nearest utility to the context that implements the
specified interface. If one is not found, raises
ComponentLookupError.
"""
def queryUtility(interface, name='', default=None, context=None):
"""Look for the utility that provides interface
Returns the nearest utility to the context that implements
the specified interface. If one is not found, returns default.
"""
def getUtilitiesFor(interface, context=None):
"""Return the utilities that provide an interface
An iterable of utility name-value pairs is returned.
"""
def getAllUtilitiesRegisteredFor(interface, context=None):
"""Return all registered utilities for an interface
This includes overridden utilities.
An iterable of utility instances is returned. No names are
returned.
"""
class IFiveSiteManager(IComponentLookup, IRegisterUtilitySimply):
"""Five site manager
For the sake of forward-portability, registering utilities can be
done directly on the site manager to cut out the middle man called
utility service (this corresponds to Zope 3.1's understanding of
site managers). An implementation of this interface will probably
delegate the work to an IFiveUtilityService component, though."""
##############################################################################
#
# 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.
#
##############################################################################
"""Local sites
$Id$
"""
from zope.app.component.interfaces import ISite, IPossibleSite
from zope.component import getGlobalSiteManager
from zope.interface import implements
from zope.interface.interface import InterfaceClass
from Acquisition import aq_parent, aq_inner
from Products.Five.site.interfaces import IFiveSiteManager, IFiveUtilityRegistry
from operator import xor
def one_of_three(a, b, c):
# Logical table for a three part test where only one can be true:
# 0 0 0: 0
# 0 0 1: 1
# 0 1 0: 1
# 0 1 1: 0
# 1 0 0: 1
# 1 0 1: 0
# 1 1 0: 0
# 1 1 1: 0
return xor(xor(a, b), c) and not (a and b and c)
class FiveSiteManager(object):
implements(IFiveSiteManager)
def __init__(self, context):
# make {get|query}NextSiteManager() work without having to
# resort to Zope 2 acquisition
self.context = self.__parent__ = context
warnings.warn("The FiveSiteManager is deprecated and will be removed "
"in Zope 2.12. \nSee Five/doc/localsite.txt .",
DeprecationWarning, 2)
@property
def __bases__(self):
next = self.next
if next is None:
return (getGlobalSiteManager(),)
return (next,)
@property
def next(self):
obj = self.context
while obj is not None:
obj = aq_parent(aq_inner(obj))
if ISite.providedBy(obj):
return obj.getSiteManager()
# In Zope 3.1+, returning None here is understood by
# getNextSiteManager as that our next site manager is the
# global one. If we returned the global one, it would be
# understood as a lookup error. Yeah, it's weird, tell me
# about it.
return None
@property
def adapters(self):
next = self.next
if next is None:
next = getGlobalSiteManager()
return next.adapters
@property
def utilities(self):
return IFiveUtilityRegistry(self.context)
def queryAdapter(self, object, interface, name, default=None):
return self.adapters.queryAdapter(object, interface, name, default)
def queryMultiAdapter(self, objects, interface, name, default=None):
return self.adapters.queryMultiAdapter(objects, interface, name, default)
def getAdapters(self, objects, provided):
next = self.next
if next is None:
next = getGlobalSiteManager()
return next.getAdapters(objects, provided)
def subscribers(self, required, provided):
return self.adapters.subscribers(required, provided)
def queryUtility(self, interface, name='', default=None):
return self.utilities.queryUtility(interface, name, default)
def getUtilitiesFor(self, interface):
return self.utilities.getUtilitiesFor(interface)
def getAllUtilitiesRegisteredFor(self, interface):
return self.utilities.getAllUtilitiesRegisteredFor(interface)
def registerUtility(self, *args, **kw):
# Can be called with new API:
# component, provided=None, name=u'', info=u'', event=True
# where info and event are ignored, or old api:
# interface, utility, name=''
name = kw.get('name', u'')
interface_kw = kw.get('interface', None)
provided_kw = kw.get('provided', None)
utility_kw = kw.get('utility', None)
component_kw = kw.get('component', None)
interface = None
utility = None
if len(args) > 0:
# Positional argument 1
if isinstance(args[0], InterfaceClass):
interface = args[0]
else:
utility = args[0]
if len(args) > 1:
if isinstance(args[1], InterfaceClass):
interface = args[1]
else:
utility = args[1]
if len(args) > 2:
if name:
raise TypeError("You can only provide one name")
else:
name = args[2]
if not one_of_three(interface is not None,
interface_kw is not None,
provided_kw is not None):
raise TypeError("You can specify one and only one interface")
if interface is None:
interface = interface_kw
if interface is None:
interface = provided_kw
if not one_of_three(utility is not None,
utility_kw is not None,
component_kw is not None):
raise TypeError("You can specify one and only one interface")
if utility is None:
utility = utility_kw
if utility is None:
utility = component_kw
return self.utilities.registerUtility(interface, utility, name)
class FiveSite:
implements(IPossibleSite)
def getSiteManager(self):
return FiveSiteManager(self)
def setSiteManager(self, sm):
raise NotImplementedError('This class has a fixed site manager')
#BBB: Goes away in Five Zope 2.12
import warnings
from Products.Five.component import enableSite, disableSite
from zope.app.component.hooks import setSite, clearSite, setHooks
def enableLocalSiteHook(obj):
warnings.warn("The enableLocalSiteHook is deprecated and will be removed "
"in Zope 2.12. \nSee Five/doc/localsite.txt .",
DeprecationWarning, 2)
enableSite(obj)
components = FiveSiteManager(obj)
obj.setSiteManager(components)
setSite(obj)
setHooks()
def disableLocalSiteHook(obj):
"""Remove __before_traverse__ hook for Local Site
"""
# This method is of course deprecated too, but issuing a warning is
# silly.
disableSite(obj)
clearSite()
obj.setSiteManager(None)
<tal:tag condition="view/update"/>
<html metal:use-macro="context/@@standard_macros/view"
i18n:domain="zope">
<body>
<div metal:fill-slot="body">
<form action="." tal:attributes="action request/URL" method="POST"
enctype="multipart/form-data">
<div class="row">
<div class="controls">
<input type="submit" value="Make site" name="UPDATE_MAKESITE"
i18n:attributes="value"
tal:attributes="disabled view/isSite"/>
<input type="submit" value="Unmake site" name="UPDATE_UNMAKESITE"
i18n:attributes="value"
tal:attributes="disabled not:view/isSite"/>
<input type="submit" value="Migrate to Five.component"
name="UPDATE_MIGRATE"
i18n:attributes="value"
tal:attributes="disabled not:view/isOldSite"/>
</div>
</div>
</form>
</div>
</body>
</html>
<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="localsite"
schema=".metadirectives.ILocalSiteDirective"
handler=".metaconfigure.installSiteHook"
/>
</meta:directives>
</configure>
##############################################################################
#
# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Five-specific directive handlers
These directives are specific to Five and have no equivalents in Zope 3.
$Id$
"""
from zope.interface import classImplements, classImplementsOnly, implementedBy
from zope.app.component.interfaces import IPossibleSite
import logging, warnings
LOG = logging.getLogger('Five')
_localsite_monkies = []
def classSiteHook(class_, site_class):
if class_ in _localsite_monkies:
LOG.warn("Class %s already has a site hook" % class_)
else:
_localsite_monkies.append(class_)
setattr(class_, 'getSiteManager', site_class.getSiteManager.im_func)
setattr(class_, 'setSiteManager', site_class.setSiteManager.im_func)
def installSiteHook(_context, class_, site_class=None):
warnings.warn_explicit("The five:localsite directive is deprecated and "
"will be removed in Zope 2.12. \n"
"See Five/doc/localsite.txt .",
DeprecationWarning,
_context.info.file, _context.info.line)
# only install the hook once
already = getattr(class_, '_localsite_marker', False)
if site_class is not None and not already:
class_._localsite_marker = True
_context.action(
discriminator = (class_,),
callable = classSiteHook,
args=(class_, site_class)
)
if not IPossibleSite.implementedBy(class_):
_context.action(
discriminator = (class_, IPossibleSite),
callable = classImplements,
args=(class_, IPossibleSite)
)
# clean up code
def uninstallSiteHooks():
for class_ in _localsite_monkies:
_localsite_monkies.remove(class_)
delattr(class_, 'getSiteManager')
delattr(class_, 'setSiteManager')
classImplementsOnly(class_, implementedBy(class_)-IPossibleSite)
if getattr(class_, '_localsite_marker', False):
delattr(class_, '_localsite_marker')
from zope.testing.cleanup import addCleanUp
addCleanUp(uninstallSiteHooks)
del addCleanUp
##############################################################################
#
# Copyright (c) 2004, 2005 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Site support ZCML directive schemas
$Id$
"""
from zope.interface import Interface
from zope.configuration.fields import GlobalObject
class ILocalSiteDirective(Interface):
"""Make instances of class hookable for Site.
site_class is an implementation of ISite, which will have it's methods
monkey_patched into the the class. If not given a default implementation
will be used.
"""
class_ = GlobalObject(
title=u"Class",
required=True
)
site_class = GlobalObject(
title=u"Site Class",
required=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.
#
##############################################################################
"""Dummy test fixtures
$Id$
"""
from zope.interface import implements, implementsOnly, Interface
from OFS.SimpleItem import SimpleItem
from Products.Five.tests.testing import FiveTraversableFolder
class IDummySite(Interface):
pass
class DummySite(FiveTraversableFolder):
"""A very dummy Site
"""
# we specifically do not let this site inherit any interfaces from
# the superclasses so that this class does not implement
# IPossibleSite under any circumstances
implementsOnly(IDummySite)
def manage_addDummySite(self, id, REQUEST=None):
"""Add the dummy site."""
id = self._setObject(id, DummySite(id))
return ''
class IDummyUtility(Interface):
pass
class ISuperDummyUtility(IDummyUtility):
pass
class DummyUtility(SimpleItem):
implements(IDummyUtility)
##############################################################################
#
# ZopeTestCase
#
# COPY THIS FILE TO YOUR 'tests' DIRECTORY.
#
# This version of framework.py will use the SOFTWARE_HOME
# environment variable to locate Zope and the Testing package.
#
# If the tests are run in an INSTANCE_HOME installation of Zope,
# Products.__path__ and sys.path with be adjusted to include the
# instance's Products and lib/python directories respectively.
#
# If you explicitly set INSTANCE_HOME prior to running the tests,
# auto-detection is disabled and the specified path will be used
# instead.
#
# If the 'tests' directory contains a custom_zodb.py file, INSTANCE_HOME
# will be adjusted to use it.
#
# If you set the ZEO_INSTANCE_HOME environment variable a ZEO setup
# is assumed, and you can attach to a running ZEO server (via the
# instance's custom_zodb.py).
#
##############################################################################
#
# The following code should be at the top of every test module:
#
# import os, sys
# if __name__ == '__main__':
# execfile(os.path.join(sys.path[0], 'framework.py'))
#
# ...and the following at the bottom:
#
# if __name__ == '__main__':
# framework()
#
##############################################################################
__version__ = '0.2.3'
# Save start state
#
__SOFTWARE_HOME = os.environ.get('SOFTWARE_HOME', '')
__INSTANCE_HOME = os.environ.get('INSTANCE_HOME', '')
if __SOFTWARE_HOME.endswith(os.sep):
__SOFTWARE_HOME = os.path.dirname(__SOFTWARE_HOME)
if __INSTANCE_HOME.endswith(os.sep):
__INSTANCE_HOME = os.path.dirname(__INSTANCE_HOME)
# Find and import the Testing package
#
if not sys.modules.has_key('Testing'):
p0 = sys.path[0]
if p0 and __name__ == '__main__':
os.chdir(p0)
p0 = ''
s = __SOFTWARE_HOME
p = d = s and s or os.getcwd()
while d:
if os.path.isdir(os.path.join(p, 'Testing')):
zope_home = os.path.dirname(os.path.dirname(p))
sys.path[:1] = [p0, p, zope_home]
break
p, d = s and ('','') or os.path.split(p)
else:
print 'Unable to locate Testing package.',
print 'You might need to set SOFTWARE_HOME.'
sys.exit(1)
import Testing, unittest
execfile(os.path.join(os.path.dirname(Testing.__file__), 'common.py'))
# Include ZopeTestCase support
#
if 1: # Create a new scope
p = os.path.join(os.path.dirname(Testing.__file__), 'ZopeTestCase')
if not os.path.isdir(p):
print 'Unable to locate ZopeTestCase package.',
print 'You might need to install ZopeTestCase.'
sys.exit(1)
ztc_common = 'ztc_common.py'
ztc_common_global = os.path.join(p, ztc_common)
f = 0
if os.path.exists(ztc_common_global):
execfile(ztc_common_global)
f = 1
if os.path.exists(ztc_common):
execfile(ztc_common)
f = 1
if not f:
print 'Unable to locate %s.' % ztc_common
sys.exit(1)
# Debug
#
print 'SOFTWARE_HOME: %s' % os.environ.get('SOFTWARE_HOME', 'Not set')
print 'INSTANCE_HOME: %s' % os.environ.get('INSTANCE_HOME', 'Not set')
sys.stdout.flush()
Functional test for local sites
===============================
This tests how local site managers are found during traversal and how
that affects component lookup (depending on whether a site is
traversed or not, local components might or might not be found).
First, we set up all of Five:
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config("configure.zcml", Products.Five)
Then we hook up the custom component architecture calls; we need to do
this here because zope.app.component.hooks registers a cleanup with
the testing cleanup framework, so the hooks get torn down by
placelesssetup each time.
>>> from zope.app.component.hooks import setHooks
>>> setHooks()
Next we turn our DummySite class into a site in ZCML (and register
some views that will provide us with some test info),
>>> zcml_text = """
... <configure xmlns="http://namespaces.zope.org/zope"
... xmlns:meta="http://namespaces.zope.org/meta"
... xmlns:five="http://namespaces.zope.org/five"
... xmlns:browser="http://namespaces.zope.org/browser">
...
... <!-- make the zope2.Public permission work -->
... <meta:redefinePermission from="zope2.Public" to="zope.Public" />
...
... <five:localsite class="Products.Five.site.tests.dummy.DummySite" />
...
... <browser:page
... for="Products.Five.site.tests.dummy.IDummySite"
... name="checkSiteManager.html"
... class="Products.Five.site.tests.test_functional.CheckSiteManagerView"
... permission="zope2.Public"
... />
...
... <browser:page
... for="Products.Five.site.tests.dummy.IDummySite"
... name="lookupUtilities.html"
... class="Products.Five.site.tests.test_functional.LookupUtilitiesView"
... permission="zope2.Public"
... />
...
... </configure>"""
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> zcml.load_string(zcml_text)
>>> warnings.showwarning = showwarning
then we add an instance to our folder:
>>> from Products.Five.site.tests.dummy import manage_addDummySite
>>> nothing = manage_addDummySite(self.folder, 'site')
Now we check what the info view tells us about local component lookup:
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> print http(r'''
... GET /test_folder_1_/site/@@checkSiteManager.html HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
zapi.getSiteManager() is zapi.getGlobalSiteManager(): True
IFiveUtilityRegistry.providedBy(utility_service): False
isinstance(zapi.getSiteManager(), FiveSiteManager): False
We see that we have no local component lookup yet, because we haven't
set the site. Therefore, enable the traversal hook by using the view
that's provided for this task (we first need to create a manager
account in order to be able to access it):
>>> uf = self.folder.acl_users
>>> uf._doAddUser('manager', 'r00t', ['Manager'], [])
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> print http(r'''
... POST /test_folder_1_/site/@@manage_site.html HTTP/1.1
... Authorization: Basic manager:r00t
... Content-Length: 25
...
... UPDATE_MAKESITE=Make site''')
HTTP/1.1 200 OK
...
>>> warnings.showwarning = showwarning
>>> warnings.showwarning = showwarning
Now we call the info view again and find that local component lookup
is working:
>>> print http(r'''
... GET /test_folder_1_/site/@@checkSiteManager.html HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
zapi.getSiteManager() is zapi.getGlobalSiteManager(): False
IFiveUtilityRegistry.providedBy(utility_service): True
isinstance(zapi.getSiteManager(), FiveSiteManager): True
Of course, sites are only active *during* traversal; after traversal
they're gone:
>>> from zope.app.component.hooks import getSite
>>> getSite() is None
True
We can also register utilities now:
>>> from zope.app import zapi
>>> sm = self.folder.site.getSiteManager()
>>> from Products.Five.site.tests.dummy import IDummyUtility, DummyUtility
>>> dummy = DummyUtility()
>>> sm.registerUtility(IDummyUtility, dummy)
and find them being looked up just fine:
>>> print http(r'''
... GET /test_folder_1_/site/@@lookupUtilities.html HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
zapi.getUtility(IDummyUtility) == dummy: True
Of course, we can't look it up once the request has ended, because we
lose the local site setup:
>>> zapi.getUtility(IDummyUtility)
Traceback (most recent call last):
...
ComponentLookupError: (<InterfaceClass Products.Five.site.tests.dummy.IDummyUtility>, '')
At last we can "unmake" the site using the browser view provided by
Five:
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> print http(r'''
... POST /test_folder_1_/site/@@manage_site.html HTTP/1.1
... Authorization: Basic manager:r00t
... Content-Length: 29
...
... UPDATE_UNMAKESITE=Unmake site''')
HTTP/1.1 200 OK
...
>>> warnings.showwarning = showwarning
And everything is back to normal with respect to local component
lookup:
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> print http(r'''
... GET /test_folder_1_/site/@@checkSiteManager.html HTTP/1.1
... ''')
HTTP/1.1 200 OK
...
zapi.getSiteManager() is zapi.getGlobalSiteManager(): True
IFiveUtilityRegistry.providedBy(utility_service): False
isinstance(zapi.getSiteManager(), FiveSiteManager): False
Finally, global services and the monkeys:
>>> from zope.app.testing.placelesssetup import tearDown
>>> tearDown()
Five Site Manager
=================
In this test we want to test Five's implementation of a site manager.
First, we need to set a few things up...
>>> from zope.app.testing.placelesssetup import setUp, tearDown
>>> setUp()
>>> import Products.Five
>>> from Products.Five import zcml
>>> import warnings
>>> showwarning = warnings.showwarning
>>> warnings.showwarning = lambda *a, **k: None
>>> zcml.load_config("meta.zcml", Products.Five)
>>> zcml.load_config("permissions.zcml", Products.Five)
>>> zcml.load_config("configure.zcml", Products.Five.site)
>>> zcml_text = """\
... <five:localsite
... xmlns:five="http://namespaces.zope.org/five"
... class="Products.Five.site.tests.dummy.DummySite" />"""
>>> zcml.load_string(zcml_text)
...for example some sort of site object:
>>> from Products.Five.site.tests.dummy import manage_addDummySite
>>> nothing = manage_addDummySite(self.folder, 'dummysite')
>>> dummysite = self.folder.dummysite
Local vs. global sites
----------------------
Let's make the possible site a real site:
>>> from Products.Five.site.localsite import enableLocalSiteHook
>>> enableLocalSiteHook(dummysite)
>>> warnings.showwarning = showwarning
and tell Zope 3 about it:
>>> from zope.app.component.hooks import setSite, setHooks
>>> setSite(dummysite)
Also hook up custom component architecture calls; we need to do this
here because zope.app.component.hooks registers a cleanup with the
testing cleanup framework, so the hooks get torn down by
placelesssetup each time.
>>> setHooks()
That seems to have worked (we test this by using the context property
of FiveSiteManager):
>>> from zope.app import zapi
>>> zapi.getSiteManager().context == dummysite
True
Since there's no other local site in between this one and the global
one, the next one should be the global one. FiveSiteManager indicates
that to us by return ``None``:
>>> from zope.app import zapi
>>> zapi.getSiteManager().next is None
True
To the the Zope 3 API, this means the next site manager should be the
global one:
>>> from zope.app.component import getNextSiteManager
>>> getNextSiteManager(dummysite.getSiteManager()) is zapi.getGlobalSiteManager()
True
ISiteManager API
----------------
Site managers are supposed to have an ``adapters`` and a ``utilities``
attribute. Five's site manager simply passes through the global
adapter registry:
>>> zapi.getSiteManager().adapters is zapi.getGlobalSiteManager().adapters
True
The utility registry, however, is an ``IFiveUtilityRegistry``:
>>> from Products.Five.site.interfaces import IFiveUtilityRegistry
>>> IFiveUtilityRegistry.providedBy(zapi.getSiteManager().utilities)
True
The methods on registering and looking up utilities are covered by the
utility tests in depth.
We test some adapter look-up here. It is also indirectly covered in
the functional test; view look up, for example, is adapter look up.
First we provide an adapter:
>>> from Products.Five.tests.adapters import Adaptable, Adapter, IAdapted
>>> import zope.component
>>> zope.component.provideAdapter(Adapter)
Now let's check for a simple adaption:
>>> adaptable = Adaptable()
>>> IAdapted(adaptable) #doctest: +ELLIPSIS
<Products.Five.tests.adapters.Adapter instance at ...>
Let's get all the adapters for ``adaptable``:
>>> list(zapi.getAdapters((adaptable,), IAdapted)) #doctest: +ELLIPSIS
[(u'', <Products.Five.tests.adapters.Adapter instance at ...>)]
Nesting sites
-------------
Let's set up another site to test nested sites:
>>> nothing = manage_addDummySite(self.folder.dummysite, 'subsite')
>>> subsite = self.folder.dummysite.subsite
Now we set the current site to the ``subsite``:
>>> enableLocalSiteHook(subsite)
>>> setSite(subsite)
When we call getServices() now, we get the correct site manager:
>>> zapi.getSiteManager().context == subsite
True
The "next" site is the less local one:
>>> zapi.getSiteManager().next.context == dummysite
True
The Zope 3 API for this agrees with that:
>>> getNextSiteManager(subsite.getSiteManager()).context == dummysite
True
The adapters is registry is passed through to the global one:
>>> subsite.getSiteManager().adapters is zapi.getGlobalSiteManager().adapters
True
Finally, some clean up:
>>> tearDown()
##############################################################################
#
# 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 local sites
$Id$
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
from zope.app import zapi
from Products.Five import BrowserView
from Products.Five.site.interfaces import IFiveUtilityRegistry
from Products.Five.site.localsite import FiveSiteManager
from Products.Five.site.tests.dummy import IDummyUtility
class CheckSiteManagerView(BrowserView):
def __call__(self):
sm = zapi.getSiteManager()
result = ('zapi.getSiteManager() is zapi.getGlobalSiteManager(): %s\n'
'IFiveUtilityRegistry.providedBy(utility_service): %s\n'
'isinstance(zapi.getSiteManager(), FiveSiteManager): %s'
% (sm is zapi.getGlobalSiteManager(),
IFiveUtilityRegistry.providedBy(sm.utilities),
isinstance(sm, FiveSiteManager)))
return result
class LookupUtilitiesView(BrowserView):
def __call__(self):
dummy = getattr(self.context.utilities, IDummyUtility.getName())
return "zapi.getUtility(IDummyUtility) == dummy: %s" % \
(zapi.getUtility(IDummyUtility) == dummy)
def test_suite():
from Testing.ZopeTestCase import FunctionalDocFileSuite
return FunctionalDocFileSuite('functional.txt',
package='Products.Five.site.tests')
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 local sites
$Id$
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
import unittest
import zope.interface
from zope.component import getGlobalSiteManager, getSiteManager
from zope.component.interfaces import ComponentLookupError
from zope.component.interfaces import IComponentLookup
from zope.traversing.interfaces import IContainmentRoot
from zope.app.component.hooks import setSite, getSite, setHooks
from zope.app.component.interfaces import ISite
from zope.app.testing.placelesssetup import PlacelessSetup
from Acquisition import Implicit
from OFS.ObjectManager import ObjectManager
import Products.Five
from Products.Five import zcml
class SiteManager(Implicit):
zope.interface.implements(IComponentLookup)
@property
def __bases__(self):
next = getattr(self, 'next', None)
if next is None:
return ()
return (next,)
class Folder(ObjectManager):
def setSiteManager(self, sm):
super(Folder, self).setSiteManager(sm)
zope.interface.alsoProvides(self, ISite)
class Package(Implicit):
pass
class Root(Folder):
zope.interface.implements(IContainmentRoot, ISite)
def getSiteManager(self):
return getGlobalSiteManager()
class SiteManagerStub(object):
zope.interface.implements(IComponentLookup)
class SiteManagerTest(PlacelessSetup, unittest.TestCase):
def setUp(self):
super(SiteManagerTest, self).setUp()
self.root = root = Root()
self.f1 = f1 = Folder().__of__(root)
self.sm1 = sm1 = SiteManager()
f1.setSiteManager(sm1)
self.p1 = p1 = Package().__of__(sm1)
self.f2 = f2 = Folder().__of__(f1)
self.sm2 = sm2 = SiteManager()
f2.setSiteManager(sm2)
self.p2 = p2 = Package().__of__(sm2)
sm1.next = getGlobalSiteManager()
sm2.next = sm1
self.unparented_folder = Folder()
self.unrooted_subfolder = Folder().__of__(self.unparented_folder)
zcml.load_config("meta.zcml", Products.Five)
zcml.load_config("permissions.zcml", Products.Five)
zcml.load_config("configure.zcml", Products.Five.component)
zcml.load_config("configure.zcml", Products.Five.site)
zcml_text = """\
<five:localsite
xmlns:five="http://namespaces.zope.org/five"
class="Products.Five.site.tests.dummy.DummySite" />"""
import warnings
showwarning = warnings.showwarning
warnings.showwarning = lambda *a, **k: None
zcml.load_string(zcml_text)
warnings.showwarning = showwarning
# Hook up custom component architecture calls; we need to do
# this here because zope.app.component.hooks registers a
# cleanup with the testing cleanup framework, so the hooks get
# torn down by placelesssetup each time.
setHooks()
def test_getSiteManager(self):
self.assertEqual(getSiteManager(None), getGlobalSiteManager())
self.assertEqual(getSiteManager(self.root), getGlobalSiteManager())
self.assertEqual(getSiteManager(self.f1), self.sm1)
self.assertEqual(getSiteManager(self.f2), self.sm2)
setSite(self.f2)
self.assertEqual(getSiteManager(None), self.sm2)
# DEPRECATED in Zope 3.4
#def test_queryNextSiteManager(self):
# from zope.app.component import queryNextSiteManager
# marker = object()
# self.assert_(queryNextSiteManager(self.root, marker) is marker)
# self.assert_(queryNextSiteManager(self.f1, marker) is getGlobalSiteManager())
# self.assertEqual(queryNextSiteManager(self.f2, marker), self.sm1)
# self.assertEqual(queryNextSiteManager(self.sm1), getGlobalSiteManager())
# self.assertEqual(queryNextSiteManager(self.sm2), self.sm1)
# self.assert_(queryNextSiteManager(self.p1, marker) is marker)
# self.assert_(queryNextSiteManager(self.p2, marker) is marker)
#
# self.assert_(queryNextSiteManager(self.unparented_folder, marker)
# is marker)
# self.assert_(queryNextSiteManager(self.unrooted_subfolder, marker)
# is marker)
# DEPRECATED in Zope 3.4
#def test_getNextSiteManager(self):
# from zope.app.component import getNextSiteManager
# self.assertRaises(ComponentLookupError, getNextSiteManager, self.root)
# self.assertEqual(getNextSiteManager(self.f1), getGlobalSiteManager())
# self.assertEqual(getNextSiteManager(self.f2), self.sm1)
# self.assertEqual(getNextSiteManager(self.sm1), getGlobalSiteManager())
# self.assertEqual(getNextSiteManager(self.sm2), self.sm1)
# self.assertRaises(ComponentLookupError, getNextSiteManager, self.p1)
# self.assertRaises(ComponentLookupError, getNextSiteManager, self.p2)
#
# self.assertRaises(ComponentLookupError,
# getNextSiteManager, self.unparented_folder)
# self.assertRaises(ComponentLookupError,
# getNextSiteManager, self.unrooted_subfolder)
# XXX Maybe we need to test this with RestrictedPython in the context
# of Zope2? Maybe we just don't care.
#
# def test_getNextSiteManager_security(self):
# from zope.app.component import getNextSiteManager
# from zope.security.checker import ProxyFactory, NamesChecker
# sm = ProxyFactory(self.sm1, NamesChecker(('next',)))
# # Check that getGlobalSiteManager() is not proxied
# self.assert_(getNextSiteManager(sm) is getGlobalSiteManager())
def test_setThreadSite_clearThreadSite(self):
from zope.app.component.site import threadSiteSubscriber, clearSite
from zope.app.publication.zopepublication import BeforeTraverseEvent
self.assertEqual(getSite(), None)
# A site is traversed
sm = SiteManagerStub()
site = Folder()
site.setSiteManager(sm)
ev = BeforeTraverseEvent(site, object())
threadSiteSubscriber(site, ev)
self.assertEqual(getSite(), site)
clearSite()
self.assertEqual(getSite(), None)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(SiteManagerTest))
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 Five site manager
$Id$
"""
import os, sys
if __name__ == '__main__':
execfile(os.path.join(sys.path[0], 'framework.py'))
def test_suite():
from Testing.ZopeTestCase import ZopeDocFileSuite
return ZopeDocFileSuite('sitemanager.txt', package="Products.Five.site.tests")
if __name__ == '__main__':
framework()
This diff is collapsed.
##############################################################################
#
# 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.
#
##############################################################################
"""Local utility registration
$Id$
"""
from zope.interface import implements
from zope.component import getGlobalSiteManager
from zope.component.interfaces import ComponentLookupError
from zope.app.component import getNextSiteManager
from Acquisition import aq_base
from OFS.Folder import Folder
from Products.Five.site.interfaces import IFiveUtilityRegistry
class SimpleLocalUtilityRegistry(object):
implements(IFiveUtilityRegistry)
def __init__(self, context):
self.context = context
# make {get|query}NextSiteManager() work without having to
# resort to Zope 2 acquisition
self.__parent__ = self.context.getSiteManager()
@property
def next(self):
try:
return getNextSiteManager(self)
except ComponentLookupError:
return getGlobalSiteManager()
def getUtility(self, interface, name=''):
"""See IFiveUtilityRegistry interface
"""
c = self.queryUtility(interface, name)
if c is not None:
return c
raise ComponentLookupError(interface, name)
def queryUtility(self, interface, name='', default=None):
"""See IFiveUtilityRegistry interface
"""
if getattr(aq_base(self.context), 'utilities', None) is not None:
for id, utility in self.context.utilities.objectItems():
if interface.providedBy(utility):
if id.find('-') != -1:
prefix, utility_name = id.split('-', 1)
else:
utility_name = ''
if name == utility_name:
return utility
return self.next.queryUtility(interface, name, default)
def getUtilitiesFor(self, interface):
names = []
if getattr(aq_base(self.context), 'utilities', None) is not None:
for id, utility in self.context.utilities.objectItems():
if interface.providedBy(utility):
if id.find('-') != -1:
prefix, name = id.split('-', 1)
else:
name = ''
names.append(name)
yield (name, utility)
for name, utility in self.next.getUtilitiesFor(interface):
if name not in names:
yield name, utility
def getAllUtilitiesRegisteredFor(self, interface):
# This also supposedly returns "overridden" utilities, but we don't
# keep them around. It also does not return the name-value pair that
# getUtilitiesFor returns.
if getattr(aq_base(self.context), 'utilities', None) is not None:
for utility in self.context.utilities.objectValues():
if interface.providedBy(utility):
yield utility
for utility in self.next.getAllUtilitiesRegisteredFor(interface):
yield utility
def registerUtility(self, interface, utility, name=''):
# I think you are *really* supposed to:
# 1. Check if there is a "registrations" object for utilities.
# 2. If not create one.
# 3. Get it.
# 4. Create a registration object for the utility.
# 5. Rgister the registration object in the registrations.
# But that is quite complex, and Jim sais he wants to change that
# anyway, and in any case the way you would normally do this in Zope3
# and Five would probably differ anyway, so, here is this new
# Five-only, easy to use method!
if getattr(aq_base(self.context), 'utilities', None) is None:
self.context._setObject('utilities', Folder('utilities'))
utilities = self.context.utilities
if name == '':
# Singletons. Only one per interface allowed, so, let's call it
# by the interface.
id = interface.getName()
else:
id = interface.getName() + '-' + name
if id in utilities.objectIds():
raise ValueError("There is already a utility registered for "
"%s with the name '%s'" % (interface.getName(),
name))
utilities._setObject(id, utility)
......@@ -73,50 +73,6 @@ def test_directives():
>>> cleanUp()
"""
def test_content_deprecation():
"""
Test deprecated content directive
There was a bug in the content directive deprecation code
which caused all code that use this directive break.
So we test this to make sure it works. If the content
directive will have been removed, this test can be removed
entirely as well.
First, we load the configuration file:
>>> import Products.Five.tests
>>> from Products.Five import zcml
>>> zcml.load_config('meta.zcml', Products.Five)
>>> zcml.load_config('directives.zcml', Products.Five.tests)
Use the <content> directives: this gives a deprecation
warning but should otherwise be all right. (We embed the block
to suppress the deprecation warning...)
>>> import warnings
>>> warnings.showwarning, _savewarning = lambda *args, **kw: None, warnings.showwarning
>>> zcml.load_string('''
... <configure xmlns="http://namespaces.zope.org/zope">
... <content class="Products.Five.tests.classes.One">
... <implements interface="Products.Five.tests.classes.IOne" />
... </content>
... </configure>
... ''')
Check that they are all right.
>>> from Products.Five.tests.classes import One, Two, IOne, ITwo
>>> IOne.implementedBy(One)
True
Clean up adapter registry and others:
>>> warnings.showwarning = _savewarning
>>> from zope.testing.cleanup import cleanUp
>>> cleanUp()
"""
def test_suite():
from Testing.ZopeTestCase import ZopeDocTestSuite
......
......@@ -91,8 +91,6 @@ def test_suite():
from Testing.ZopeTestCase import FunctionalDocFileSuite
return unittest.TestSuite((
DocTestSuite(),
FunctionalDocFileSuite('viewable.txt',
package="Products.Five.tests",),
))
if __name__ == '__main__':
......
Testing defaultViewable
=======================
>>> import Products.Five
>>> from Products.Five import zcml
>>> zcml.load_config('configure.zcml', package=Products.Five)
PROPFIND without defaultViewable
--------------------------------
>>> print http(r"""
... PROPFIND /test_folder_1_ HTTP/1.1
... Authorization: Basic test_user_1_:secret
... Content-Length: 250
... Content-Type: application/xml
... Depth: 1
...
... <?xml version="1.0" encoding="utf-8"?>
... <propfind xmlns="DAV:"><prop>
... <getlastmodified xmlns="DAV:"/>
... <creationdate xmlns="DAV:"/>
... <resourcetype xmlns="DAV:"/>
... <getcontenttype xmlns="DAV:"/>
... <getcontentlength xmlns="DAV:"/>
... </prop></propfind>
... """, handle_errors=False)
HTTP/1.1 207 Multi-Status
Connection: close
Content-Length: ...
Content-Location: http://localhost/test_folder_1_/
Content-Type: text/xml; charset="utf-8"
Date: ...
<BLANKLINE>
<?xml version="1.0" encoding="utf-8"?>
<d:multistatus xmlns:d="DAV:">
<d:response>
<d:href>/test_folder_1_/</d:href>
<d:propstat>
<d:prop>
<n:getlastmodified xmlns:n="DAV:">...
<n:creationdate xmlns:n="DAV:">...
<n:resourcetype xmlns:n="DAV:"><n:collection/></n:resourcetype>
<n:getcontenttype xmlns:n="DAV:"></n:getcontenttype>
<n:getcontentlength xmlns:n="DAV:"></n:getcontentlength>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
<d:response>
<d:href>/test_folder_1_/acl_users</d:href>
<d:propstat>
<d:prop>
<n:getlastmodified xmlns:n="DAV:">...
<n:creationdate xmlns:n="DAV:">...
<n:resourcetype xmlns:n="DAV:"></n:resourcetype>
<n:getcontenttype xmlns:n="DAV:"></n:getcontenttype>
<n:getcontentlength xmlns:n="DAV:"></n:getcontentlength>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
PROPFIND with defaultViewable
-----------------------------
Now make the class default viewable:
>>> #from Products.Five.fiveconfigure import classDefaultViewable
>>> #from OFS.Folder import Folder
>>> #classDefaultViewable(Folder)
And try it again:
>>> print http(r"""
... PROPFIND /test_folder_1_ HTTP/1.1
... Authorization: Basic test_user_1_:secret
... Content-Length: 250
... Content-Type: application/xml
... Depth: 1
...
... <?xml version="1.0" encoding="utf-8"?>
... <propfind xmlns="DAV:"><prop>
... <getlastmodified xmlns="DAV:"/>
... <creationdate xmlns="DAV:"/>
... <resourcetype xmlns="DAV:"/>
... <getcontenttype xmlns="DAV:"/>
... <getcontentlength xmlns="DAV:"/>
... </prop></propfind>
... """, handle_errors=False)
HTTP/1.1 207 Multi-Status
Connection: close
Content-Length: ...
Content-Location: http://localhost/test_folder_1_/
Content-Type: text/xml; charset="utf-8"
Date: ...
<BLANKLINE>
<?xml version="1.0" encoding="utf-8"?>
<d:multistatus xmlns:d="DAV:">
<d:response>
<d:href>/test_folder_1_/</d:href>
<d:propstat>
<d:prop>
<n:getlastmodified xmlns:n="DAV:">...
<n:creationdate xmlns:n="DAV:">...
<n:resourcetype xmlns:n="DAV:"><n:collection/></n:resourcetype>
<n:getcontenttype xmlns:n="DAV:"></n:getcontenttype>
<n:getcontentlength xmlns:n="DAV:"></n:getcontentlength>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
<d:response>
<d:href>/test_folder_1_/acl_users</d:href>
<d:propstat>
<d:prop>
<n:getlastmodified xmlns:n="DAV:">...
<n:creationdate xmlns:n="DAV:">...
<n:resourcetype xmlns:n="DAV:"></n:resourcetype>
<n:getcontenttype xmlns:n="DAV:"></n:getcontenttype>
<n:getcontentlength xmlns:n="DAV:"></n:getcontentlength>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
</d:multistatus>
Clean up
--------
>>> from zope.app.testing.placelesssetup import tearDown
>>> tearDown()
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