[CMF-checkins] SVN: CMF/branches/tseaver-catalog_events/ Snapshot
work to get cataloging done via events.
Tres Seaver
tseaver at palladion.com
Wed Feb 15 23:44:33 EST 2006
Log message for revision 41631:
Snapshot work to get cataloging done via events.
Changed:
A CMF/branches/tseaver-catalog_events/
D CMF/branches/tseaver-catalog_events/CHANGES.txt
A CMF/branches/tseaver-catalog_events/CHANGES.txt
U CMF/branches/tseaver-catalog_events/CMFCore/CMFCatalogAware.py
D CMF/branches/tseaver-catalog_events/CMFCore/CookieCrumbler.py
A CMF/branches/tseaver-catalog_events/CMFCore/CookieCrumbler.py
D CMF/branches/tseaver-catalog_events/CMFCore/configure.zcml
A CMF/branches/tseaver-catalog_events/CMFCore/configure.zcml
U CMF/branches/tseaver-catalog_events/CMFCore/tests/base/dummy.py
U CMF/branches/tseaver-catalog_events/CMFCore/tests/base/testcase.py
U CMF/branches/tseaver-catalog_events/CMFCore/tests/test_CMFCatalogAware.py
A CMF/branches/tseaver-catalog_events/CMFCore/tests/test_CookieCrumbler.py
U CMF/branches/tseaver-catalog_events/CMFCore/tests/test_OpaqueItems.py
U CMF/branches/tseaver-catalog_events/CMFCore/tests/test_PortalContent.py
U CMF/branches/tseaver-catalog_events/CMFCore/tests/test_PortalFolder.py
U CMF/branches/tseaver-catalog_events/CMFDefault/tests/test_Discussions.py
-=-
Copied: CMF/branches/tseaver-catalog_events (from rev 41511, CMF/trunk)
Deleted: CMF/branches/tseaver-catalog_events/CHANGES.txt
===================================================================
--- CMF/trunk/CHANGES.txt 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CHANGES.txt 2006-02-16 04:44:31 UTC (rev 41631)
@@ -1,297 +0,0 @@
-CMF 2.0.0-alpha (2006/01/22)
-
- New Features
-
- - CMFCore.FSPythonScript: Customized versions now track the "original"
- source from which they were customized, and can present a diff between
- that version and their current source text.
-
- - CMFDefault and CMFCalendar: Added locales directories with .pot files.
- A modified i18nextract.py script from Zope 3 is used to extract
- translatable strings from .py, .pt, .html and .xml files.
-
- - FSFile: autodetect the encoding of UTF-8 text files with a
- suitable Byte Order Mark (0xEF 0xBB 0xBF).
-
- - CMFDefault.MetadataTool: support arbitrary additional schemas.
- The "stock" DublinCore-specific API is still accessible, implemented
- via a special "DCMI" subobject.
-
- - WorkflowTool and DCWorkflow: Improved add form for workflow objects.
- Presettings can now be loaded from workflow settings in setup profiles.
- This replaces the feature that did allow to load presettings from the
- oldstyle workflow factories registry.
-
- - WorkflowTool: Switched to generic plug-in mechanism for workflows.
- Any class registered for IWorkflowDefinition can now be used in the
- WorkflowTool.
-
- - DCWorkflow: Added 'revision2' profile.
- This replaces the hardcoded 'Revision 2' default workflow.
-
- - CMFActionIcons, CMFCalendar, CMFDefault, CMFTopic, CMFUid:
- use the new 'for_' argument in GenericSetup's profile registry API
- to indicate that profiles are intended for CMFCore's ISiteRoot sites.
-
- - CMFTopic: added specialized GenericSetup support for topics, to
- allow capturing criteria in a single XML file.
-
- - CMFDefault and CMFTopic: Split off CMFTopic profile.
- CMFTopic support is now configured by an optional extension profile.
- CMFDefault no longer depends on CMFTopic.
-
- - TypesTool: Improved add form for type info objects.
- Presettings can now be loaded from type info settings in setup profiles.
- This replaces the feature that did allow to load presettings from
- registered (oldstyle) fti data.
-
- - CMFCore.CachingPolicyManager: Caching policies can now control all the
- Cache-Control tokens defined in the HTTP 1.1 spec (s-maxage, public,
- private, no-transform). When no-cache is enabled, a Pragma: no-cache
- header is also sent for HTTP 1.0 clients. Thanks go to Geoff Davis
- for contributing the necessary patches.
-
- - ActionsTool: Improved add form for 'CMF Action' objects.
- Presettings can now be loaded from Action settings in setup profiles.
-
- - CMFCore and GenericSetup: Added catalog tool setup handlers.
- This includes node adapters for PluginIndexes, ZCTextIndex and ZCatalog.
- Support for additional indexes can be added by providing INode adapters.
- All indexes are cleared by this handler, so please make sure to
- re-catalog existing content if necessary.
-
- - GenericSetup.utils: Added new sub-framework for XML im- and export.
- Instead of using ConfiguratorBase configurators should now implement
- IBody or INode. These adapters should subclass from XMLAdapterBase or
- NodeAdapterBase and mix in ObjectManagerHelpers and / or
- PropertyManagerHelpers if needed.
-
- - CMFCore.exportimport: Added framework and interfaces for exporting
- and importing content using the export / import contexts provided by
- GenericSetup.
-
- - CMFSetup: Split off GenericSetup.
- GenericSetup allows to use CMFSetup functionality outside CMF. See the
- README.txt of GenericSetup for details.
-
- - Interfaces: Converted all interfaces to zope 3 style interfaces.
- Most interfaces are bridged back to zope 2 style interfaces to provide
- backwards compatibility.
-
- - The features of CMFonFive have been folder into CMFCore and
- CMFDefault:
-
- * Zope 3 menus (browser:menu) are bridged to CMF actions using the
- portal_fiveactions tool. Any menuItem registered will be
- accessible though the portal_actions tool, where the menu for
- which the item was registered will be used as the action
- category.
-
- * The Zope 3 'cmf' skin layer provides integration between the
- Zope3 standard macros and the CMF main template, by redefining
- five_template.pt.
-
- - TypeInformation and newstyle Actions: Added i18n support.
- If 'i18n_domain' is specified, 'title' and 'description' are returned as
- MassageID objects instead of strings. This allows to use different i18n
- domains for different TypeInfos and Actions.
-
- - Replaced user messages by Message objects to improve the i18n support.
-
- - CMFDefault GenericSetup profile: Added CMF BTree Folder to the list of
- automatically instantiated types in the types tool.
- (http://www.zope.org/Collectors/CMF/371)
-
- - CMFDefault skins: Added members_delete_form.
- This adds a confirmation step to avoid accidental deletion of members.
-
- - DirectoryView and derived classes can now have metadata associated with
- them just like regular FSObject-derived objects can.
-
- - DirectoryView and derived classes: It is now possible to customize what
- gets created to represent directories inside the directory view.
- Previously, the code had a fixed assumption that all directories on the
- file system must turn into instances of
- CMFCore.DirectoryView.DirectoryView(Surrogate). It is now possible to
- register a class deriving from DirectoryView and have that be
- instantiated instead.
-
- - ActionsTool: Added new way to define Actions.
- 'CMF Action Category' objects can now be added to the portal_actions
- tool and 'CMF Action' objects to categories or subcategories. To
- migrate oldstyle Actions (ActionInformation objects) you can create a
- snapshot and re-import Actions using the portal_setup tool.
-
- - TypesTool: TypeInformation classes are now pluggable.
- Any class registered for ITypeInformation can now be used in the
- TypesTool.
-
- Bug Fixes
-
- - CMFDefault skins: Refactored and improved discussion_reply_form.
-
- - CMFDefault utils: Fixed html_marshal function.
- The return values are no longer escaped to avoid double quoting and no
- longer stringified. The page templates take care of these steps.
-
- - PortalFolder: Synced _verifyObjectPaste code with OFS.CopySupport.
- The behavior is almost the same as before, but the code is less tolerant
- if content types are not registered properly.
-
- - ActionProviderBase: getActionObject did stumble over newstyle Actions.
-
- - CMFCore.exportimport.content: Ensure that BODYFILE in our "faux"
- request is a file-like object, FBO objects which expect to call its
- 'read' method.
-
- - Got rid of the "CMF Site" and "Configured CMF Site" duality in the ZMI
- add list by removing the "CMF Site" class registration in CMFDefault
- and moving the "Configured CMF Site" registration from CMFSetup into
- CMFDefault, renaming it to "CMF Site".
- (http://www.zope.org/Collectors/CMF/364)
-
- - Updated RELEASE.txt and the slurp_release script to now use Subversion
- instead of CVS, and to reflect the new tag/branch naming conventions
- used in the CMF repository.
-
- - Added testing framework to suppress / examine output from warnings
- module and from zLOG.
-
- - CMFUid/UniqueIdGeneratorTool.py: Replaced the old BTree.Length.Length
- implementation by a simple counter. Using a BTree.Length.Length object
- as counter may have caused setting the same unique id to multiple
- objects under high load. The tools counter gets automigrated on the
- first access. This is a forward port from CMF-1_5-branch before the
- CMF 1.5.2 release.
-
- - CMFCore.utils.ToolInit: For icon registration to work with ToolInit
- you would have to have the same product_name parameter as the actual
- product name of the product. Now we just pick up that product name from
- the product context instead, and ignore the product_name parameter (with
- a deprecation warning).
-
- - CMFSetup: Merged the registerClass and registerIcon call since
- registerClass is capable of registering icons.
-
- - DublinCore and PortalFolder: Changed fallback in 'Type' method.
- The fallback is only necessary if the related type info is missing.
-
- - CMFCore.PortalContent: Wrong variable name in __call__ would blow up
- if no default view could be found for a piece of content.
-
- Others
-
- - Moved GenericSetup out of the CMF package, it is now a standalone
- product, but still distributed as part of the CMF package.
-
- - Replaced use of deprecated 'zLOG' module with standard Python
- 'logging' module.
-
- - CMFCore utils: Made _checkPermission depend on Zope's checkPermission.
- There is no longer a need to modify the checkPermission behavior in CMF.
-
- - TypeInformation: Removed support for old setting formats.
- If TypeInformation objects are initialized with keyword arguments,
- 'actions' and 'aliases' keys have to use the format introduced in
- CMF 1.5.
-
- - CMFSetup and GenericSetup: Removed obsolete CMFSetup product.
- Added __module_aliases__ to support setup tools created with CMFSetup.
-
- - DCWorkflow: Removed hardcoded default workflows.
-
- - Workflow: Removed deprecated WorkflowInformation and getActionsFor.
-
- - CMFCore and GenericSetup: Moved mechanisms for content export / import
- to GenericSetup/content.py, and made more generic.
-
- - CMFDefault: Removed PortalGenerator and manage_addCMFSite.
-
- - Portal Types: Removed factory_type_information data.
- TypesTool.listDefaultTypeInformation was removed, the 'fti' argument of
- utils.ContentInit and the 'typeinfo_name' argument of
- TypesTool.manage_addTypeInformation are ignored.
-
- - CatalogTool: A new portal_catalog is now empty.
- Removed enumerateIndexes, enumerateLexicons, enumerateColumns and
- _initIndexes. Please use the setup tool to populate the catalog.
-
- - CMFActionIcons, CMFCalendar and CMFTopic: Removed old install scripts.
-
- - Refactored and extended CMFDefault.tests.test_join so it can be easily
- subclassed and reused for alternative membership implementations. All
- that is needed is to ovverride _createPortal to return a portal with
- the desired non-default membership-related tools installed.
-
- - Remove all "old-style" actions from tools that still carried them
- (CMFDefault.MembershipTool, CMFDefault.PropertiesTool,
- CMFDefault.RegistrationTool, CMFDefault.SyndicationTool,
- CMFDefault.DiscussionTool, CMFCore.UndoTool). These have been
- superceded by "new-style" action information objects stored inside
- the Actions Tool.
-
- - Some simplifications to the slurp_release release helper script and
- updates to the RELEASE.txt release instructions.
-
- - The CMF now depends on Zope 2.9.0 including Five.
-
- - Non-release packages moved out of the /CMF/ repository package:
-
- o hotfixes moved to /CMF_Hotfixes
-
- o others (CMFCollector, CMFStaging, CMFTracker, CMFWorkspaces)
- moved to /CMF_Extras/
-
- - To document how to create a CMF release from CMF a description is now
- included in RELEASE.txt at the root of the CMF package. This text can
- guide release managers or volunteers with the appropriate release
- privileges.
-
- - Added DeprecationWarning to the all_cmf_tests.py script. The canonical
- way to run the tests is using "zopectl test".
- (http://www.zope.org/Collectors/CMF/272)
-
- - CMFDefault: Oldstyle DefaultWorkflowDefinition is no longer registered.
- So you can't accidentally add this deprecated workflow.
-
- - The "Access future portal content" was not used anywhere and has been
- removed. (http://www.zope.org/Collectors/CMF/240)
-
- - CMFDefault: Marked 'manage_addCMFSite' as deprecated.
- To add a new CMF site, select 'Configured CMF Site'. The 'CMFDefault
- Site' profile creates a new site like those you know from 'CMF Site'.
-
- - CMFDefault: Cleaned out some super-ancient code for migrating
- or fixing PTK content so it would work with CMF.
-
- - CatalogTool: Removed deprecated indexes and metadata.
- 'Creator' was replaced by 'listCreators', 'portal_type' should be used
- instead of 'meta_type'.
-
- - CMFCore: Removed some deprecated aliases and constants.
-
- - Permissions: Removed deprecated oldstyle permission modules.
-
- - TypeInformation: Removed deprecated Type method.
-
- - PortalFolder: Removed deprecated _morphSpec() and spec arguments.
-
- - Portal Types: Removed deprecated _getViewFor / getActionById machinery.
- PortalContent objects might be used as methods, so __call__() still
- returns their default view.
-
- - CMFDefault and CMFTopic skins: Removed deprecated DTML skins and layers.
-
- - ActionsTool: Removed deprecated support for oldstyle Action Providers.
- If Action Providers don't implement the ActionProvider interface they
- are now ignored.
-
- - Workflow: Removed deprecated WorkflowMethod machinery.
-
- - Made unit tests close the request properly.
-
- - Reordered base classes of File and Image, to allow use of super().
-
-CMF 1.5.x and previous
-
- For a complete list of changes see HISTORY.txt.
Copied: CMF/branches/tseaver-catalog_events/CHANGES.txt (from rev 41514, CMF/trunk/CHANGES.txt)
Modified: CMF/branches/tseaver-catalog_events/CMFCore/CMFCatalogAware.py
===================================================================
--- CMF/trunk/CMFCore/CMFCatalogAware.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/CMFCatalogAware.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -22,7 +22,15 @@
from ExtensionClass import Base
from Globals import DTMLFile
from Globals import InitializeClass
+from OFS.interfaces import IObjectWillBeMovedEvent
+from OFS.interfaces import IObjectClonedEvent
+from zope.interface import implements
+from zope.component import adapts
+from zope.app.container.interfaces import IObjectAddedEvent
+from zope.app.container.interfaces import IObjectMovedEvent
+from zope.app.location.interfaces import ISublocations
+
from permissions import AccessContentsInformation
from permissions import ManagePortal
from permissions import ModifyPortalContent
@@ -31,8 +39,8 @@
from utils import getToolByName
from interfaces import ICallableOpaqueItem
-from interfaces.IOpaqueItems \
- import ICallableOpaqueItem as z2ICallableOpaqueItem
+from interfaces import ICallableOpaqueItemEvents
+from interfaces import IContentish
class CMFCatalogAware(Base):
"""Mix-in for notifying portal_catalog and portal_workflow
@@ -165,8 +173,7 @@
self_base = aq_base(self)
for name in self_base.__dict__.keys():
obj = getattr(self_base, name)
- if ICallableOpaqueItem.providedBy(obj) \
- or z2ICallableOpaqueItem.isImplementedBy(obj):
+ if ICallableOpaqueItem.providedBy(obj):
items.append((obj.getId(), obj))
return tuple(items)
@@ -190,22 +197,7 @@
# Hooks
# -----
- def manage_afterAdd(self, item, container):
- """
- Add self to the catalog.
- (Called when the object is created or moved.)
- """
- self.indexObject()
- self.__recurse('manage_afterAdd', item, container)
-
- def manage_afterClone(self, item):
- """
- Add self to the workflow.
- (Called when the object is cloned.)
- """
- self.notifyWorkflowCreated()
- self.__recurse('manage_afterClone', item)
-
+ def _clearLocalRolesAfterClone(self):
# Make sure owner local role is set after pasting
# The standard Zope mechanisms take care of executable ownership
current_user = _getAuthenticatedUser(self)
@@ -214,27 +206,23 @@
self.manage_delLocalRoles(local_role_holders)
self.manage_setLocalRoles(current_user.getId(), ['Owner'])
- def manage_beforeDelete(self, item, container):
+ def _recurseOpaques(self, name, container=None):
"""
- Remove self from the catalog.
- (Called when the object is deleted or moved.)
+ Recurse in both opaque subobjects.
"""
- self.__recurse('manage_beforeDelete', item, container)
- self.unindexObject()
+ for opaque in self.opaqueValues():
+ s = getattr(opaque, '_p_changed', 0)
+ try:
+ if container is None:
+ getattr(opaque, name)(opaque)
+ else:
+ getattr(opaque, name)(opaque, container)
+ except AttributeError:
+ pass
+ else:
+ if s is None:
+ opaque._p_deactivate()
- def __recurse(self, name, *args):
- """
- Recurse in both normal and opaque subobjects.
- """
- values = self.objectValues()
- opaque_values = self.opaqueValues()
- for subobjects in values, opaque_values:
- for ob in subobjects:
- s = getattr(ob, '_p_changed', 0)
- if hasattr(aq_base(ob), name):
- getattr(ob, name)(*args)
- if s is None: ob._p_deactivate()
-
# ZMI
# ---
@@ -279,3 +267,59 @@
manage_tabs_message=manage_tabs_message)
InitializeClass(CMFCatalogAware)
+
+class ContentishSublocations(object):
+ """ Get the sublocations for content, including "opaque" subitems.
+ """
+ adapts(IContentish)
+ implements(ISublocations)
+
+ def __init__(self, context):
+ self.context = context
+
+ def sublocations(self):
+
+ for ob in self.context.objectValues():
+ yield ob
+
+ for opaque in self.context.opaqueValues():
+ s = getattr(opaque, '_p_changed', 0)
+ yield opaque
+ if s is None:
+ opaque._p_deactivate()
+
+def handleObjectEvent(ob, event):
+ """ Event subscriber for (IContentish, IObjectEvent) events.
+
+ o XXX: the nasty '_recurseOpaques' propagates notification to opaque
+ items, which are ignored by the stock ObjectManager propagation.
+ """
+ if not IContentish.providedBy(ob):
+ return
+
+ if IObjectAddedEvent.providedBy(event):
+ if event.newParent is not None:
+ ob.indexObject()
+ if ICallableOpaqueItemEvents.providedBy(ob):
+ ob.manage_afterAdd(ob, event.newParent)
+ #ob._recurseOpaques('manage_afterAdd', ob)
+
+ elif IObjectClonedEvent.providedBy(event):
+ ob.notifyWorkflowCreated()
+ ob._clearLocalRolesAfterClone()
+ if ICallableOpaqueItemEvents.providedBy(ob):
+ ob.manage_afterClone(ob)
+ #ob._recurseOpaques('manage_afterClone')
+
+ elif IObjectMovedEvent.providedBy(event):
+ if event.newParent is not None:
+ ob.reindexObject()
+ #ob._recurseOpaques('manage_afterAdd', ob)
+
+ elif IObjectWillBeMovedEvent.providedBy(event):
+ if event.oldParent is not None:
+ ob.unindexObject()
+ if ICallableOpaqueItemEvents.providedBy(ob):
+ ob.manage_beforeDelete(ob, event.oldParent)
+ #ob._recurseOpaques('manage_beforeDelete', ob)
+
Deleted: CMF/branches/tseaver-catalog_events/CMFCore/CookieCrumbler.py
===================================================================
--- CMF/trunk/CMFCore/CookieCrumbler.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/CookieCrumbler.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -1,441 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001 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.
-#
-##############################################################################
-""" Cookie Crumbler: Enable cookies for non-cookie user folders.
-
-$Id$
-"""
-
-from base64 import encodestring, decodestring
-from urllib import quote, unquote
-
-from Acquisition import aq_inner, aq_parent
-from DateTime import DateTime
-from AccessControl import ClassSecurityInfo, Permissions
-from ZPublisher import BeforeTraverse
-import Globals
-from Globals import HTMLFile
-from ZPublisher.HTTPRequest import HTTPRequest
-from OFS.Folder import Folder
-from zExceptions import Redirect
-from zope.interface import implements
-
-from interfaces import ICookieCrumbler
-
-
-# Constants.
-ATTEMPT_NONE = 0 # No attempt at authentication
-ATTEMPT_LOGIN = 1 # Attempt to log in
-ATTEMPT_RESUME = 2 # Attempt to resume session
-
-ModifyCookieCrumblers = 'Modify Cookie Crumblers'
-ViewManagementScreens = Permissions.view_management_screens
-
-
-class CookieCrumblerDisabled(Exception):
-
- """Cookie crumbler should not be used for a certain request.
- """
-
-
-class CookieCrumbler(Folder):
-
- """Reads cookies during traversal and simulates the HTTP auth headers.
- """
-
- implements(ICookieCrumbler)
-
- meta_type = 'Cookie Crumbler'
-
- security = ClassSecurityInfo()
- security.declareProtected(ModifyCookieCrumblers, 'manage_editProperties')
- security.declareProtected(ModifyCookieCrumblers, 'manage_changeProperties')
- security.declareProtected(ViewManagementScreens, 'manage_propertiesForm')
-
- # By default, anonymous users can view login/logout pages.
- _View_Permission = ('Anonymous',)
-
-
- _properties = ({'id':'auth_cookie', 'type': 'string', 'mode':'w',
- 'label':'Authentication cookie name'},
- {'id':'name_cookie', 'type': 'string', 'mode':'w',
- 'label':'User name form variable'},
- {'id':'pw_cookie', 'type': 'string', 'mode':'w',
- 'label':'User password form variable'},
- {'id':'persist_cookie', 'type': 'string', 'mode':'w',
- 'label':'User name persistence form variable'},
- {'id':'auto_login_page', 'type': 'string', 'mode':'w',
- 'label':'Login page ID'},
- {'id':'logout_page', 'type': 'string', 'mode':'w',
- 'label':'Logout page ID'},
- {'id':'unauth_page', 'type': 'string', 'mode':'w',
- 'label':'Failed authorization page ID'},
- {'id':'local_cookie_path', 'type': 'boolean', 'mode':'w',
- 'label':'Use cookie paths to limit scope'},
- {'id':'cache_header_value', 'type': 'string', 'mode':'w',
- 'label':'Cache-Control header value'},
- {'id':'log_username', 'type':'boolean', 'mode': 'w',
- 'label':'Log cookie auth username to access log'}
- )
-
- auth_cookie = '__ac'
- name_cookie = '__ac_name'
- pw_cookie = '__ac_password'
- persist_cookie = '__ac_persistent'
- auto_login_page = 'login_form'
- unauth_page = ''
- logout_page = 'logged_out'
- local_cookie_path = False
- cache_header_value = 'private'
- log_username = True
-
- security.declarePrivate('delRequestVar')
- def delRequestVar(self, req, name):
- # No errors of any sort may propagate, and we don't care *what*
- # they are, even to log them.
- try: del req.other[name]
- except: pass
- try: del req.form[name]
- except: pass
- try: del req.cookies[name]
- except: pass
- try: del req.environ[name]
- except: pass
-
- security.declarePublic('getCookiePath')
- def getCookiePath(self):
- if not self.local_cookie_path:
- return '/'
- parent = aq_parent(aq_inner(self))
- if parent is not None:
- return '/' + parent.absolute_url(1)
- else:
- return '/'
-
- # Allow overridable cookie set/expiration methods.
- security.declarePrivate('getCookieMethod')
- def getCookieMethod(self, name, default=None):
- return getattr(self, name, default)
-
- security.declarePrivate('defaultSetAuthCookie')
- def defaultSetAuthCookie(self, resp, cookie_name, cookie_value):
- kw = {}
- req = getattr(self, 'REQUEST', None)
- if req is not None and req.get('SERVER_URL', '').startswith('https:'):
- # Ask the client to send back the cookie only in SSL mode
- kw['secure'] = 'y'
- resp.setCookie(cookie_name, cookie_value,
- path=self.getCookiePath(), **kw)
-
- security.declarePrivate('defaultExpireAuthCookie')
- def defaultExpireAuthCookie(self, resp, cookie_name):
- resp.expireCookie(cookie_name, path=self.getCookiePath())
-
- def _setAuthHeader(self, ac, request, response):
- """Set the auth headers for both the Zope and Medusa http request
- objects.
- """
- request._auth = 'Basic %s' % ac
- response._auth = 1
- if self.log_username:
- # Set the authorization header in the medusa http request
- # so that the username can be logged to the Z2.log
- try:
- # Put the full-arm latex glove on now...
- medusa_headers = response.stdout._request._header_cache
- except AttributeError:
- pass
- else:
- medusa_headers['authorization'] = request._auth
-
- security.declarePrivate('modifyRequest')
- def modifyRequest(self, req, resp):
- """Copies cookie-supplied credentials to the basic auth fields.
-
- Returns a flag indicating what the user is trying to do with
- cookies: ATTEMPT_NONE, ATTEMPT_LOGIN, or ATTEMPT_RESUME. If
- cookie login is disabled for this request, raises
- CookieCrumblerDisabled.
- """
- if (req.__class__ is not HTTPRequest
- or not req['REQUEST_METHOD'] in ('HEAD', 'GET', 'PUT', 'POST')
- or req.environ.has_key('WEBDAV_SOURCE_PORT')):
- raise CookieCrumblerDisabled
-
- # attempt may contain information about an earlier attempt to
- # authenticate using a higher-up cookie crumbler within the
- # same request.
- attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)
-
- if attempt == ATTEMPT_NONE:
- if req._auth:
- # An auth header was provided and no cookie crumbler
- # created it. The user must be using basic auth.
- raise CookieCrumblerDisabled
-
- if req.has_key(self.pw_cookie) and req.has_key(self.name_cookie):
- # Attempt to log in and set cookies.
- attempt = ATTEMPT_LOGIN
- name = req[self.name_cookie]
- pw = req[self.pw_cookie]
- ac = encodestring('%s:%s' % (name, pw)).rstrip()
- self._setAuthHeader(ac, req, resp)
- if req.get(self.persist_cookie, 0):
- # Persist the user name (but not the pw or session)
- expires = (DateTime() + 365).toZone('GMT').rfc822()
- resp.setCookie(self.name_cookie, name,
- path=self.getCookiePath(),
- expires=expires)
- else:
- # Expire the user name
- resp.expireCookie(self.name_cookie,
- path=self.getCookiePath())
- method = self.getCookieMethod( 'setAuthCookie'
- , self.defaultSetAuthCookie )
- method( resp, self.auth_cookie, quote( ac ) )
- self.delRequestVar(req, self.name_cookie)
- self.delRequestVar(req, self.pw_cookie)
-
- elif req.has_key(self.auth_cookie):
- # Attempt to resume a session if the cookie is valid.
- # Copy __ac to the auth header.
- ac = unquote(req[self.auth_cookie])
- if ac and ac != 'deleted':
- try:
- decodestring(ac)
- except:
- # Not a valid auth header.
- pass
- else:
- attempt = ATTEMPT_RESUME
- self._setAuthHeader(ac, req, resp)
- self.delRequestVar(req, self.auth_cookie)
- method = self.getCookieMethod(
- 'twiddleAuthCookie', None)
- if method is not None:
- method(resp, self.auth_cookie, quote(ac))
-
- req._cookie_auth = attempt
- return attempt
-
-
- def __call__(self, container, req):
- '''The __before_publishing_traverse__ hook.'''
- resp = self.REQUEST['RESPONSE']
- try:
- attempt = self.modifyRequest(req, resp)
- except CookieCrumblerDisabled:
- return
- if req.get('disable_cookie_login__', 0):
- return
-
- if (self.unauth_page or
- attempt == ATTEMPT_LOGIN or attempt == ATTEMPT_NONE):
- # Modify the "unauthorized" response.
- req._hold(ResponseCleanup(resp))
- resp.unauthorized = self.unauthorized
- resp._unauthorized = self._unauthorized
- if attempt != ATTEMPT_NONE:
- # Trying to log in or resume a session
- if self.cache_header_value:
- # we don't want caches to cache the resulting page
- resp.setHeader('Cache-Control', self.cache_header_value)
- # demystify this in the response.
- resp.setHeader('X-Cache-Control-Hdr-Modified-By',
- 'CookieCrumbler')
- phys_path = self.getPhysicalPath()
- if self.logout_page:
- # Cookies are in use.
- page = getattr(container, self.logout_page, None)
- if page is not None:
- # Provide a logout page.
- req._logout_path = phys_path + ('logout',)
- req._credentials_changed_path = (
- phys_path + ('credentialsChanged',))
-
- security.declarePublic('credentialsChanged')
- def credentialsChanged(self, user, name, pw):
- ac = encodestring('%s:%s' % (name, pw)).rstrip()
- method = self.getCookieMethod( 'setAuthCookie'
- , self.defaultSetAuthCookie )
- resp = self.REQUEST['RESPONSE']
- method( resp, self.auth_cookie, quote( ac ) )
-
- def _cleanupResponse(self):
- resp = self.REQUEST['RESPONSE']
- # No errors of any sort may propagate, and we don't care *what*
- # they are, even to log them.
- try: del resp.unauthorized
- except: pass
- try: del resp._unauthorized
- except: pass
- return resp
-
- security.declarePrivate('unauthorized')
- def unauthorized(self):
- resp = self._cleanupResponse()
- # If we set the auth cookie before, delete it now.
- if resp.cookies.has_key(self.auth_cookie):
- del resp.cookies[self.auth_cookie]
- # Redirect if desired.
- url = self.getUnauthorizedURL()
- if url is not None:
- raise Redirect, url
- # Fall through to the standard unauthorized() call.
- resp.unauthorized()
-
- def _unauthorized(self):
- resp = self._cleanupResponse()
- # If we set the auth cookie before, delete it now.
- if resp.cookies.has_key(self.auth_cookie):
- del resp.cookies[self.auth_cookie]
- # Redirect if desired.
- url = self.getUnauthorizedURL()
- if url is not None:
- resp.redirect(url, lock=1)
- # We don't need to raise an exception.
- return
- # Fall through to the standard _unauthorized() call.
- resp._unauthorized()
-
- security.declarePublic('getUnauthorizedURL')
- def getUnauthorizedURL(self):
- '''
- Redirects to the login page.
- '''
- req = self.REQUEST
- resp = req['RESPONSE']
- attempt = getattr(req, '_cookie_auth', ATTEMPT_NONE)
- if attempt == ATTEMPT_NONE:
- # An anonymous user was denied access to something.
- page_id = self.auto_login_page
- retry = ''
- elif attempt == ATTEMPT_LOGIN:
- # The login attempt failed. Try again.
- page_id = self.auto_login_page
- retry = '1'
- else:
- # An authenticated user was denied access to something.
- page_id = self.unauth_page
- retry = ''
- if page_id:
- page = self.restrictedTraverse(page_id, None)
- if page is not None:
- came_from = req.get('came_from', None)
- if came_from is None:
- came_from = req.get('VIRTUAL_URL', None)
- if came_from is None:
- came_from = '%s%s%s' % ( req['SERVER_URL'].strip(),
- req['SCRIPT_NAME'].strip(),
- req['PATH_INFO'].strip() )
- query = req.get('QUERY_STRING')
- if query:
- # Include the query string in came_from
- if not query.startswith('?'):
- query = '?' + query
- came_from = came_from + query
- url = '%s?came_from=%s&retry=%s&disable_cookie_login__=1' % (
- page.absolute_url(), quote(came_from), retry)
- return url
- return None
-
- # backward compatible alias
- getLoginURL = getUnauthorizedURL
-
- security.declarePublic('logout')
- def logout(self):
- '''
- Logs out the user and redirects to the logout page.
- '''
- req = self.REQUEST
- resp = req['RESPONSE']
- method = self.getCookieMethod( 'expireAuthCookie'
- , self.defaultExpireAuthCookie )
- method( resp, cookie_name=self.auth_cookie )
- if self.logout_page:
- page = self.restrictedTraverse(self.logout_page, None)
- if page is not None:
- resp.redirect('%s?disable_cookie_login__=1'
- % page.absolute_url())
- return ''
- # We should not normally get here.
- return 'Logged out.'
-
- # Installation and removal of traversal hooks.
-
- def manage_beforeDelete(self, item, container):
- if item is self:
- handle = self.meta_type + '/' + self.getId()
- BeforeTraverse.unregisterBeforeTraverse(container, handle)
-
- def manage_afterAdd(self, item, container):
- if item is self:
- handle = self.meta_type + '/' + self.getId()
- container = container.this()
- nc = BeforeTraverse.NameCaller(self.getId())
- BeforeTraverse.registerBeforeTraverse(container, nc, handle)
-
- security.declarePublic('propertyLabel')
- def propertyLabel(self, id):
- """Return a label for the given property id
- """
- for p in self._properties:
- if p['id'] == id:
- return p.get('label', id)
- return id
-
-Globals.InitializeClass(CookieCrumbler)
-
-
-class ResponseCleanup:
- def __init__(self, resp):
- self.resp = resp
-
- def __del__(self):
- # Free the references.
- #
- # No errors of any sort may propagate, and we don't care *what*
- # they are, even to log them.
- try: del self.resp.unauthorized
- except: pass
- try: del self.resp._unauthorized
- except: pass
- try: del self.resp
- except: pass
-
-
-manage_addCCForm = HTMLFile('dtml/addCC', globals())
-manage_addCCForm.__name__ = 'addCC'
-
-def _create_forms(ob):
- ''' Create default forms inside ob '''
- import os
- from OFS.DTMLMethod import addDTMLMethod
- dtmldir = os.path.join(os.path.dirname(__file__), 'dtml')
- for fn in ('index_html', 'logged_in', 'logged_out', 'login_form',
- 'standard_login_footer', 'standard_login_header'):
- filename = os.path.join(dtmldir, fn + '.dtml')
- f = open(filename, 'rt')
- try: data = f.read()
- finally: f.close()
- addDTMLMethod(ob, fn, file=data)
-
-def manage_addCC(dispatcher, id, create_forms=0, REQUEST=None):
- ' '
- ob = CookieCrumbler()
- ob.id = id
- dispatcher._setObject(ob.getId(), ob)
- ob = getattr(dispatcher.this(), ob.getId())
- if create_forms:
- _create_forms(ob)
- if REQUEST is not None:
- return dispatcher.manage_main(dispatcher, REQUEST)
Copied: CMF/branches/tseaver-catalog_events/CMFCore/CookieCrumbler.py (from rev 41522, CMF/trunk/CMFCore/CookieCrumbler.py)
Deleted: CMF/branches/tseaver-catalog_events/CMFCore/configure.zcml
===================================================================
--- CMF/trunk/CMFCore/configure.zcml 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/configure.zcml 2006-02-16 04:44:31 UTC (rev 41631)
@@ -1,42 +0,0 @@
-<configure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:five="http://namespaces.zope.org/five"
- >
-
- <include package=".browser"/>
-
- <include package=".exportimport"/>
-
- <five:registerClass
- class=".ActionInformation.ActionCategory"
- meta_type="CMF Action Category"
- addview="addActionCategory.html"
- permission="cmf.ManagePortal"
- global="False"
- />
-
- <five:registerClass
- class=".ActionInformation.Action"
- meta_type="CMF Action"
- addview="addAction.html"
- permission="cmf.ManagePortal"
- global="False"
- />
-
- <five:registerClass
- class=".TypesTool.FactoryTypeInformation"
- meta_type="Factory-based Type Information"
- addview="addFactoryTypeInformation.html"
- permission="cmf.ManagePortal"
- global="False"
- />
-
- <five:registerClass
- class=".TypesTool.ScriptableTypeInformation"
- meta_type="Scriptable Type Information"
- addview="addScriptableTypeInformation.html"
- permission="cmf.ManagePortal"
- global="False"
- />
-
-</configure>
Copied: CMF/branches/tseaver-catalog_events/CMFCore/configure.zcml (from rev 41514, CMF/trunk/CMFCore/configure.zcml)
Modified: CMF/branches/tseaver-catalog_events/CMFCore/tests/base/dummy.py
===================================================================
--- CMF/trunk/CMFCore/tests/base/dummy.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/tests/base/dummy.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -17,9 +17,13 @@
from Acquisition import Implicit, aq_base, aq_inner, aq_parent
from OFS.SimpleItem import Item
+from OFS.interfaces import IObjectManager
+from zope.interface import implements
+from Products.CMFCore.interfaces import IContentish
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.PortalContent import PortalContent
+
from security import OmnipotentUser
@@ -80,6 +84,7 @@
"""
A Dummy piece of PortalContent
"""
+ implements(IContentish)
meta_type = 'Dummy'
portal_type = 'Dummy Content'
url = 'foo_url'
@@ -166,9 +171,10 @@
class DummyFolder(DummyObject):
+ """ Dummy Container for testing
"""
- Dummy Container for testing
- """
+ implements(IObjectManager)
+
def __init__( self, id='dummy', fake_product=0, prefix='' ):
self._prefix = prefix
self._id = id
@@ -186,17 +192,31 @@
return getattr(self, id)
def _setObject(self, id, object):
+ from zope.event import notify
+ from zope.app.container.contained import ObjectAddedEvent
+ from zope.app.container.contained import notifyContainerModified
+ from OFS.event import ObjectWillBeAddedEvent
+ notify(ObjectWillBeAddedEvent(object, self, id))
self._setOb(id, object)
object = self._getOb(id)
if hasattr(aq_base(object), 'manage_afterAdd'):
object.manage_afterAdd(object, self)
+ notify(ObjectAddedEvent(object, self, id))
+ notifyContainerModified(self)
return object
def _delObject(self, id):
+ from zope.event import notify
+ from zope.app.container.contained import ObjectRemovedEvent
+ from zope.app.container.contained import notifyContainerModified
+ from OFS.event import ObjectWillBeRemovedEvent
object = self._getOb(id)
+ notify(ObjectWillBeRemovedEvent(object, self, id))
if hasattr(aq_base(object), 'manage_beforeDelete'):
object.manage_beforeDelete(object, self)
self._delOb(id)
+ notify(ObjectRemovedEvent(object, self, id))
+ notifyContainerModified(self)
def getPhysicalPath(self):
p = aq_parent(aq_inner(self))
Modified: CMF/branches/tseaver-catalog_events/CMFCore/tests/base/testcase.py
===================================================================
--- CMF/trunk/CMFCore/tests/base/testcase.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/tests/base/testcase.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -168,6 +168,29 @@
_prefix = abspath(join(_prefix,'..'))
+class ContentEventAwareTests(PlacelessSetup):
+ """ Mix-in for test case classes which need to get object events handled.
+ """
+
+ def _registerObjectEventHandler(self):
+ from zope.component import provideAdapter
+ from zope.component import provideHandler
+ from zope.app.container.interfaces import IObjectEvent
+ from zope.app.location.interfaces import ISublocations
+ from Products.CMFCore.interfaces import IContentish
+ from Products.CMFCore.interfaces import IFolderish
+ from Products.CMFCore.CMFCatalogAware import ContentishSublocations
+ from Products.CMFCore.CMFCatalogAware import handleObjectEvent
+ import Products.Five
+ from Products.Five import zcml
+ # First, set up "stock" OFS event propagation
+ zcml.load_config('meta.zcml', Products.Five)
+ zcml.load_config('event.zcml', Products.Five)
+ # Now, register the CMF-specific handler
+ provideHandler(handleObjectEvent, adapts=(IContentish, IObjectEvent))
+ provideAdapter(ContentishSublocations, adapts=(IContentish,),
+ provides=ISublocations)
+
class FSDVTest( TestCase, WarningInterceptor ):
# Base class for FSDV test, creates a fake skin
# copy that can be edited.
Modified: CMF/branches/tseaver-catalog_events/CMFCore/tests/test_CMFCatalogAware.py
===================================================================
--- CMF/trunk/CMFCore/tests/test_CMFCatalogAware.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/tests/test_CMFCatalogAware.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -17,14 +17,20 @@
import unittest
import Testing
+import transaction
from OFS.Folder import Folder
from OFS.SimpleItem import SimpleItem
+from zope.interface import implements
from Products.CMFCore.CMFCatalogAware import CMFCatalogAware
from Products.CMFCore.exceptions import NotFound
+from Products.CMFCore.interfaces import IContentish
+from Products.CMFCore.tests.test_PortalFolder import _AllowedUser
+from Products.CMFCore.tests.test_PortalFolder import _SensitiveSecurityPolicy
from Products.CMFCore.tests.base.testcase import LogInterceptor
-from Products.CMFCore.WorkflowTool import WorkflowTool
+from Products.CMFCore.tests.base.testcase import ContentEventAwareTests
+from Products.CMFCore.tests.base.testcase import SecurityTest
CMF_SECURITY_INDEXES = CMFCatalogAware._cmf_security_indexes
@@ -84,7 +90,14 @@
res.append(self.brain_class(ob, obpath))
return res
+class DummyWorkflowTool(SimpleItem):
+ def __init__(self):
+ self.log = []
+ def notifyCreated(self, obj):
+ self.log.append('created %s' % physicalpath(obj))
+
class TheClass(CMFCatalogAware, Folder):
+ implements(IContentish)
def __init__(self, id):
self._setId(id)
self.notified = False
@@ -92,14 +105,15 @@
self.notified = True
-class CMFCatalogAwareTests(unittest.TestCase, LogInterceptor):
-
+class CMFCatalogAwareTests(unittest.TestCase,
+ LogInterceptor,
+ ):
def setUp(self):
self.root = DummyRoot('')
self.root.site = SimpleFolder('site')
self.site = self.root.site
self.site._setObject('portal_catalog', DummyCatalog())
- self.site._setObject('portal_workflow', WorkflowTool())
+ self.site._setObject('portal_workflow', DummyWorkflowTool())
self.site.foo = TheClass('foo')
def tearDown(self):
@@ -184,12 +198,161 @@
def test_workflow_tool(self):
foo = self.site.foo
self.assertEqual(foo._getWorkflowTool(), self.site.portal_workflow)
-
+
# FIXME: more tests needed
+class CMFCatalogAware_CopySupport_Tests(SecurityTest, ContentEventAwareTests):
+
+ def setUp(self):
+ SecurityTest.setUp(self)
+ ContentEventAwareTests.setUp(self)
+
+ def tearDown(self):
+ ContentEventAwareTests.tearDown(self)
+ SecurityTest.tearDown(self)
+
+
+ def _makeSite(self):
+ import cStringIO
+ from OFS.Application import Application
+ from OFS.tests.testCopySupport import makeConnection
+ from Products.CMFCore.PortalFolder import PortalFolder
+ from Testing.makerequest import makerequest
+
+ self.connection = makeConnection()
+ try:
+ r = self.connection.root()
+ a = Application()
+ r['Application'] = a
+ self.root = a
+ responseOut = self.responseOut = cStringIO.StringIO()
+ self.app = makerequest(self.root, stdout=responseOut)
+ site = SimpleFolder('site')
+ self.app._setObject('site', site)
+ site = self.app._getOb('site')
+ site._setObject('portal_catalog', DummyCatalog())
+ site._setObject('portal_workflow', DummyWorkflowTool())
+ # Hack, we need a _p_mtime for the file, so we make sure that it
+ # has one. We use a subtransaction, which means we can rollback
+ # later and pretend we didn't touch the ZODB.
+ transaction.savepoint(optimistic=True)
+ except:
+ self.connection.close()
+ raise
+ else:
+ return site
+
+ def _initPolicyAndUser( self
+ , a_lambda=None
+ , v_lambda=None
+ , c_lambda=None
+ ):
+ from AccessControl import SecurityManager
+ from Products.CMFCore.tests.base.testcase import newSecurityManager
+
+ def _promiscuous( *args, **kw ):
+ return 1
+
+ if a_lambda is None:
+ a_lambda = _promiscuous
+
+ if v_lambda is None:
+ v_lambda = _promiscuous
+
+ if c_lambda is None:
+ c_lambda = _promiscuous
+
+ scp = _SensitiveSecurityPolicy( v_lambda, c_lambda )
+ SecurityManager.setSecurityPolicy( scp )
+ newSecurityManager( None
+ , _AllowedUser( a_lambda ).__of__( self.root ) )
+
+ def test_object_indexed_after_adding(self):
+
+ site = self._makeSite()
+ self._registerObjectEventHandler()
+ bar = TheClass('bar')
+ site._setObject('bar', bar)
+ cat = site.portal_catalog
+ self.assertEquals(cat.log, ["index /site/bar"])
+
+ def test_object_unindexed_after_removing(self):
+
+ site = self._makeSite()
+ self._registerObjectEventHandler()
+ bar = TheClass('bar')
+ site._setObject('bar', bar)
+ cat = site.portal_catalog
+ cat.log = []
+ site._delObject('bar')
+ self.assertEquals(cat.log, ["unindex /site/bar"])
+
+ def test_object_indexed_after_copy_and_pasting(self):
+
+ self._initPolicyAndUser() # allow copy/paste operations
+ site = self._makeSite()
+ site.folder1 = SimpleFolder('folder1')
+ folder1 = site.folder1
+ site.folder2 = SimpleFolder('folder2')
+ folder2 = site.folder2
+
+ self._registerObjectEventHandler()
+ bar = TheClass('bar')
+ folder1._setObject('bar', bar)
+ cat = site.portal_catalog
+ cat.log = []
+
+ transaction.savepoint(optimistic=True)
+
+ cookie = folder1.manage_copyObjects(ids=['bar'])
+ folder2.manage_pasteObjects(cookie)
+
+ self.assertEquals(cat.log, ["index /site/folder2/bar"])
+
+ def test_object_reindexed_after_cut_and_paste(self):
+
+ self._initPolicyAndUser() # allow copy/paste operations
+ site = self._makeSite()
+ site.folder1 = SimpleFolder('folder1')
+ folder1 = site.folder1
+ site.folder2 = SimpleFolder('folder2')
+ folder2 = site.folder2
+
+ self._registerObjectEventHandler()
+ bar = TheClass('bar')
+ folder1._setObject('bar', bar)
+ cat = site.portal_catalog
+ cat.log = []
+
+ transaction.savepoint(optimistic=True)
+
+ cookie = folder1.manage_cutObjects(ids=['bar'])
+ folder2.manage_pasteObjects(cookie)
+
+ self.assertEquals(cat.log, ["unindex /site/folder1/bar",
+ "reindex /site/folder2/bar []"])
+
+ def test_object_reindexed_after_moving(self):
+
+ self._initPolicyAndUser() # allow copy/paste operations
+ site = self._makeSite()
+
+ self._registerObjectEventHandler()
+ bar = TheClass('bar')
+ site._setObject('bar', bar)
+ cat = site.portal_catalog
+ cat.log = []
+
+ transaction.savepoint(optimistic=True)
+
+ site.manage_renameObject(id='bar', new_id='baz')
+ self.assertEquals(cat.log, ["unindex /site/bar",
+ "reindex /site/baz []"])
+
def test_suite():
return unittest.TestSuite((
unittest.makeSuite(CMFCatalogAwareTests),
+ unittest.makeSuite(CMFCatalogAware_CopySupport_Tests),
))
if __name__ == '__main__':
Copied: CMF/branches/tseaver-catalog_events/CMFCore/tests/test_CookieCrumbler.py (from rev 41522, CMF/trunk/CMFCore/tests/test_CookieCrumbler.py)
Modified: CMF/branches/tseaver-catalog_events/CMFCore/tests/test_OpaqueItems.py
===================================================================
--- CMF/trunk/CMFCore/tests/test_OpaqueItems.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/tests/test_OpaqueItems.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -18,17 +18,17 @@
import Testing
from zope.interface import implements
+from zope.interface import implementedBy
from Products.CMFCore.interfaces import ICallableOpaqueItem
from Products.CMFCore.interfaces import ICallableOpaqueItemEvents
-from Products.CMFCore.interfaces.IOpaqueItems \
- import ICallableOpaqueItem as z2ICallableOpaqueItem
-from Products.CMFCore.interfaces.IOpaqueItems \
- import ICallableOpaqueItemEvents as z2ICallableOpaqueItemEvents
+from Products.CMFCore.interfaces import IContentish
+
from Products.CMFCore.PortalFolder import PortalFolder
from Products.CMFCore.tests.base.dummy \
import DummyContent as OriginalDummyContent
from Products.CMFCore.tests.base.testcase import SecurityTest
+from Products.CMFCore.tests.base.testcase import ContentEventAwareTests
from Products.CMFCore.TypesTool import TypesTool
@@ -48,6 +48,7 @@
class DummyContent(OriginalDummyContent):
""" A Dummy piece of PortalContent with additional attributes
"""
+ implements(IContentish)
def __init__(self, id='dummy', opaqueItem=None, *args, **kw):
OriginalDummyContent.__init__(self, id, *args, **kw)
@@ -95,18 +96,12 @@
class Marker(OpaqueBase):
""" Opaque item without manage_after/before hookes but marked as callable
"""
- implements(ICallableOpaqueItem)
- __implements__ = (
- z2ICallableOpaqueItem,
- )
+ implements(IContentish, ICallableOpaqueItem)
class Hooks(OpaqueBase):
""" Opaque item with manage_after/before hooks but not marked as callable
"""
- implements(ICallableOpaqueItemEvents)
- __implements__ = (
- z2ICallableOpaqueItemEvents,
- )
+ implements(IContentish, ICallableOpaqueItemEvents)
def manage_afterAdd(self, item, container):
self.addCount = self.addCounter
@@ -124,17 +119,18 @@
class MarkerAndHooks(Marker, Hooks):
""" Opaque item with manage_after/before hookes and marked as callable
"""
- __implements__ = Marker.__implements__ + Hooks.__implements__
+ implements(implementedBy(Marker), implementedBy(Hooks))
# -------------------------------------------
# Unit Tests
# -------------------------------------------
-class ManageBeforeAfterTests(SecurityTest):
+class ManageBeforeAfterTests(SecurityTest, ContentEventAwareTests):
def setUp(self):
SecurityTest.setUp(self)
+ ContentEventAwareTests.setUp(self)
root = self.root
@@ -163,6 +159,12 @@
try: sub._delObject('dummy')
except AttributeError: pass
+ self._registerObjectEventHandler()
+
+ def tearDown(self):
+ ContentEventAwareTests.tearDown(self)
+ SecurityTest.tearDown(self)
+
def test_nonCallableItem(self):
# no exception should be raised
folder = self.folder
Modified: CMF/branches/tseaver-catalog_events/CMFCore/tests/test_PortalContent.py
===================================================================
--- CMF/trunk/CMFCore/tests/test_PortalContent.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/tests/test_PortalContent.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -24,6 +24,7 @@
from Products.CMFCore.tests.base.dummy import DummySite
from Products.CMFCore.tests.base.dummy import DummyUserFolder
from Products.CMFCore.tests.base.testcase import SecurityRequestTest
+from Products.CMFCore.tests.base.testcase import ContentEventAwareTests
class PortalContentTests(unittest.TestCase):
@@ -49,18 +50,24 @@
verifyClass(IDynamicType, PortalContent)
-class TestContentCopyPaste(SecurityRequestTest):
+class TestContentCopyPaste(SecurityRequestTest, ContentEventAwareTests):
# Tests related to http://www.zope.org/Collectors/CMF/205
# Copy/pasting a content item must set ownership to pasting user
def setUp(self):
SecurityRequestTest.setUp(self)
+ ContentEventAwareTests.setUp(self)
self.root._setObject('site', DummySite('site'))
self.site = self.root.site
self.acl_users = self.site._setObject('acl_users', DummyUserFolder())
+ self._registerObjectEventHandler()
+ def tearDown(self):
+ ContentEventAwareTests.tearDown(self)
+ SecurityRequestTest.tearDown(self)
+
def _initContent(self, folder, id):
from Products.CMFCore.PortalContent import PortalContent
Modified: CMF/branches/tseaver-catalog_events/CMFCore/tests/test_PortalFolder.py
===================================================================
--- CMF/trunk/CMFCore/tests/test_PortalFolder.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFCore/tests/test_PortalFolder.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -40,6 +40,7 @@
from Products.CMFCore.tests.base.testcase import newSecurityManager
from Products.CMFCore.tests.base.testcase import noSecurityManager
from Products.CMFCore.tests.base.testcase import SecurityTest
+from Products.CMFCore.tests.base.testcase import ContentEventAwareTests
from Products.CMFCore.tests.base.tidata import FTIDATA_CMF15
from Products.CMFCore.tests.base.tidata import FTIDATA_DUMMY
from Products.CMFCore.tests.base.utils import has_path
@@ -53,11 +54,12 @@
'permission': 'View'}]
-class PortalFolderFactoryTests( SecurityTest ):
+class PortalFolderFactoryTests( SecurityTest, ContentEventAwareTests ):
def setUp( self ):
from Products.CMFCore.PortalFolder import PortalFolder
SecurityTest.setUp( self )
+ ContentEventAwareTests.setUp( self )
self.root._setObject( 'portal_types', TypesTool() )
types_tool = self.root.portal_types
@@ -73,6 +75,12 @@
fti = FTIDATA_DUMMY[0].copy()
types_tool._setObject( 'Dummy Content', FTI(**fti) )
+ self._registerObjectEventHandler()
+
+ def tearDown( self ):
+ ContentEventAwareTests.tearDown( self )
+ SecurityTest.tearDown( self )
+
def _makeOne( self, id ):
from Products.CMFCore.PortalFolder import PortalFolder
return PortalFolder( id ).__of__( self.root )
@@ -111,12 +119,20 @@
, type_name='Dummy Content', id='foo' )
-class PortalFolderTests(SecurityTest):
+class PortalFolderTests(SecurityTest, ContentEventAwareTests):
def setUp(self):
+
SecurityTest.setUp(self)
+ ContentEventAwareTests.setUp( self )
self.site = DummySite('site').__of__(self.root)
+ self._registerObjectEventHandler()
+
+ def tearDown( self ):
+ ContentEventAwareTests.tearDown( self )
+ SecurityTest.tearDown( self )
+
def _makeOne(self, id, *args, **kw):
from Products.CMFCore.PortalFolder import PortalFolder
@@ -248,9 +264,7 @@
wftool.notifyCreated(test)
self.assertEqual( len(ctool), 0 )
-
def test_tracker261(self):
-
#
# Tracker issue #261 says that content in a deleted folder
# is not being uncatalogued. Try creating a subfolder with
@@ -274,7 +288,7 @@
self.assertEqual( len(ctool), 1 )
foo.reset()
- test.manage_delObjects( ids=['sub'] )
+ test._delObject( 'sub' )
self.failIf( foo.after_add_called )
self.failUnless( foo.before_delete_called )
self.assertEqual( len(ctool), 0 )
@@ -423,13 +437,19 @@
self.assert_(sub.checkIdAvailable('.foo'))
-class PortalFolderMoveTests(SecurityTest):
+class PortalFolderMoveTests(SecurityTest, ContentEventAwareTests):
def setUp(self):
SecurityTest.setUp(self)
+ ContentEventAwareTests.setUp( self )
self.root._setObject( 'site', DummySite('site') )
self.site = self.root.site
+ self._registerObjectEventHandler()
+ def tearDown( self ):
+ ContentEventAwareTests.tearDown( self )
+ SecurityTest.tearDown( self )
+
def _makeOne(self, id, *args, **kw):
from Products.CMFCore.PortalFolder import PortalFolder
Modified: CMF/branches/tseaver-catalog_events/CMFDefault/tests/test_Discussions.py
===================================================================
--- CMF/trunk/CMFDefault/tests/test_Discussions.py 2006-01-31 15:34:23 UTC (rev 41511)
+++ CMF/branches/tseaver-catalog_events/CMFDefault/tests/test_Discussions.py 2006-02-16 04:44:31 UTC (rev 41631)
@@ -25,6 +25,7 @@
from Products.CMFCore.tests.base.dummy import DummySite
from Products.CMFCore.tests.base.dummy import DummyTool
from Products.CMFCore.tests.base.testcase import SecurityTest
+from Products.CMFCore.tests.base.testcase import ContentEventAwareTests
from Products.CMFCore.tests.base.tidata import FTIDATA_DUMMY
from Products.CMFCore.tests.base.utils import has_path
from Products.CMFCore.TypesTool import FactoryTypeInformation as FTI
@@ -95,15 +96,21 @@
verifyClass(IDiscussable, DiscussionItemContainer)
-class DiscussionTests( SecurityTest ):
+class DiscussionTests( SecurityTest, ContentEventAwareTests ):
def setUp( self ):
SecurityTest.setUp(self)
+ ContentEventAwareTests.setUp( self )
self.site = DummySite('site').__of__(self.root)
self.site._setObject( 'portal_discussion', DiscussionTool() )
self.site._setObject( 'portal_membership', DummyTool() )
self.site._setObject( 'portal_types', TypesTool() )
+ self._registerObjectEventHandler()
+ def tearDown( self ):
+ ContentEventAwareTests.tearDown( self )
+ SecurityTest.tearDown( self )
+
def _makeDummyContent(self, id, *args, **kw):
return self.site._setObject( id, DummyContent(id, *args, **kw) )
More information about the CMF-checkins
mailing list