From andrew@zope.com Wed Jan 2 22:03:34 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 2 Jan 2002 17:03:34 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/dtml - editToolsActions.dtml:1.1.2.1 manageActionProviders.dtml:1.1.2.1 Message-ID: <200201022203.g02M3YH21516@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv20833/CMFCore/dtml Added Files: Tag: andrew_ttw_actions-branch editToolsActions.dtml manageActionProviders.dtml Log Message: *Changes to support Tools TTW Actions *Changes to support TTW Action Provider configuration *Docs and Test coming soon... === Added File CMF/CMFCore/dtml/editToolsActions.dtml === &dtml-form_title;

&dtml-form_title;

Name
Id
Action
Condition
Permission
Category
Visible?

Add an action

Name
Id
Action
Condition
Permission
Category
Visible?
=== Added File CMF/CMFCore/dtml/manageActionProviders.dtml === manage action providers

Action Providers

Name
&dtml-sequence-item;
From andrew@zope.com Wed Jan 2 22:03:35 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 2 Jan 2002 17:03:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - DiscussionTool.py:1.6.12.1 MembershipTool.py:1.17.12.1 MetadataTool.py:1.9.12.1 RegistrationTool.py:1.8.8.1 SyndicationTool.py:1.9.12.1 URLTool.py:1.7.12.1 Message-ID: <200201022203.g02M3ZC21532@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv20833/CMFDefault Modified Files: Tag: andrew_ttw_actions-branch DiscussionTool.py MembershipTool.py MetadataTool.py RegistrationTool.py SyndicationTool.py URLTool.py Log Message: *Changes to support Tools TTW Actions *Changes to support TTW Action Provider configuration *Docs and Test coming soon... === CMF/CMFDefault/DiscussionTool.py 1.6 => 1.6.12.1 === from utils import _dtmldir from DiscussionItem import DiscussionItemContainer +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.Expression import Expression class DiscussionNotAllowed( Exception ): pass -class DiscussionTool( UniqueObject, SimpleItem ): +class DiscussionTool( UniqueObject, SimpleItem, ActionProviderBase ): id = 'portal_discussion' meta_type = 'Default Discussion Tool' + _actions = [ActionInformation(id='reply' + , title='Reply' + , action=Expression( + text='string: ${object_url}/discussion_reply_form') + , condition=Expression( + text='python: object is not None and ' + + 'portal.portal_discussion.isDiscussionAllowedFor(object)') + , permissions=('Reply to item',) + , category='object' + , visible=1 + )] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = (ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + SimpleItem.manage_options + ) + SimpleItem.manage_options) # # ZMI methods @@ -52,6 +67,13 @@ # 'portal_discussion' interface methods # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return available actions via tool. + """ + return self._actions + security.declarePublic( 'overrideDiscussionFor' ) def overrideDiscussionFor(self, content, allowDiscussion): """ @@ -96,29 +118,6 @@ if typeInfo: return typeInfo.allowDiscussion() return 0 - - # - # ActionProvider interface - # - security.declarePrivate( 'listActions' ) - def listActions(self, info): - # Return actions for reply and show replies - content = info.content - if content is None or not self.isDiscussionAllowedFor(content): - return None - - discussion = self.getDiscussionFor(content) - discussion_url = info.content_url - - actions = ( - {'name': 'Reply', - 'url': discussion_url + '/discussion_reply_form', - 'permissions': ['Reply to item'], - 'category': 'object' - }, - ) - - return actions # # Utility methods === CMF/CMFDefault/MembershipTool.py 1.17 => 1.17.12.1 === from Products.CMFCore.utils import _getAuthenticatedUser, _checkPermission from Products.CMFCore.utils import getToolByName +from Products.CMFCore.ActionsTool import ActionInformation import Products.CMFCore.MembershipTool from Products.CMFCore.PortalFolder import manage_addPortalFolder import Document from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo +from Products.CMFCore.Expression import Expression from Products.CMFCore.CMFCorePermissions import View, AccessContentsInformation from Products.CMFCore.CMFCorePermissions import ListPortalMembers, AddPortalMember from Products.CMFCore.CMFCorePermissions import ManagePortal @@ -40,10 +42,92 @@ in the Tool Box on the left. ''' - class MembershipTool ( Products.CMFCore.MembershipTool.MembershipTool ): """ """ + _actions =[ActionInformation(id='join' + , title='Join' + , description='Click here to Join' + , action=Expression( + text='string: ${portal_url}/join_form') + , permissions=(View,) + , category='user' + , condition=Expression(text='not: member') + , visible=1 + ) + , ActionInformation(id='login' + , title='Login' + , description='Click here to Login' + , action=Expression( + text='string: ${portal_url}/login_form') + , permissions=(View,) + , category='user' + , condition=Expression(text='not: member') + , visible=1 + ) + , ActionInformation(id='preferences' + , title='Preferences' + , description='Change your user preferences' + , action=Expression( + text='string: ${portal_url}/personalize_form') + , permissions=(View,) + , category='user' + , condition=Expression(text='member') + , visible=1 + ) + , ActionInformation(id='logout' + , title='Log out' + , description='Click here to logout' + , action=Expression( + text='string: ${portal_url}/logout') + , permissions=(View,) + , category='user' + , condition=Expression(text='member') + , visible=1 + ) + , ActionInformation(id='configPortal' + , title='Reconfigure Portal' + , description='Reconfigure the portal' + , action=Expression( + text='string: ${portal_url}/reconfig_form') + , permissions=(ManagePortal,) + , category='global' + , condition=None + , visible=1 + ) + , ActionInformation(id='addFavorite' + , title='Add to favorites' + , description='Add this item to your favorites' + , action=Expression( + text='string: ${portal_url}/addtoFavorites') + , permissions=(View,) + , category='user' + , condition=Expression( + text='python: portal.portal_membership.getHomeFolder()') + , visible=1 + ) + , ActionInformation(id='mystuff' + , title='my stuff' + , description='Goto your home folder' + , action=Expression(text='member/getHomeUrl') + , permissions=(View,) + , category='user' + , condition=Expression( + text='python: member and portal.portal_membership.getHomeFolder()') + , visible=1 + ) + , ActionInformation(id='favorites' + , title='My favorites' + , description='Browser your favorites' + , action=Expression( + text='python: member.getHomeUrl() + \'/Favorites/folder_contents\'') + , permissions=(View,) + , category='user' + , condition=Expression( + text='python: member and hasattr(portal.portal_membership.getHomeFolder(), \'Favorites\')') + , visible=1 + ) + ] meta_type = 'Default Membership Tool' @@ -151,63 +235,8 @@ return None security.declarePrivate( 'listActions' ) - def listActions(self, info): - '''Lists actions available to the user.''' - user_actions = None - portal_url = info.portal_url - if info.isAnonymous: - user_actions = ( - {'name': 'Log in', - 'url': portal_url + '/login_form', - 'permissions': [], - 'category': 'user'}, - {'name': 'Join', - 'url': portal_url + '/join_form', - 'permissions': [AddPortalMember], - 'category': 'user'}, - ) - - if not info.isAnonymous: - home_folder = self.getHomeFolder() - homeUrl = self.getHomeUrl() - user_actions = ( - {'name': 'Preferences', - 'url': portal_url + '/personalize_form', - 'permissions': [], - 'category': 'user'}, - {'name': 'Log out', - 'url': portal_url + '/logout', - 'permissions' : [], - 'category': 'user'}, - {'name': 'Reconfigure portal', - 'url': portal_url + '/reconfig_form', - 'permissions': ['Manage portal'], - 'category': 'global'}, - ) - - if homeUrl is not None: - content_url = info.content_url - actions = ( - {'name': 'Add to Favorites', - 'url': ( content_url + '/addtoFavorites' ), - 'permissions' : [], - 'category': 'user'}, - {'name': 'My Stuff', - 'url': homeUrl + '/folder_contents', - 'permissions': [], - 'category': 'user'}, - ) - user_actions = user_actions + actions - - if hasattr( home_folder, 'Favorites' ): - added_actions = ( - {'name': 'My Favorites', - 'url' : homeUrl + '/Favorites/folder_contents', - 'permissions': [], - 'category': 'user'},) - user_actions = user_actions + added_actions - - return user_actions - + def listActions(self, info=None): + '''Lists actions available through the tool.''' + return self._actions InitializeClass(MembershipTool) === CMF/CMFDefault/MetadataTool.py 1.9 => 1.9.12.1 === from AccessControl import ClassSecurityInfo, getSecurityManager from Products.CMFCore import CMFCorePermissions +from Products.CMFCore.ActionProviderBase import ActionProviderBase from utils import _dtmldir class MetadataElementPolicy( Persistent ): @@ -190,11 +191,13 @@ class MetadataError( Exception ): pass -class MetadataTool( UniqueObject, SimpleItem ): +class MetadataTool( UniqueObject, SimpleItem, ActionProviderBase ): id = 'portal_metadata' meta_type = 'Default Metadata Tool' + _actions = [] + security = ClassSecurityInfo() # @@ -225,7 +228,8 @@ # # ZMI methods # - manage_options = ( ( { 'label' : 'Overview' + manage_options = ( ActionProviderBase.manage_options + + ( { 'label' : 'Overview' , 'action' : 'manage_overview' } , { 'label' : 'Properties' @@ -248,6 +252,13 @@ security.declareProtected( CMFCorePermissions.ManagePortal , 'propertiesForm' ) propertiesForm = DTMLFile( 'metadataProperties', _dtmldir ) + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return actions provided via tool. + """ + return self._actions security.declareProtected( CMFCorePermissions.ManagePortal , 'editProperties' ) === CMF/CMFDefault/RegistrationTool.py 1.8 => 1.8.8.1 === from Products.CMFCore.utils import UniqueObject from Products.CMFCore.utils import _checkPermission, getToolByName +from Products.CMFCore.ActionProviderBase import ActionProviderBase from Products.CMFCore.RegistrationTool import RegistrationTool from Globals import InitializeClass, DTMLFile @@ -27,9 +28,11 @@ from Products.CMFCore import CMFCorePermissions from utils import _dtmldir -class RegistrationTool (RegistrationTool): +class RegistrationTool (RegistrationTool, ActionProviderBase): meta_type = 'Default Registration Tool' + _actions = [] + security = ClassSecurityInfo() # @@ -37,6 +40,11 @@ # security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) + + manage_options = ( ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } + , + )) manage_overview = DTMLFile( 'explainRegistrationTool', _dtmldir ) # @@ -55,10 +63,11 @@ return None security.declarePublic('listActions') - def listActions(self, info): + def listActions(self, info=None): """ + Return actions provided via tool. """ - return None + return self._actions security.declarePublic( 'testPropertiesValidity' ) def testPropertiesValidity(self, props, member=None): === CMF/CMFDefault/SyndicationTool.py 1.9 => 1.9.12.1 === from Products.CMFCore.CMFCorePermissions import ManageProperties from Products.CMFCore.CMFCorePermissions import AccessContentsInformation +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression import Products.CMFCore.CMFCorePermissions from Products.CMFCore.PortalFolder import PortalFolder from SyndicationInfo import SyndicationInformation _dtmldir = os.path.join( package_home( globals() ), 'dtml' ) -class SyndicationTool (UniqueObject, SimpleItem): +class SyndicationTool (UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_syndication' meta_type = 'Default Syndication Tool' + _actions = [ActionInformation(id='syndication' + , title='Syndication' + , action=Expression( + text='string: ${folder_url}/synPropertiesForm') + , condition=Expression( + text='python: folder is object') + , permissions=(ManageProperties,) + , category='folder' + , visible=1 + )] + security = ClassSecurityInfo() #Default Sitewide Values @@ -46,7 +60,8 @@ max_items = 15 #ZMI Methods - manage_options = (({'label' : 'Overview' + manage_options = (ActionProviderBase.manage_options + + ({'label' : 'Overview' ,'action' : 'overview' , 'help' : ('CMFDefault', 'Syndication-Tool_Overview.stx') } ,{'label' : 'Properties' @@ -69,6 +84,13 @@ security.declareProtected(ManagePortal, 'reportForm') reportForm = HTMLFile('synReports', _dtmldir) + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return actions provided by tool + """ + return self._actions + security.declareProtected(ManagePortal, 'editProperties') def editProperties(self , updatePeriod=None === CMF/CMFDefault/URLTool.py 1.7 => 1.7.12.1 === security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } , ) + SimpleItem.manage_options From andrew@zope.com Wed Jan 2 22:03:35 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 2 Jan 2002 17:03:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.1.2.1 ActionProviderBase.py:1.1.2.1 Expression.py:1.1.2.1 ActionsTool.py:1.19.10.1 CatalogTool.py:1.21.14.1 MemberDataTool.py:1.12.14.1 MembershipTool.py:1.16.14.1 PortalFolder.py:1.28.10.1 RegistrationTool.py:1.7.14.1 SkinsTool.py:1.11.14.1 TypesTool.py:1.26.14.1 UndoTool.py:1.4.14.1 WorkflowTool.py:1.19.4.1 Message-ID: <200201022203.g02M3Zl21533@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv20833/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionsTool.py CatalogTool.py MemberDataTool.py MembershipTool.py PortalFolder.py RegistrationTool.py SkinsTool.py TypesTool.py UndoTool.py WorkflowTool.py Added Files: Tag: andrew_ttw_actions-branch ActionInformation.py ActionProviderBase.py Expression.py Log Message: *Changes to support Tools TTW Actions *Changes to support TTW Action Provider configuration *Docs and Test coming soon... === Added File CMF/CMFCore/ActionInformation.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (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 # ############################################################################## """Basic action list tool. $Id: ActionInformation.py,v 1.1.2.1 2002/01/02 22:03:32 andrew Exp $ """ __version__='$Revision: 1.1.2.1 $'[11:-2] from utils import SimpleItemWithProperties, _dtmldir, getToolByName import CMFCorePermissions from AccessControl import ClassSecurityInfo from Acquisition import aq_inner, aq_parent from Globals import InitializeClass, DTMLFile class ActionInformation(SimpleItemWithProperties): """ Represent a single action which the user can select from a list and execut in some context. """ _isActionInformation = 1 __allow_access_to_unprotected_subobjects__ = 1 manage_options = (SimpleItemWithProperties.manage_options[:1] + ({'label': 'Actions', 'action': 'manage_editActionsForm'},) + SimpleItemWithProperties.manage_options[1:]) security = ClassSecurityInfo() security.declareProtected(CMFCorePermissions.ManagePortal , 'manage_editProperties' , 'manage_changeProperties' , 'manage_propertiesForm' ) _basic_properties = ( {'id': 'title', 'type': 'string', 'mode': 'w', 'label': 'Title'} , {'id': 'description', 'type': 'text', 'mode': 'w', 'label': 'Description'} , {'id': 'category', 'type': 'string', 'mode': 'w', 'label': 'Category'} , {'id': 'priority', 'type': 'boolean', 'mode': 'w', 'label': 'Priority'} ) title = '' description = '' category = '' priority = 0 visible = 1 _action = '' def __init__(self , id , title='' , description='' , category='object' , condition='' , permissions=() , priority=10 , visible=1 , action=''): """ Setup an instance """ self.id = id self.title = title self.description = description self.category = category self.condition = condition self.permissions = permissions self.priority = priority self.visible = visible self._action = action security.declareProtected(CMFCorePermissions.View, 'Title') def Title(self): """ Return the Action title - name """ if self.title: return self.title else: return self.getId() security.declareProtected(CMFCorePermissions.View, 'Description') def Description(self): """ Return a description of the action """ return self.description security.declarePrivate('testCondition') def testCondition(self, ec): """ Evaluate condition and return 0 or 1 """ if self.condition: return self.condition(ec) else: return 1 security.declarePublic('getAction') def getAction(self, ec): """ Return the action, which is an TALES expresssion """ if self._action: aa = self._action(ec) else: aa = '' action = {} action['id'] = self.id action['name'] = self.Title() action['url'] = aa action['permissions'] = self.getPermissions() action['category'] = self.getCategory() action['visible'] = self.getVisibility() return action security.declarePublic('getCondition') def getCondition(self): """ If not an empty string or None, evaluate the expression """ if self.condition: return self.condition.text else: return self.condition security.declarePublic('getPermission') def getPermissions(self): """ Return the permission if any required for a user to execute the action """ return self.permissions security.declarePublic('getCategory') def getCategory(self): """ Return the category for which the action is """ if self.category: return self.category else: return 'object' security.declarePublic('getVisibility') def getVisibility(self): """ Return boolean for whether the action is visible in the UI """ if self.visible: return self.visible else: return 1 security.declarePublic('getPriority') def getPriority(self): """ Return integer priority for sorting """ if self.priority: return self.priority else: return 10 InitializeClass(ActionInformation) class oai: #Provided for backwards compatability # Provides information that may be needed when constructing the list of # available actions. __allow_access_to_unprotected_subobjects__ = 1 def __init__(self, tool, folder, object=None): self.portal = portal = aq_parent(aq_inner(tool)) membership = getToolByName(tool, 'portal_membership') self.isAnonymous = membership.isAnonymousUser() self.portal_url = portal.absolute_url() if folder is not None: self.folder_url = folder.absolute_url() self.folder = folder else: self.folder_url = self.portal_url self.folder = portal self.content = object if object is not None: self.content_url = object.absolute_url() else: self.content_url = None def __getitem__(self, name): # Mapping interface for easy string formatting. if name[:1] == '_': raise KeyError, name if hasattr(self, name): return getattr(self, name) raise KeyError, name === Added File CMF/CMFCore/ActionProviderBase.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## from OFS.SimpleItem import SimpleItem from Globals import DTMLFile from CMFCorePermissions import ManagePortal from utils import _dtmldir, cookString from AccessControl import ClassSecurityInfo from ActionInformation import ActionInformation from Expression import Expression """Basic action list tool. $Id: ActionProviderBase.py,v 1.1.2.1 2002/01/02 22:03:32 andrew Exp $ """ __version__='$Revision: 1.1.2.1 $'[11:-2] class ActionProviderBase: """ Provide ActionTabs and management methods for ActionProviders """ _actions = [] security = ClassSecurityInfo() _actions_form = DTMLFile( 'editToolsActions', _dtmldir ) manage_options = ({ 'label' : 'Actions', 'action' : 'manage_editActionsForm' } , ) security.declarePrivate('listActions') def listActions(self): """ Return all the actions defined by a tool """ if self._actions: return self._actions else: return None security.declareProtected(ManagePortal, 'manage_editActionsForm') def manage_editActionsForm(self, REQUEST, manage_tabs_message=None): """ Shows the 'Actions' management tab. """ actions = [] if self.listActions() is not None: for a in self.listActions(): a1 = {} a1['id'] = a.getId() a1['name'] = a.Title() p = a.getPermissions() if p: a1['permission'] = p[0] else: a1['permission'] = '' if not a.getCategory(): a1['category'] = 'object' else: a1['category'] = a.getCategory() if not a.getVisibility(): a1['visible'] = 1 else: a1['visible'] = a.getVisibility() if a._action: a1['action'] = a._action.text else: a1['action'] = '' if a.condition: a1['condition'] = a.getCondition() else: a1['condition'] = '' actions.append(a1) # possible_permissions is in AccessControl.Role.RoleManager. pp = self.possible_permissions() return self._actions_form(self, REQUEST, actions=actions, possible_permissions=pp, management_view='Actions', manage_tabs_message=manage_tabs_message) security.declareProtected(ManagePortal, 'addAction') def addAction( self , id , name , action , condition , permission , category , visible=1 , REQUEST=None ): """ Adds an action to the list. """ al = self._actions if not name: raise ValueError('A name is required.') if action: a_expr = Expression(text=str(action)) else: a_expr = '' if condition: c_expr = Expression(text=str(condition)) else: c_expr = '' al.append(ActionInformation(id=str(id) , title=str(name) , action=a_expr , condition=c_expr , permissions=(str(permission),) , category=str(category) , visible=int(visible) )) self._actions = al if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message='Added.') security.declareProtected(ManagePortal, 'changeActions') def changeActions(self, properties=None, REQUEST=None): """ Changes the _actions. """ if properties is None: properties = REQUEST actions = [] for idx in range(len(self._actions)): s_idx = str(idx) action = { 'id': str(properties.get('id_' + s_idx, '')), 'name': str(properties.get('name_' + s_idx, '')), 'action': str(properties.get('action_' + s_idx, '')), 'condition': str(properties.get('condition_' + s_idx, '')), 'permissions': (properties.get('permission_' + s_idx, ()),), 'category': str(properties.get('category_' + s_idx, 'object')), 'visible': not not properties.get('visible_' + s_idx, 0), } if not action['name']: raise ValueError('A name is required.') a = self._actions[idx] a.id = action['id'] a.title = action['name'] if not a._action: a._action = Expression(text=action['action']) else: a._action.text = action['action'] if not a.condition: a.condition = Expression(text=action['condition']) else: del(a.condition) a.condition= Expression(text = action['condition']) a.permissions = action['permissions'] a.category = action['category'] a.visible = action['visible'] if REQUEST is not None: return self.manage_editActionsForm(REQUEST, manage_tabs_message= 'Actions changed.') security.declareProtected(ManagePortal, 'deleteActions') def deleteActions(self, selections=(), REQUEST=None): """ Deletes actions. """ actions = list(self._actions) sels = list(map(int, selections)) # Convert to a list of integers. sels.sort() sels.reverse() for idx in sels: del actions[idx] self._actions = actions if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message=( 'Deleted %d action(s).' % len(sels))) security.declareProtected(ManagePortal, 'moveUpActions') def moveUpActions(self, selections=(), REQUEST=None): """ Moves the specified actions up one slot. """ actions = list(self._actions) sels = list(map(int, selections)) # Convert to a list of integers. sels.sort() for idx in sels: idx2 = idx - 1 if idx2 < 0: # Wrap to the bottom. idx2 = len(actions) - 1 # Swap. a = actions[idx2] actions[idx2] = actions[idx] actions[idx] = a self._actions = actions if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message=( 'Moved up %d action(s).' % len(sels))) security.declareProtected(ManagePortal, 'moveDownActions') def moveDownActions(self, selections=(), REQUEST=None): """ Moves the specified actions down one slot. """ actions = list(self._actions) sels = list(map(int, selections)) # Convert to a list of integers. sels.sort() sels.reverse() for idx in sels: idx2 = idx + 1 if idx2 >= len(actions): # Wrap to the top. idx2 = 0 # Swap. a = actions[idx2] actions[idx2] = actions[idx] actions[idx] = a self._actions = actions if REQUEST is not None: return self.manage_editActionsForm( REQUEST, manage_tabs_message=( 'Moved down %d action(s).' % len(sels))) === Added File CMF/CMFCore/Expression.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (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 # ############################################################################## ''' Expressions in a web-configurable workflow. $Id: Expression.py,v 1.1.2.1 2002/01/02 22:03:32 andrew Exp $ ''' __version__='$Revision: 1.1.2.1 $'[11:-2] import Globals from Globals import Persistent from Acquisition import aq_inner, aq_parent from AccessControl import getSecurityManager, ClassSecurityInfo from utils import getToolByName from Products.PageTemplates.Expressions import getEngine from Products.PageTemplates.TALES import SafeMapping from Products.PageTemplates.PageTemplate import ModuleImporter class Expression (Persistent): text = '' _v_compiled = None security = ClassSecurityInfo() def __init__(self, text): self.text = text self._v_compiled = getEngine().compile(text) def __call__(self, econtext): compiled = self._v_compiled if compiled is None: compiled = self._v_compiled = getEngine().compile(self.text) # ?? Maybe expressions should manipulate the security # context stack. res = compiled(econtext) if isinstance(res, Exception): raise res #print 'returning %s from %s' % (`res`, self.text) return res Globals.InitializeClass(Expression) def createExprContext(folder, portal, object): ''' An expression context provides names for TALES expressions. ''' pm = getToolByName(portal, 'portal_membership') if object is None: object_url = '' else: object_url = object.absolute_url() if pm.isAnonymousUser(): member = None else: member = pm.getAuthenticatedMember() data = { 'object_url': object_url, 'folder_url': folder.absolute_url(), 'portal_url': portal.absolute_url(), 'object': object, 'content': object, 'folder': folder, 'portal': portal, 'nothing': None, 'request': getattr( object, 'REQUEST', None ), 'modules': ModuleImporter, 'member': member, } return getEngine().getContext(data) === CMF/CMFCore/ActionsTool.py 1.19 => 1.19.10.1 === -from utils import UniqueObject, _getAuthenticatedUser, _checkPermission -from utils import getToolByName, _dtmldir +import OFS +from utils import UniqueObject, SimpleItemWithProperties, _getAuthenticatedUser, _checkPermission +from utils import getToolByName, _dtmldir, cookString import CMFCorePermissions from OFS.SimpleItem import SimpleItem from Globals import InitializeClass, DTMLFile, package_home @@ -27,60 +28,57 @@ from Acquisition import aq_base, aq_inner, aq_parent from AccessControl import ClassSecurityInfo from string import join +from Expression import Expression, createExprContext +from ActionInformation import ActionInformation, oai +from ActionProviderBase import ActionProviderBase -class ActionInformation: - # Provides information that may be needed when constructing the list of - # available actions. - __allow_access_to_unprotected_subobjects__ = 1 - - def __init__(self, tool, folder, object=None): - self.portal = portal = aq_parent(aq_inner(tool)) - membership = getToolByName(tool, 'portal_membership') - self.isAnonymous = membership.isAnonymousUser() - self.portal_url = portal.absolute_url() - if folder is not None: - self.folder_url = folder.absolute_url() - self.folder = folder - else: - self.folder_url = self.portal_url - self.folder = portal - self.content = object - if object is not None: - self.content_url = object.absolute_url() - else: - self.content_url = None - def __getitem__(self, name): - # Mapping interface for easy string formatting. - if name[:1] == '_': - raise KeyError, name - if hasattr(self, name): - return getattr(self, name) - raise KeyError, name - - -class ActionsTool (UniqueObject, SimpleItem): +class ActionsTool(UniqueObject, OFS.Folder.Folder, ActionProviderBase): """ Weave together the various sources of "actions" which are apropos to the current user and context. """ id = 'portal_actions' + _actions = [ActionInformation(id='folderContents' + , title='Folder contents' + , action=Expression( + text='string: ${folder_url}/folder_contents') + , condition=Expression( + text='python: folder is not object') + , permissions=('List folder contents',) + , category='object' + , visible=1 + ) + , ActionInformation(id='folderContents' + , title='Folder contents' + , action=Expression( + text='string: ${folder_url}/folder_contents') + , condition=Expression( + text='python: folder is object') + , permissions=('List folder contents',) + , category='folder' + , visible=1 + )] + meta_type = 'CMF Actions Tool' - action_providers = ( 'portal_actions' - , 'portal_memberdata' - , 'portal_registration' - , 'portal_discussion' - , 'portal_membership' - , 'portal_workflow' - , 'portal_undo' - ) + action_providers = ('portal_membership' + , 'portal_memberdata' + , 'portal_actions' + , 'portal_registration' + , 'portal_discussion' + , 'portal_undo' + , 'portal_syndication' + , 'portal_workflow') security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } - , - ) + SimpleItem.manage_options + manage_options = ( ActionProviderBase.manage_options + + ({'label' : 'Action Providers', 'action' : 'manage_actionProviders'} + , { 'label' : 'Overview', 'action' : 'manage_overview' } + , + ) + OFS.Folder.Folder.manage_options + ) # # ZMI methods @@ -88,19 +86,56 @@ security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) manage_overview = DTMLFile( 'explainActionsTool', _dtmldir ) + manage_actionProviders = DTMLFile('manageActionProviders', _dtmldir) # # Programmatically manipulate the list of action providers # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Lists actions available through the tool. + """ + return self._actions + security.declareProtected( CMFCorePermissions.ManagePortal , 'listActionProviders' ) - def listActionProviders( self ): + def listActionProviders(self): """ returns a sequence of action providers known by this tool """ return self.action_providers + security.declareProtected(CMFCorePermissions.ManagePortal + , 'manage_aproviders') + def manage_aproviders(self + , apname='' + , chosen=() + , add_provider=0 + , del_provider=0 + , REQUEST=None): + """ + Manage TTW Action Providers + """ + #import pdb; pdb.set_trace() + providers = self.listActionProviders() + new_providers = [] + if add_provider: + providers.append(apname) + elif del_provider: + for item in providers: + if item not in chosen: + new_providers.append(item) + providers = new_providers + self.action_providers = providers + if REQUEST is not None: + return self.manage_actionProviders(self + , REQUEST + , manage_tabs_message='Properties changed.') + + + security.declareProtected( CMFCorePermissions.ManagePortal , 'addActionProvider' ) @@ -111,26 +146,6 @@ p_new = p_old + ( provider_name, ) self.action_providers = p_new - security.declarePrivate('listActions') - def listActions(self, info): - """ - List actions available from this tool - """ - if info.isAnonymous: - return None - else: - actions = [] - folder_url = info.folder_url - content_url = info.content_url - if folder_url is not None: - actions.append( - { 'name' : 'Folder contents' - , 'url' : folder_url + '/folder_contents' - , 'permissions' : ['List folder contents'] - , 'category' : 'folder' - }) - return actions - security.declareProtected( CMFCorePermissions.ManagePortal , 'deleteActionProvider' ) @@ -162,16 +177,24 @@ break else: folder = aq_parent(aq_inner(folder)) - - info = ActionInformation(self, folder, object) - + ec = createExprContext(folder, portal, object) + ai_objs = [] actions = [] + info = oai(self, folder, object) # Include actions from specific tools. for provider_name in self.listActionProviders(): provider = getattr(self, provider_name) a = provider.listActions(info) - if a: - actions.extend(list(a)) + #import pdb; pdb.set_trace() + if a and type(a[0]) is not {}: + ai_objs.extend(list(a)) + elif len(a) > 0: + actions.append(a) + + if ai_objs: + for ai in ai_objs: + if ai.testCondition(ec): + actions.append(ai.getAction(ec)) # Include actions from object. if object is not None: @@ -181,7 +204,7 @@ if ti is not None: defs = ti.getActions() if defs: - c_url = info.content_url + c_url = object.absolute_url() for d in defs: a = d['action'] if a: @@ -197,7 +220,7 @@ 'visible': d.get('visible', 1), }) if hasattr(base, 'listActions'): - a = object.listActions(info) + a = object.listActions() if a: actions.extend(list(a)) === CMF/CMFCore/CatalogTool.py 1.21 => 1.21.14.1 === security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ZCatalog.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + ZCatalog.manage_options + )) def __init__(self): ZCatalog.__init__(self, self.getId()) === CMF/CMFCore/MemberDataTool.py 1.12 => 1.12.14.1 === from CMFCorePermissions import ViewManagementScreens import CMFCorePermissions +from ActionProviderBase import ActionProviderBase _marker = [] # Create a new marker object. -class MemberDataTool (UniqueObject, SimpleItem, PropertyManager): +class MemberDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase): '''This tool wraps user objects, making them act as Member objects. ''' id = 'portal_memberdata' meta_type = 'CMF Member Data Tool' + _actions = [] _v_temps = None _properties = () security = ClassSecurityInfo() - manage_options=( ( { 'label' : 'Overview' + manage_options=( ActionProviderBase.manage_options + + ({ 'label' : 'Overview' , 'action' : 'manage_overview' } , { 'label' : 'Contents' @@ -83,8 +86,11 @@ # 'portal_memberdata' interface methods # security.declarePrivate('listActions') - def listActions(self, info): - return None + def listActions(self, info=None): + """ + Return actions provided via tool. + """ + return self._actions security.declarePrivate('getMemberDataContents') def getMemberDataContents(self): === CMF/CMFCore/MembershipTool.py 1.16 => 1.16.14.1 === from CMFCorePermissions import ManagePortal import CMFCorePermissions +from ActionProviderBase import ActionProviderBase import Acquisition default_member_content = '''Default page for %s @@ -37,22 +38,23 @@ in the Tool Box on the left. ''' -class MembershipTool (UniqueObject, SimpleItem): +class MembershipTool (UniqueObject, SimpleItem, ActionProviderBase): # This tool accesses member data through an acl_users object. # It can be replaced with something that accesses member data in # a different way. id = 'portal_membership' meta_type = 'CMF Membership Tool' - + _actions = [] security = ClassSecurityInfo() - manage_options=( { 'label' : 'Overview' - , 'action' : 'manage_overview' - } - , { 'label' : 'Configuration' + manage_options=( ({ 'label' : 'Configuration' , 'action' : 'manage_mapRoles' - } - ) + SimpleItem.manage_options + },) + + ActionProviderBase.manage_options + + ( { 'label' : 'Overview' + , 'action' : 'manage_overview' + }, + ) + SimpleItem.manage_options) # # ZMI methods @@ -387,7 +389,7 @@ security.declarePrivate('listActions') - def listActions(self, info): + def listActions(self): return None security.declarePublic('getHomeFolder') === CMF/CMFCore/PortalFolder.py 1.28 => 1.28.10.1 === , 'category' : 'folder' } - , { 'id' : 'syndication' - , 'name' : 'Syndication' - , 'action' : 'synPropertiesForm' - , 'permissions' : (ManageProperties,) - , 'category' : 'folder' - } ) } , === CMF/CMFCore/RegistrationTool.py 1.7 => 1.7.14.1 === import CMFCorePermissions import string, random +from ActionProviderBase import ActionProviderBase -class RegistrationTool (UniqueObject, SimpleItem): +class RegistrationTool (UniqueObject, SimpleItem, ActionProviderBase): # This tool creates and modifies users by making calls # to portal_membership. id = 'portal_registration' @@ -37,9 +38,10 @@ security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = (ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + SimpleItem.manage_options + ) + SimpleItem.manage_options) # # ZMI methods @@ -51,9 +53,6 @@ # # 'portal_registration' interface methods # - security.declarePrivate('listActions') - def listActions(self, info): - return none security.declarePublic('isRegistrationAllowed') def isRegistrationAllowed(self, REQUEST): === CMF/CMFCore/SkinsTool.py 1.11 => 1.11.14.1 === security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( modifiedOptions() + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + modifiedOptions() + )) def __init__(self): self.selections = PersistentMapping() === CMF/CMFCore/TypesTool.py 1.26 => 1.26.14.1 === security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( OFS.Folder.Folder.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + OFS.Folder.Folder.manage_options + )) # # ZMI methods === CMF/CMFCore/UndoTool.py 1.4 => 1.4.14.1 === from string import split from AccessControl import ClassSecurityInfo - +from Expression import Expression +from ActionInformation import ActionInformation +from ActionProviderBase import ActionProviderBase from CMFCorePermissions import ManagePortal, UndoChanges, ListUndoableChanges -class UndoTool (UniqueObject, SimpleItem): +class UndoTool (UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_undo' meta_type = 'CMF Undo Tool' # This tool is used to undo changes. + _actions = [ActionInformation(id='undo' + , title='Undo' + , action=Expression( + text='string: ${portal_url}/undo_form') + , condition=Expression( + text='member') + , permissions=(ListUndoableChanges,) + , category='global' + , visible=1 + )] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ActionProviderBase.manage_options + + SimpleItem.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + SimpleItem.manage_options - + )) # # ZMI methods # @@ -46,14 +59,11 @@ manage_overview = DTMLFile( 'explainUndoTool', _dtmldir ) security.declarePrivate('listActions') - def listActions( self, info ): - if info.isAnonymous: - return [] - return [ { 'name': 'Undo' - , 'url': 'undo_form' - , 'permissions': [ ListUndoableChanges ] - , 'category': 'global' - } ] + def listActions(self, info=None): + """ + List actions available through tool + """ + return self._actions # # 'portal_undo' interface methods === CMF/CMFCore/WorkflowTool.py 1.19 => 1.19.4.1 === security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } - , { 'label' : 'Workflows' + manage_options = ( { 'label' : 'Workflows' , 'action' : 'manage_selectWorkflows' } + , { 'label' : 'Overview', 'action' : 'manage_overview' } ) + Folder.manage_options # From andrew@zope.com Thu Jan 3 00:13:56 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 2 Jan 2002 19:13:56 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.1.2.2 ActionProviderBase.py:1.1.2.2 ActionsTool.py:1.19.10.2 Message-ID: <200201030013.g030Dub01908@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv1238/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionInformation.py ActionProviderBase.py ActionsTool.py Log Message: *Cleanup of changeActions in ActionProviderbase *Removed tools from action_providers which do not have any default actions *Moved Join action to Registration Tool *Moved Reconfigure Portal to ProtertiesTool === CMF/CMFCore/ActionInformation.py 1.1.2.1 => 1.1.2.2 === is visible in the UI """ - if self.visible: - return self.visible - else: - return 1 + return self.visible security.declarePublic('getPriority') def getPriority(self): === CMF/CMFCore/ActionProviderBase.py 1.1.2.1 => 1.1.2.2 === else: a1['permission'] = '' - if not a.getCategory(): - a1['category'] = 'object' - else: - a1['category'] = a.getCategory() - if not a.getVisibility(): - a1['visible'] = 1 - else: - a1['visible'] = a.getVisibility() + a1['category'] = a.getCategory() or 'object' + a1['visible'] = a.getVisibility() if a._action: a1['action'] = a._action.text else: @@ -145,7 +139,7 @@ 'permissions': (properties.get('permission_' + s_idx, ()),), 'category': str(properties.get('category_' + s_idx, 'object')), - 'visible': not not properties.get('visible_' + s_idx, 0), + 'visible': properties.get('visible_' + s_idx, 0), } if not action['name']: raise ValueError('A name is required.') @@ -156,11 +150,10 @@ a._action = Expression(text=action['action']) else: a._action.text = action['action'] - if not a.condition: + if action['condition'] is not '': a.condition = Expression(text=action['condition']) else: - del(a.condition) - a.condition= Expression(text = action['condition']) + a.condition = '' a.permissions = action['permissions'] a.category = action['category'] a.visible = action['visible'] === CMF/CMFCore/ActionsTool.py 1.19.10.1 => 1.19.10.2 === action_providers = ('portal_membership' - , 'portal_memberdata' , 'portal_actions' , 'portal_registration' , 'portal_discussion' , 'portal_undo' , 'portal_syndication' - , 'portal_workflow') + , 'portal_workflow' + , 'portal_properties') security = ClassSecurityInfo() From andrew@zope.com Thu Jan 3 00:13:56 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 2 Jan 2002 19:13:56 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - MembershipTool.py:1.17.12.2 PropertiesTool.py:1.5.12.1 RegistrationTool.py:1.8.8.2 Message-ID: <200201030013.g030Dud01912@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv1238/CMFDefault Modified Files: Tag: andrew_ttw_actions-branch MembershipTool.py PropertiesTool.py RegistrationTool.py Log Message: *Cleanup of changeActions in ActionProviderbase *Removed tools from action_providers which do not have any default actions *Moved Join action to Registration Tool *Moved Reconfigure Portal to ProtertiesTool === CMF/CMFDefault/MembershipTool.py 1.17.12.1 => 1.17.12.2 === """ """ - _actions =[ActionInformation(id='join' - , title='Join' - , description='Click here to Join' - , action=Expression( - text='string: ${portal_url}/join_form') - , permissions=(View,) - , category='user' - , condition=Expression(text='not: member') - , visible=1 - ) - , ActionInformation(id='login' + _actions =[ActionInformation(id='login' , title='Login' , description='Click here to Login' , action=Expression( @@ -83,16 +73,6 @@ , permissions=(View,) , category='user' , condition=Expression(text='member') - , visible=1 - ) - , ActionInformation(id='configPortal' - , title='Reconfigure Portal' - , description='Reconfigure the portal' - , action=Expression( - text='string: ${portal_url}/reconfig_form') - , permissions=(ManagePortal,) - , category='global' - , condition=None , visible=1 ) , ActionInformation(id='addFavorite' === CMF/CMFDefault/PropertiesTool.py 1.5 => 1.5.12.1 === from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression from Products.CMFCore import CMFCorePermissions from utils import _dtmldir -class PropertiesTool (UniqueObject, SimpleItem): +class PropertiesTool(UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_properties' meta_type = 'Default Properties Tool' + _actions = [ActionInformation(id='configPortal' + , title='Reconfigure Portal' + , description='Reconfigure the portal' + , action=Expression( + text='string: ${portal_url}/reconfig_form') + , permissions=(CMFCorePermissions.ManagePortal,) + , category='global' + , condition=None + , visible=1 + )] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , ) + SimpleItem.manage_options + ) # # ZMI methods @@ -48,6 +63,13 @@ # # 'portal_properties' interface methods # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return actions provided by tool. + """ + return self._actions + security.declareProtected( CMFCorePermissions.ManagePortal , 'editProperties' ) def editProperties(self, props): === CMF/CMFDefault/RegistrationTool.py 1.8.8.1 => 1.8.8.2 === from Products.CMFCore.utils import UniqueObject from Products.CMFCore.utils import _checkPermission, getToolByName +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression from Products.CMFCore.ActionProviderBase import ActionProviderBase from Products.CMFCore.RegistrationTool import RegistrationTool @@ -31,7 +33,16 @@ class RegistrationTool (RegistrationTool, ActionProviderBase): meta_type = 'Default Registration Tool' - _actions = [] + _actions = [ActionInformation(id='join' + , title='Join' + , description='Click here to Join' + , action=Expression( + text='string: ${portal_url}/join_form') + , permissions=(CMFCorePermissions.View,) + , category='user' + , condition=Expression(text='not: member') + , visible=1 + )] security = ClassSecurityInfo() From andrew@zope.com Thu Jan 3 15:07:54 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 10:07:54 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionProviderBase.py:1.1.2.3 ActionsTool.py:1.19.10.3 SkinsTool.py:1.11.14.2 Message-ID: <200201031507.g03F7sa20162@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv19741/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionProviderBase.py ActionsTool.py SkinsTool.py Log Message: *Various logic and bug fixes to ActionsTool and ActionProviderBase *Added TTW Action support to SkinsTool and URLTool === CMF/CMFCore/ActionProviderBase.py 1.1.2.2 => 1.1.2.3 === a.id = action['id'] a.title = action['name'] - if not a._action: + if action['action'] is not '': a._action = Expression(text=action['action']) + #if not a._action: + # a._action = Expression(text=action['action']) else: - a._action.text = action['action'] + a._action = '' if action['condition'] is not '': a.condition = Expression(text=action['condition']) else: === CMF/CMFCore/ActionsTool.py 1.19.10.2 => 1.19.10.3 === """ #import pdb; pdb.set_trace() - providers = self.listActionProviders() + providers = list(self.listActionProviders()) new_providers = [] if add_provider: providers.append(apname) @@ -186,10 +186,11 @@ provider = getattr(self, provider_name) a = provider.listActions(info) #import pdb; pdb.set_trace() - if a and type(a[0]) is not {}: + if a and type(a[0]) is not type({}): ai_objs.extend(list(a)) - elif len(a) > 0: - actions.append(a) + else: + for i in a: + actions.append(i) if ai_objs: for ai in ai_objs: === CMF/CMFCore/SkinsTool.py 1.11.14.1 => 1.11.14.2 === from AccessControl import ClassSecurityInfo from CMFCorePermissions import ManagePortal, AccessContentsInformation +from ActionProviderBase import ActionProviderBase +from ActionInformation import ActionInformation +from Expression import Expression from OFS.Image import Image from OFS.DTMLMethod import DTMLMethod @@ -53,13 +56,14 @@ 'action':'manage_propertiesForm'}] return tuple(rval) -class SkinsTool(UniqueObject, SkinsContainer, PortalFolder): +class SkinsTool(UniqueObject, SkinsContainer, PortalFolder, ActionProviderBase): ''' This tool is used to supply skins to a portal. ''' id = 'portal_skins' meta_type = 'CMF Skins Tool' + _actions = [] cookie_persistence = 0 security = ClassSecurityInfo() @@ -67,7 +71,8 @@ manage_options = ( modifiedOptions() + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - )) + ) + ActionProviderBase.manage_options + ) def __init__(self): self.selections = PersistentMapping() @@ -90,6 +95,14 @@ request_varname = 'portal_skin' allow_any = 0 selections = None + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of actions information instances + provided by the tool. + """ + return self._actions security.declareProtected(ManagePortal, 'manage_propertiesForm') manage_propertiesForm = DTMLFile('dtml/skinProps', globals()) From andrew@zope.com Thu Jan 3 15:07:54 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 10:07:54 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - URLTool.py:1.7.12.2 Message-ID: <200201031507.g03F7sc20163@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv19741/CMFDefault Modified Files: Tag: andrew_ttw_actions-branch URLTool.py Log Message: *Various logic and bug fixes to ActionsTool and ActionProviderBase *Added TTW Action support to SkinsTool and URLTool === CMF/CMFDefault/URLTool.py 1.7.12.1 => 1.7.12.2 === from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression from Products.CMFCore import CMFCorePermissions from utils import _dtmldir -class URLTool (UniqueObject, SimpleItem): +class URLTool (UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_url' meta_type = 'Default URL Tool' + _actions = [] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , ) + SimpleItem.manage_options + ) # # ZMI methods @@ -55,6 +61,13 @@ Returns the absolute URL of the portal. ''' return aq_parent(aq_inner(self)).absolute_url(relative=relative) + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of actions provided via the tool + """ + return self._actions security.declarePublic( 'getPortalObject' ) def getPortalObject( self ): From andrew@zope.com Thu Jan 3 19:09:29 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 14:09:29 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.1.2.3 Expression.py:1.1.2.2 Message-ID: <200201031909.g03J9T112935@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv11908/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionInformation.py Expression.py Log Message: *Added new method getActionExpression to return the text attribute of an action. *Removed kruft from expression context. *Added start of unit tests === CMF/CMFCore/ActionInformation.py 1.1.2.2 => 1.1.2.3 === return action + security.declarePublic('getActionExpression') + def getActionExpression(self): + """ + If not an empty string or None, return the + text of the expression otherwise return '' + """ + if self._action: + return self._action.text + else: + return self._action + security.declarePublic('getCondition') def getCondition(self): """ - If not an empty string or None, evaluate the expression + If not an empty string or None, return the + text of the expression otherwise + return '' """ if self.condition: return self.condition.text === CMF/CMFCore/Expression.py 1.1.2.1 => 1.1.2.2 === 'portal_url': portal.absolute_url(), 'object': object, - 'content': object, 'folder': folder, 'portal': portal, 'nothing': None, From andrew@zope.com Thu Jan 3 19:09:29 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 14:09:29 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_ActionsTool.py:1.1.2.1 test_all.py:1.6.14.1 Message-ID: <200201031909.g03J9Tm12938@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv11908/CMFCore/tests Modified Files: Tag: andrew_ttw_actions-branch test_all.py Added Files: Tag: andrew_ttw_actions-branch test_ActionsTool.py Log Message: *Added new method getActionExpression to return the text attribute of an action. *Removed kruft from expression context. *Added start of unit tests === Added File CMF/CMFCore/tests/test_ActionsTool.py === import Zope import unittest import OFS.Folder, OFS.SimpleItem import Acquisition from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import noSecurityManager from AccessControl import SecurityManager from Products.CMFCore.ActionsTool import * from Products.CMFCore.ActionInformation import ActionInformation from Products.CMFCore.Expression import Expression from Products.CMFCore.PortalContent import PortalContent from Products.CMFCore.CMFCorePermissions import AddPortalContent, ManagePortal from Products.CMFCore.CMFCorePermissions import ModifyPortalContent from Products.CMFCore import utils import ZPublisher.HTTPRequest class PermissiveSecurityPolicy: """ Stub out the existing security policy for unit testing purposes. """ # # Standard SecurityPolicy interface # def validate( self , accessed=None , container=None , name=None , value=None , context=None , roles=None , *args , **kw): return 1 def checkPermission( self, permission, object, context) : if permission == 'forbidden permission': return 0 return 1 class OmnipotentUser( Acquisition.Implicit ): """ Stubbed out manager for unit testing purposes. """ def getId( self ): return 'all_powerful_Oz' getUserName = getId def allowed( self, object, object_roles=None ): return 1 class UserWithRoles( Acquisition.Implicit ): """ Stubbed out manager for unit testing purposes. """ def __init__( self, *roles ): self._roles = roles def getId( self ): return 'high_roller' getUserName = getId def allowed( self, object, object_roles=None ): for orole in object_roles: if orole in self._roles: return 1 return 0 class UnitTestUser( Acquisition.Implicit ): """ Stubbed out manager for unit testing purposes. """ def getId( self ): return 'unit_tester' getUserName = getId def allowed( self, object, object_roles=None ): # for testing permissions on actions if object.getId() == 'actions_dummy': if 'Anonymous' in object_roles: return 1 else: return 0 return 1 class ActionsToolTests( unittest.TestCase ): def setUp( self ): get_transaction().begin() self._policy = PermissiveSecurityPolicy() self._oldPolicy = SecurityManager.setSecurityPolicy(self._policy) self.connection = Zope.DB.open() root = self.root = self.connection.root()[ 'Application' ] newSecurityManager( None, UnitTestUser().__of__( self.root ) ) env = { 'SERVER_NAME' : 'http://localhost' , 'SERVER_PORT' : '80' } root.REQUEST = ZPublisher.HTTPRequest.HTTPRequest( None, env, None ) root._setObject( 'portal_actions', ActionsTool() ) tool = root.portal_actions tool.action_providers = ('portal_actions') self.assertEqual(tool.listActionProviders(), ('portal_actions',)) def tearDown( self ): get_transaction().abort() self.connection.close() noSecurityManager() SecurityManager.setSecurityPolicy(self._oldPolicy) class ActionInformationTests(unittest.TestCase): def test_basic_construction(self): ai = ActionInformation(id='view' ) self.assertEqual(ai.getId(), 'view') self.assertEqual(ai.Title(), 'view') self.assertEqual(ai.Description(), '') self.assertEqual(ai.getCondition(), '') self.assertEqual(ai.getActionExpression(), '') self.assertEqual(ai.getPermissions(), ()) def test_construction_with_Expressions(self): ai = ActionInformation(id='view' , title='View' , action=Expression( text='view') , condition=Expression( text='member')) self.assertEqual(ai.getId(), 'view') self.assertEqual(ai.Title(), 'View') self.assertEqual(ai.Description(), '') self.assertEqual(ai.getCondition(), 'member') self.assertEqual(ai.getActionExpression(), 'view') self.assertEqual(ai.getPermissions(), ()) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ActionsToolTests)) suite.addTest(unittest.makeSuite(ActionInformationTests)) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() === CMF/CMFCore/tests/test_all.py 1.6 => 1.6.14.1 === from Products.CMFCore.tests import test_PortalFolder from Products.CMFCore.tests import test_TypesTool +from Products.CMFCore.tests import test_ActionsTool from Products.CMFCore.tests import test_CatalogTool def test_suite(): @@ -10,6 +11,7 @@ suite.addTest( test_ContentTypeRegistry.test_suite() ) suite.addTest( test_PortalFolder.test_suite() ) suite.addTest( test_TypesTool.test_suite() ) + suite.addTest( test_ActionsTool.test_suite() ) suite.addTest( test_CatalogTool.test_suite() ) return suite From andrew@zope.com Thu Jan 3 19:46:09 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 14:46:09 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionProviderBase.py:1.1.2.4 CatalogTool.py:1.21.14.2 TypesTool.py:1.26.14.2 Message-ID: <200201031946.g03Jk9U21846@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv21742/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionProviderBase.py CatalogTool.py TypesTool.py Log Message: *changed ActionProviderBase to use getActionExpression method *Added TTW action support to CatalogTool and TypesTool which had been overlooked. === CMF/CMFCore/ActionProviderBase.py 1.1.2.3 => 1.1.2.4 === a1['visible'] = a.getVisibility() if a._action: - a1['action'] = a._action.text + a1['action'] = a.getActionExpression() else: a1['action'] = '' if a.condition: @@ -148,8 +148,6 @@ a.title = action['name'] if action['action'] is not '': a._action = Expression(text=action['action']) - #if not a._action: - # a._action = Expression(text=action['action']) else: a._action = '' if action['condition'] is not '': === CMF/CMFCore/CatalogTool.py 1.21.14.1 => 1.21.14.2 === from AccessControl import ClassSecurityInfo from utils import mergedLocalRoles +from ActionProviderBase import ActionProviderBase +from ActionInformation import ActionInformation +from Expression import Expression import os import CMFCorePermissions from Acquisition import aq_base @@ -64,14 +67,16 @@ return list(allowed.keys()) -class CatalogTool (UniqueObject, ZCatalog): +class CatalogTool (UniqueObject, ZCatalog, ActionProviderBase): '''This is a ZCatalog that filters catalog queries. ''' id = 'portal_catalog' meta_type = 'CMF Catalog' security = ClassSecurityInfo() + _actions = [] manage_options = ( ZCatalog.manage_options + + ActionProviderBase.manage_options + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , )) @@ -83,6 +88,14 @@ # # Subclass extension interface # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of action information instances + provided via tool + """ + return self._actions + security.declarePublic( 'enumerateIndexes' ) # Subclass can call def enumerateIndexes( self ): # Return a list of ( index_name, type ) pairs for the initial === CMF/CMFCore/TypesTool.py 1.26.14.1 => 1.26.14.2 === from Acquisition import aq_base import Products, CMFCorePermissions +from ActionProviderBase import ActionProviderBase +from ActionInformation import ActionInformation +from Expression import Expression from CMFCorePermissions import View, ManagePortal, AccessContentsInformation @@ -501,16 +504,18 @@ , ScriptableTypeInformation.meta_type ) -class TypesTool( UniqueObject, OFS.Folder.Folder ): +class TypesTool( UniqueObject, OFS.Folder.Folder, ActionProviderBase ): """ Provides a configurable registry of portal content types. """ id = 'portal_types' meta_type = 'CMF Types Tool' + _actions = [] security = ClassSecurityInfo() manage_options = ( OFS.Folder.Folder.manage_options + + ActionProviderBase.manage_options + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , )) @@ -521,6 +526,14 @@ security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) manage_overview = DTMLFile( 'explainTypesTool', _dtmldir ) + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of action information instances + for actions provided via tool + """ + return self._actions def all_meta_types(self): all = TypesTool.inheritedAttribute('all_meta_types')(self) From andrew@zope.com Thu Jan 3 20:38:18 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 15:38:18 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - MembershipTool.py:1.17.12.3 Message-ID: <200201032038.g03KcIV01414@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv1310/CMFDefault Modified Files: Tag: andrew_ttw_actions-branch MembershipTool.py Log Message: *Fixed a couple action expression errors. === CMF/CMFDefault/MembershipTool.py 1.17.12.2 => 1.17.12.3 === , title='my stuff' , description='Goto your home folder' - , action=Expression(text='member/getHomeUrl') + , action=Expression( + text='python: portal.portal_membership.getHomeUrl()') , permissions=(View,) , category='user' , condition=Expression( @@ -100,7 +101,7 @@ , title='My favorites' , description='Browser your favorites' , action=Expression( - text='python: member.getHomeUrl() + \'/Favorites/folder_contents\'') + text='python: portal.portal_membership.getHomeUrl() + \'/Favorites/folder_contents\'') , permissions=(View,) , category='user' , condition=Expression( From andrew@zope.com Thu Jan 3 22:43:18 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 17:43:18 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/interfaces - portal_actions.py:1.6.14.1 Message-ID: <200201032243.g03MhIr32610@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/interfaces In directory cvs.zope.org:/tmp/cvs-serv32579/CMFCore/interfaces Modified Files: Tag: andrew_ttw_actions-branch portal_actions.py Log Message: *Factored out ActionInformation tests from ActionTool tests *Fixed bug in ActionTool tests *updated test_all.py === CMF/CMFCore/interfaces/portal_actions.py 1.6 => 1.6.14.1 === # listActions__roles__ = () # No permission. def listActions(info): - '''Returns a list of mappings describing actions. Each action + '''Support for the old list of mappings is currently supported: + Returns a list of mappings describing actions. Each action should contain the keys "name", "url", "permissions", and "category", conforming to the specs outlined in portal_actions.listFilteredActionsFor(). The info argument @@ -85,5 +86,6 @@ folder_url content content_url + The new way of doing this is.... ''' From andrew@zope.com Thu Jan 3 22:43:18 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 17:43:18 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_ActionInformation.py:1.1.2.1 test_ActionsTool.py:1.1.2.2 test_all.py:1.6.14.2 Message-ID: <200201032243.g03MhIw32614@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv32579/CMFCore/tests Modified Files: Tag: andrew_ttw_actions-branch test_ActionsTool.py test_all.py Added Files: Tag: andrew_ttw_actions-branch test_ActionInformation.py Log Message: *Factored out ActionInformation tests from ActionTool tests *Fixed bug in ActionTool tests *updated test_all.py === Added File CMF/CMFCore/tests/test_ActionInformation.py === import unittest from Products.CMFCore.ActionsTool import * from Products.CMFCore.ActionInformation import ActionInformation from Products.CMFCore.Expression import Expression class ActionInformationTests(unittest.TestCase): def test_basic_construction(self): ai = ActionInformation(id='view' ) self.assertEqual(ai.getId(), 'view') self.assertEqual(ai.Title(), 'view') self.assertEqual(ai.Description(), '') self.assertEqual(ai.getCondition(), '') self.assertEqual(ai.getActionExpression(), '') self.assertEqual(ai.getPermissions(), ()) def test_construction_with_Expressions(self): ai = ActionInformation(id='view' , title='View' , action=Expression( text='view') , condition=Expression( text='member')) self.assertEqual(ai.getId(), 'view') self.assertEqual(ai.Title(), 'View') self.assertEqual(ai.Description(), '') self.assertEqual(ai.getCondition(), 'member') self.assertEqual(ai.getActionExpression(), 'view') self.assertEqual(ai.getPermissions(), ()) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ActionInformationTests)) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() === CMF/CMFCore/tests/test_ActionsTool.py 1.1.2.1 => 1.1.2.2 === from AccessControl import SecurityManager from Products.CMFCore.ActionsTool import * -from Products.CMFCore.ActionInformation import ActionInformation -from Products.CMFCore.Expression import Expression -from Products.CMFCore.PortalContent import PortalContent -from Products.CMFCore.CMFCorePermissions import AddPortalContent, ManagePortal +from Products.CMFCore.CMFCorePermissions import AddPortalContent from Products.CMFCore.CMFCorePermissions import ModifyPortalContent from Products.CMFCore import utils import ZPublisher.HTTPRequest @@ -37,36 +34,6 @@ return 0 return 1 -class OmnipotentUser( Acquisition.Implicit ): - """ - Stubbed out manager for unit testing purposes. - """ - def getId( self ): - return 'all_powerful_Oz' - - getUserName = getId - - def allowed( self, object, object_roles=None ): - return 1 - -class UserWithRoles( Acquisition.Implicit ): - """ - Stubbed out manager for unit testing purposes. - """ - def __init__( self, *roles ): - self._roles = roles - - def getId( self ): - return 'high_roller' - - getUserName = getId - - def allowed( self, object, object_roles=None ): - for orole in object_roles: - if orole in self._roles: - return 1 - return 0 - class UnitTestUser( Acquisition.Implicit ): """ Stubbed out manager for unit testing purposes. @@ -101,8 +68,11 @@ root.REQUEST = ZPublisher.HTTPRequest.HTTPRequest( None, env, None ) root._setObject( 'portal_actions', ActionsTool() ) - tool = root.portal_actions - tool.action_providers = ('portal_actions') + self.tool = tool = root.portal_actions + tool.action_providers = ('portal_actions',) + + def test_actionProviders(self): + tool = self.tool self.assertEqual(tool.listActionProviders(), ('portal_actions',)) def tearDown( self ): @@ -111,37 +81,9 @@ noSecurityManager() SecurityManager.setSecurityPolicy(self._oldPolicy) -class ActionInformationTests(unittest.TestCase): - - def test_basic_construction(self): - ai = ActionInformation(id='view' - ) - self.assertEqual(ai.getId(), 'view') - self.assertEqual(ai.Title(), 'view') - self.assertEqual(ai.Description(), '') - self.assertEqual(ai.getCondition(), '') - self.assertEqual(ai.getActionExpression(), '') - self.assertEqual(ai.getPermissions(), ()) - - def test_construction_with_Expressions(self): - ai = ActionInformation(id='view' - , title='View' - , action=Expression( - text='view') - , condition=Expression( - text='member')) - self.assertEqual(ai.getId(), 'view') - self.assertEqual(ai.Title(), 'View') - self.assertEqual(ai.Description(), '') - self.assertEqual(ai.getCondition(), 'member') - self.assertEqual(ai.getActionExpression(), 'view') - self.assertEqual(ai.getPermissions(), ()) - - def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ActionsToolTests)) - suite.addTest(unittest.makeSuite(ActionInformationTests)) return suite def run(): === CMF/CMFCore/tests/test_all.py 1.6.14.1 => 1.6.14.2 === from Products.CMFCore.tests import test_TypesTool from Products.CMFCore.tests import test_ActionsTool +from Products.CMFCore.tests import test_ActionInformation from Products.CMFCore.tests import test_CatalogTool def test_suite(): @@ -12,6 +13,7 @@ suite.addTest( test_PortalFolder.test_suite() ) suite.addTest( test_TypesTool.test_suite() ) suite.addTest( test_ActionsTool.test_suite() ) + suite.addTest( test_ActionInformation.test_suite() ) suite.addTest( test_CatalogTool.test_suite() ) return suite From andrew@zope.com Thu Jan 3 22:43:48 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 3 Jan 2002 17:43:48 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.19.10.4 Message-ID: <200201032243.g03MhmE00608@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv32579/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionsTool.py Log Message: *Factored out ActionInformation tests from ActionTool tests *Fixed bug in ActionTool tests *updated test_all.py === CMF/CMFCore/ActionsTool.py 1.19.10.3 => 1.19.10.4 === provider = getattr(self, provider_name) a = provider.listActions(info) - #import pdb; pdb.set_trace() if a and type(a[0]) is not type({}): ai_objs.extend(list(a)) else: From andrew@zope.com Fri Jan 4 14:30:21 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 09:30:21 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.19.10.5 Message-ID: <200201041430.g04EULr31466@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv31405/CMFCore Modified Files: Tag: andrew_ttw_actions-branch ActionsTool.py Log Message: *added a few more actions tool unit tests. *removed debugging kruft from ActionsTool === CMF/CMFCore/ActionsTool.py 1.19.10.4 => 1.19.10.5 === Manage TTW Action Providers """ - #import pdb; pdb.set_trace() providers = list(self.listActionProviders()) new_providers = [] if add_provider: From andrew@zope.com Fri Jan 4 14:30:21 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 09:30:21 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_ActionsTool.py:1.1.2.3 Message-ID: <200201041430.g04EULA31467@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv31405/CMFCore/tests Modified Files: Tag: andrew_ttw_actions-branch test_ActionsTool.py Log Message: *added a few more actions tool unit tests. *removed debugging kruft from ActionsTool === CMF/CMFCore/tests/test_ActionsTool.py 1.1.2.2 => 1.1.2.3 === from AccessControl import SecurityManager from Products.CMFCore.ActionsTool import * +from Products.CMFDefault.URLTool import * from Products.CMFCore.CMFCorePermissions import AddPortalContent from Products.CMFCore.CMFCorePermissions import ModifyPortalContent from Products.CMFCore import utils @@ -68,12 +69,26 @@ root.REQUEST = ZPublisher.HTTPRequest.HTTPRequest( None, env, None ) root._setObject( 'portal_actions', ActionsTool() ) - self.tool = tool = root.portal_actions - tool.action_providers = ('portal_actions',) + root._setObject('foo', URLTool() ) + self.tool = root.portal_actions + self.ut = root.foo + self.tool.action_providers = ('portal_actions',) def test_actionProviders(self): tool = self.tool self.assertEqual(tool.listActionProviders(), ('portal_actions',)) + + def test_addActionProvider(self): + tool = self.tool + tool.addActionProvider('foo') + self.assertEqual(tool.listActionProviders(), + ('portal_actions', 'foo')) + + def test_delActionProvider(self): + tool = self.tool + tool.deleteActionProvider('foo') + self.assertEqual(tool.listActionProviders(), + ('portal_actions',)) def tearDown( self ): get_transaction().abort() From andrew@zope.com Fri Jan 4 17:02:37 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 12:02:37 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.1.2.4 Message-ID: <200201041702.g04H2bX04226@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv3865 Modified Files: Tag: andrew_ttw_actions-branch ActionInformation.py Log Message: *more tests *test cleanup === CMF/CMFCore/ActionInformation.py 1.1.2.3 => 1.1.2.4 === """ Return integer priority for sorting + Not used....keep and implement or toss? """ if self.priority: return self.priority From andrew@zope.com Fri Jan 4 17:02:37 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 12:02:37 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_ActionInformation.py:1.1.2.2 test_ActionsTool.py:1.1.2.4 Message-ID: <200201041702.g04H2bS04228@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv3865/tests Modified Files: Tag: andrew_ttw_actions-branch test_ActionInformation.py test_ActionsTool.py Log Message: *more tests *test cleanup === CMF/CMFCore/tests/test_ActionInformation.py 1.1.2.1 => 1.1.2.2 === -from Products.CMFCore.ActionsTool import * +import Zope, OFS.SimpleItem, unittest +from Products.CMFDefault.MembershipTool import * +from Products.CMFCore.PortalContent import PortalContent from Products.CMFCore.ActionInformation import ActionInformation -from Products.CMFCore.Expression import Expression +from Products.CMFCore.Expression import Expression, createExprContext + +class DummyMembershipTool: + def isAnonymousUser(self): + return 1 + +class DummyContent(PortalContent, OFS.SimpleItem.Item): + """ + """ + meta_type = 'Dummy' + url = 'foo_url' + + def __init__(self, id, url=None): + self.url = url + + def absolute_url(self): + return self.url class ActionInformationTests(unittest.TestCase): + def setUp( self ): + get_transaction().begin() + self.connection = Zope.DB.open() + root = self.root = self.connection.root()[ 'Application' ] + root._setObject('portal', DummyContent('portal', 'url_portal')) + portal = self.portal = self.root.portal + portal.portal_membership = DummyMembershipTool() + self.folder = DummyContent('foo', 'url_foo') + self.object = DummyContent('bar', 'url_bar') + def test_basic_construction(self): ai = ActionInformation(id='view' ) @@ -13,6 +40,8 @@ self.assertEqual(ai.Description(), '') self.assertEqual(ai.getCondition(), '') self.assertEqual(ai.getActionExpression(), '') + self.assertEqual(ai.getVisibility(), 1) + self.assertEqual(ai.getCategory(), 'object') self.assertEqual(ai.getPermissions(), ()) def test_construction_with_Expressions(self): @@ -21,13 +50,36 @@ , action=Expression( text='view') , condition=Expression( - text='member')) + text='member') + , category='global' + , visible=0) self.assertEqual(ai.getId(), 'view') self.assertEqual(ai.Title(), 'View') self.assertEqual(ai.Description(), '') self.assertEqual(ai.getCondition(), 'member') self.assertEqual(ai.getActionExpression(), 'view') + self.assertEqual(ai.getVisibility(), 0) + self.assertEqual(ai.getCategory(), 'global') self.assertEqual(ai.getPermissions(), ()) + + def test_Condition(self): + portal = self.portal + folder = self.folder + object = self.object + ai = ActionInformation(id='view' + , title='View' + , action=Expression( + text='view') + , condition=Expression( + text='member') + , category='global' + , visible=1) + ec = createExprContext(folder, portal, object) + self.failIf(ai.testCondition(ec)) + + def tearDown( self ): + get_transaction().abort() + self.connection.close() def test_suite(): === CMF/CMFCore/tests/test_ActionsTool.py 1.1.2.3 => 1.1.2.4 === -import unittest -import OFS.Folder, OFS.SimpleItem -import Acquisition -from AccessControl.SecurityManagement import newSecurityManager -from AccessControl.SecurityManagement import noSecurityManager -from AccessControl import SecurityManager +import Zope, unittest from Products.CMFCore.ActionsTool import * from Products.CMFDefault.URLTool import * -from Products.CMFCore.CMFCorePermissions import AddPortalContent -from Products.CMFCore.CMFCorePermissions import ModifyPortalContent -from Products.CMFCore import utils import ZPublisher.HTTPRequest -class PermissiveSecurityPolicy: - """ - Stub out the existing security policy for unit testing purposes. - """ - # - # Standard SecurityPolicy interface - # - def validate( self - , accessed=None - , container=None - , name=None - , value=None - , context=None - , roles=None - , *args - , **kw): - return 1 - - def checkPermission( self, permission, object, context) : - if permission == 'forbidden permission': - return 0 - return 1 - -class UnitTestUser( Acquisition.Implicit ): - """ - Stubbed out manager for unit testing purposes. - """ - def getId( self ): - return 'unit_tester' - - getUserName = getId - - def allowed( self, object, object_roles=None ): - # for testing permissions on actions - if object.getId() == 'actions_dummy': - if 'Anonymous' in object_roles: - return 1 - else: - return 0 - return 1 - class ActionsToolTests( unittest.TestCase ): def setUp( self ): get_transaction().begin() - self._policy = PermissiveSecurityPolicy() - self._oldPolicy = SecurityManager.setSecurityPolicy(self._policy) self.connection = Zope.DB.open() root = self.root = self.connection.root()[ 'Application' ] - newSecurityManager( None, UnitTestUser().__of__( self.root ) ) env = { 'SERVER_NAME' : 'http://localhost' , 'SERVER_PORT' : '80' @@ -93,8 +40,6 @@ def tearDown( self ): get_transaction().abort() self.connection.close() - noSecurityManager() - SecurityManager.setSecurityPolicy(self._oldPolicy) def test_suite(): suite = unittest.TestSuite() From andrew@zope.com Fri Jan 4 19:40:28 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 14:40:28 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_Expression.py:1.1.2.1 test_all.py:1.6.14.3 Message-ID: <200201041940.g04JeSH08786@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv8777/CMFCore/tests Modified Files: Tag: andrew_ttw_actions-branch test_all.py Added Files: Tag: andrew_ttw_actions-branch test_Expression.py Log Message: *added Expression tests === Added File CMF/CMFCore/tests/test_Expression.py === import Zope, unittest, OFS.SimpleItem, Acquisition from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import noSecurityManager from AccessControl import SecurityManager from Products.CMFCore.ActionsTool import * from Products.CMFCore.ActionInformation import ActionInformation from Products.CMFCore.PortalContent import PortalContent from Products.CMFCore.Expression import Expression, createExprContext import ZPublisher.HTTPRequest class PermissiveSecurityPolicy: """ Stub out the existing security policy for unit testing purposes. """ # # Standard SecurityPolicy interface # def validate( self , accessed=None , container=None , name=None , value=None , context=None , roles=None , *args , **kw): return 1 def checkPermission( self, permission, object, context) : if permission == 'forbidden permission': return 0 return 1 class UnitTestUser( Acquisition.Implicit ): """ Stubbed out manager for unit testing purposes. """ def getId( self ): return 'unit_tester' getUserName = getId def allowed( self, object, object_roles=None ): # for testing permissions on actions if object.getId() == 'actions_dummy': if 'Anonymous' in object_roles: return 1 else: return 0 return 1 class DummyMembershipTool: def __init__(self, anon=1): self.anon = anon def isAnonymousUser(self): return self.anon def getAuthenticatedMember(self): return "member" class DummyContent(PortalContent, OFS.SimpleItem.Item): """ """ meta_type = 'Dummy' url = 'foo_url' def __init__(self, id, url=None): self.id = id self.url = url def absolute_url(self): return self.url class ExpressionTests( unittest.TestCase ): def setUp( self ): get_transaction().begin() self._policy = PermissiveSecurityPolicy() self._oldPolicy = SecurityManager.setSecurityPolicy(self._policy) self.connection = Zope.DB.open() root = self.root = self.connection.root()[ 'Application' ] newSecurityManager(None, UnitTestUser().__of__( self.root )) root._setObject('portal', DummyContent('portal', 'url_portal')) portal = self.portal = self.root.portal self.folder = DummyContent('foo', 'url_foo') self.object = DummyContent('bar', 'url_bar') self.ai = ActionInformation(id='view' , title='View' , action=Expression( text='view') , condition=Expression( text='member') , category='global' , visible=1) def test_anonymous_ec(self): self.portal.portal_membership = DummyMembershipTool() ec = createExprContext(self.folder, self.portal, self.object) member = ec.global_vars['member'] self.failIf(member) def test_authenticatedUser_ec(self): self.portal.portal_membership = DummyMembershipTool(anon=0) ec = createExprContext(self.folder, self.portal, self.object) member = ec.global_vars['member'] self.assertEqual(member, 'member') def test_ec_context(self): self.portal.portal_membership = DummyMembershipTool() ec = createExprContext(self.folder, self.portal, self.object) object = ec.global_vars['object'] portal = ec.global_vars['portal'] folder = ec.global_vars['folder'] self.failUnless(object) self.assertEqual(object.id, 'bar') self.assertEqual(object.absolute_url(), 'url_bar') self.failUnless(portal) self.assertEqual(portal.id, 'portal') self.assertEqual(portal.absolute_url(), 'url_portal') self.failUnless(folder) self.assertEqual(folder.id, 'foo') self.assertEqual(folder.absolute_url(), 'url_foo') def tearDown( self ): get_transaction().abort() self.connection.close() noSecurityManager() SecurityManager.setSecurityPolicy(self._oldPolicy) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ExpressionTests)) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() === CMF/CMFCore/tests/test_all.py 1.6.14.2 => 1.6.14.3 === from Products.CMFCore.tests import test_ActionsTool from Products.CMFCore.tests import test_ActionInformation +from Products.CMFCore.tests import test_Expression from Products.CMFCore.tests import test_CatalogTool def test_suite(): @@ -14,6 +15,7 @@ suite.addTest( test_TypesTool.test_suite() ) suite.addTest( test_ActionsTool.test_suite() ) suite.addTest( test_ActionInformation.test_suite() ) + suite.addTest( test_Expression.test_suite() ) suite.addTest( test_CatalogTool.test_suite() ) return suite From andrew@zope.com Fri Jan 4 19:50:05 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 14:50:05 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - DiscussionTool.py:1.7 MembershipTool.py:1.18 MetadataTool.py:1.10 PropertiesTool.py:1.6 RegistrationTool.py:1.9 SyndicationTool.py:1.10 URLTool.py:1.8 Message-ID: <200201041950.g04Jo5511156@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv10417/CMFDefault Modified Files: DiscussionTool.py MembershipTool.py MetadataTool.py PropertiesTool.py RegistrationTool.py SyndicationTool.py URLTool.py Log Message: *Merging andrew_ttw_actions-branch into the head *Adds TTW configuration of Action Providers *Allows tools to have actions managed TTW *Added unittests for ActionTool, ActionInformation, and Expression *Added TTW action management to all stock CMF tools *Added Expression module for allowing actions and conditions of actions to be TALES. === CMF/CMFDefault/DiscussionTool.py 1.6 => 1.7 === from utils import _dtmldir from DiscussionItem import DiscussionItemContainer +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.Expression import Expression class DiscussionNotAllowed( Exception ): pass -class DiscussionTool( UniqueObject, SimpleItem ): +class DiscussionTool( UniqueObject, SimpleItem, ActionProviderBase ): id = 'portal_discussion' meta_type = 'Default Discussion Tool' + _actions = [ActionInformation(id='reply' + , title='Reply' + , action=Expression( + text='string: ${object_url}/discussion_reply_form') + , condition=Expression( + text='python: object is not None and ' + + 'portal.portal_discussion.isDiscussionAllowedFor(object)') + , permissions=('Reply to item',) + , category='object' + , visible=1 + )] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = (ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + SimpleItem.manage_options + ) + SimpleItem.manage_options) # # ZMI methods @@ -52,6 +67,13 @@ # 'portal_discussion' interface methods # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return available actions via tool. + """ + return self._actions + security.declarePublic( 'overrideDiscussionFor' ) def overrideDiscussionFor(self, content, allowDiscussion): """ @@ -96,29 +118,6 @@ if typeInfo: return typeInfo.allowDiscussion() return 0 - - # - # ActionProvider interface - # - security.declarePrivate( 'listActions' ) - def listActions(self, info): - # Return actions for reply and show replies - content = info.content - if content is None or not self.isDiscussionAllowedFor(content): - return None - - discussion = self.getDiscussionFor(content) - discussion_url = info.content_url - - actions = ( - {'name': 'Reply', - 'url': discussion_url + '/discussion_reply_form', - 'permissions': ['Reply to item'], - 'category': 'object' - }, - ) - - return actions # # Utility methods === CMF/CMFDefault/MembershipTool.py 1.17 => 1.18 === from Products.CMFCore.utils import _getAuthenticatedUser, _checkPermission from Products.CMFCore.utils import getToolByName +from Products.CMFCore.ActionsTool import ActionInformation import Products.CMFCore.MembershipTool from Products.CMFCore.PortalFolder import manage_addPortalFolder import Document from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo +from Products.CMFCore.Expression import Expression from Products.CMFCore.CMFCorePermissions import View, AccessContentsInformation from Products.CMFCore.CMFCorePermissions import ListPortalMembers, AddPortalMember from Products.CMFCore.CMFCorePermissions import ManagePortal @@ -40,10 +42,73 @@ in the Tool Box on the left. ''' - class MembershipTool ( Products.CMFCore.MembershipTool.MembershipTool ): """ """ + _actions =[ActionInformation(id='login' + , title='Login' + , description='Click here to Login' + , action=Expression( + text='string: ${portal_url}/login_form') + , permissions=(View,) + , category='user' + , condition=Expression(text='not: member') + , visible=1 + ) + , ActionInformation(id='preferences' + , title='Preferences' + , description='Change your user preferences' + , action=Expression( + text='string: ${portal_url}/personalize_form') + , permissions=(View,) + , category='user' + , condition=Expression(text='member') + , visible=1 + ) + , ActionInformation(id='logout' + , title='Log out' + , description='Click here to logout' + , action=Expression( + text='string: ${portal_url}/logout') + , permissions=(View,) + , category='user' + , condition=Expression(text='member') + , visible=1 + ) + , ActionInformation(id='addFavorite' + , title='Add to favorites' + , description='Add this item to your favorites' + , action=Expression( + text='string: ${portal_url}/addtoFavorites') + , permissions=(View,) + , category='user' + , condition=Expression( + text='python: portal.portal_membership.getHomeFolder()') + , visible=1 + ) + , ActionInformation(id='mystuff' + , title='my stuff' + , description='Goto your home folder' + , action=Expression( + text='python: portal.portal_membership.getHomeUrl()') + , permissions=(View,) + , category='user' + , condition=Expression( + text='python: member and portal.portal_membership.getHomeFolder()') + , visible=1 + ) + , ActionInformation(id='favorites' + , title='My favorites' + , description='Browser your favorites' + , action=Expression( + text='python: portal.portal_membership.getHomeUrl() + \'/Favorites/folder_contents\'') + , permissions=(View,) + , category='user' + , condition=Expression( + text='python: member and hasattr(portal.portal_membership.getHomeFolder(), \'Favorites\')') + , visible=1 + ) + ] meta_type = 'Default Membership Tool' @@ -151,63 +216,8 @@ return None security.declarePrivate( 'listActions' ) - def listActions(self, info): - '''Lists actions available to the user.''' - user_actions = None - portal_url = info.portal_url - if info.isAnonymous: - user_actions = ( - {'name': 'Log in', - 'url': portal_url + '/login_form', - 'permissions': [], - 'category': 'user'}, - {'name': 'Join', - 'url': portal_url + '/join_form', - 'permissions': [AddPortalMember], - 'category': 'user'}, - ) - - if not info.isAnonymous: - home_folder = self.getHomeFolder() - homeUrl = self.getHomeUrl() - user_actions = ( - {'name': 'Preferences', - 'url': portal_url + '/personalize_form', - 'permissions': [], - 'category': 'user'}, - {'name': 'Log out', - 'url': portal_url + '/logout', - 'permissions' : [], - 'category': 'user'}, - {'name': 'Reconfigure portal', - 'url': portal_url + '/reconfig_form', - 'permissions': ['Manage portal'], - 'category': 'global'}, - ) - - if homeUrl is not None: - content_url = info.content_url - actions = ( - {'name': 'Add to Favorites', - 'url': ( content_url + '/addtoFavorites' ), - 'permissions' : [], - 'category': 'user'}, - {'name': 'My Stuff', - 'url': homeUrl + '/folder_contents', - 'permissions': [], - 'category': 'user'}, - ) - user_actions = user_actions + actions - - if hasattr( home_folder, 'Favorites' ): - added_actions = ( - {'name': 'My Favorites', - 'url' : homeUrl + '/Favorites/folder_contents', - 'permissions': [], - 'category': 'user'},) - user_actions = user_actions + added_actions - - return user_actions - + def listActions(self, info=None): + '''Lists actions available through the tool.''' + return self._actions InitializeClass(MembershipTool) === CMF/CMFDefault/MetadataTool.py 1.9 => 1.10 === from AccessControl import ClassSecurityInfo, getSecurityManager from Products.CMFCore import CMFCorePermissions +from Products.CMFCore.ActionProviderBase import ActionProviderBase from utils import _dtmldir class MetadataElementPolicy( Persistent ): @@ -190,11 +191,13 @@ class MetadataError( Exception ): pass -class MetadataTool( UniqueObject, SimpleItem ): +class MetadataTool( UniqueObject, SimpleItem, ActionProviderBase ): id = 'portal_metadata' meta_type = 'Default Metadata Tool' + _actions = [] + security = ClassSecurityInfo() # @@ -225,7 +228,8 @@ # # ZMI methods # - manage_options = ( ( { 'label' : 'Overview' + manage_options = ( ActionProviderBase.manage_options + + ( { 'label' : 'Overview' , 'action' : 'manage_overview' } , { 'label' : 'Properties' @@ -248,6 +252,13 @@ security.declareProtected( CMFCorePermissions.ManagePortal , 'propertiesForm' ) propertiesForm = DTMLFile( 'metadataProperties', _dtmldir ) + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return actions provided via tool. + """ + return self._actions security.declareProtected( CMFCorePermissions.ManagePortal , 'editProperties' ) === CMF/CMFDefault/PropertiesTool.py 1.5 => 1.6 === from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression from Products.CMFCore import CMFCorePermissions from utils import _dtmldir -class PropertiesTool (UniqueObject, SimpleItem): +class PropertiesTool(UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_properties' meta_type = 'Default Properties Tool' + _actions = [ActionInformation(id='configPortal' + , title='Reconfigure Portal' + , description='Reconfigure the portal' + , action=Expression( + text='string: ${portal_url}/reconfig_form') + , permissions=(CMFCorePermissions.ManagePortal,) + , category='global' + , condition=None + , visible=1 + )] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , ) + SimpleItem.manage_options + ) # # ZMI methods @@ -48,6 +63,13 @@ # # 'portal_properties' interface methods # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return actions provided by tool. + """ + return self._actions + security.declareProtected( CMFCorePermissions.ManagePortal , 'editProperties' ) def editProperties(self, props): === CMF/CMFDefault/RegistrationTool.py 1.8 => 1.9 === from Products.CMFCore.utils import UniqueObject from Products.CMFCore.utils import _checkPermission, getToolByName +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression +from Products.CMFCore.ActionProviderBase import ActionProviderBase from Products.CMFCore.RegistrationTool import RegistrationTool from Globals import InitializeClass, DTMLFile @@ -27,9 +30,20 @@ from Products.CMFCore import CMFCorePermissions from utils import _dtmldir -class RegistrationTool (RegistrationTool): +class RegistrationTool (RegistrationTool, ActionProviderBase): meta_type = 'Default Registration Tool' + _actions = [ActionInformation(id='join' + , title='Join' + , description='Click here to Join' + , action=Expression( + text='string: ${portal_url}/join_form') + , permissions=(CMFCorePermissions.View,) + , category='user' + , condition=Expression(text='not: member') + , visible=1 + )] + security = ClassSecurityInfo() # @@ -37,6 +51,11 @@ # security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) + + manage_options = ( ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } + , + )) manage_overview = DTMLFile( 'explainRegistrationTool', _dtmldir ) # @@ -55,10 +74,11 @@ return None security.declarePublic('listActions') - def listActions(self, info): + def listActions(self, info=None): """ + Return actions provided via tool. """ - return None + return self._actions security.declarePublic( 'testPropertiesValidity' ) def testPropertiesValidity(self, props, member=None): === CMF/CMFDefault/SyndicationTool.py 1.9 => 1.10 === from Products.CMFCore.CMFCorePermissions import ManageProperties from Products.CMFCore.CMFCorePermissions import AccessContentsInformation +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression import Products.CMFCore.CMFCorePermissions from Products.CMFCore.PortalFolder import PortalFolder from SyndicationInfo import SyndicationInformation _dtmldir = os.path.join( package_home( globals() ), 'dtml' ) -class SyndicationTool (UniqueObject, SimpleItem): +class SyndicationTool (UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_syndication' meta_type = 'Default Syndication Tool' + _actions = [ActionInformation(id='syndication' + , title='Syndication' + , action=Expression( + text='string: ${folder_url}/synPropertiesForm') + , condition=Expression( + text='python: folder is object') + , permissions=(ManageProperties,) + , category='folder' + , visible=1 + )] + security = ClassSecurityInfo() #Default Sitewide Values @@ -46,7 +60,8 @@ max_items = 15 #ZMI Methods - manage_options = (({'label' : 'Overview' + manage_options = (ActionProviderBase.manage_options + + ({'label' : 'Overview' ,'action' : 'overview' , 'help' : ('CMFDefault', 'Syndication-Tool_Overview.stx') } ,{'label' : 'Properties' @@ -69,6 +84,13 @@ security.declareProtected(ManagePortal, 'reportForm') reportForm = HTMLFile('synReports', _dtmldir) + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return actions provided by tool + """ + return self._actions + security.declareProtected(ManagePortal, 'editProperties') def editProperties(self , updatePeriod=None === CMF/CMFDefault/URLTool.py 1.7 => 1.8 === from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo +from Products.CMFCore.ActionProviderBase import ActionProviderBase +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression from Products.CMFCore import CMFCorePermissions from utils import _dtmldir -class URLTool (UniqueObject, SimpleItem): +class URLTool (UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_url' meta_type = 'Default URL Tool' + _actions = [] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , ) + SimpleItem.manage_options + ) # # ZMI methods @@ -55,6 +61,13 @@ Returns the absolute URL of the portal. ''' return aq_parent(aq_inner(self)).absolute_url(relative=relative) + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of actions provided via the tool + """ + return self._actions security.declarePublic( 'getPortalObject' ) def getPortalObject( self ): From andrew@zope.com Fri Jan 4 19:50:35 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 14:50:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionInformation.py:1.2 ActionProviderBase.py:1.2 Expression.py:1.2 ActionsTool.py:1.20 CatalogTool.py:1.22 MemberDataTool.py:1.13 MembershipTool.py:1.17 PortalFolder.py:1.29 RegistrationTool.py:1.8 SkinsTool.py:1.12 TypesTool.py:1.27 UndoTool.py:1.5 WorkflowTool.py:1.20 Message-ID: <200201041950.g04JoZP11172@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv10417/CMFCore Modified Files: ActionsTool.py CatalogTool.py MemberDataTool.py MembershipTool.py PortalFolder.py RegistrationTool.py SkinsTool.py TypesTool.py UndoTool.py WorkflowTool.py Added Files: ActionInformation.py ActionProviderBase.py Expression.py Log Message: *Merging andrew_ttw_actions-branch into the head *Adds TTW configuration of Action Providers *Allows tools to have actions managed TTW *Added unittests for ActionTool, ActionInformation, and Expression *Added TTW action management to all stock CMF tools *Added Expression module for allowing actions and conditions of actions to be TALES. === CMF/CMFCore/ActionInformation.py 1.1 => 1.2 === +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 +# +############################################################################## + +"""Basic action list tool. + +$Id$ +""" +__version__='$Revision$'[11:-2] + +from utils import SimpleItemWithProperties, _dtmldir, getToolByName +import CMFCorePermissions +from AccessControl import ClassSecurityInfo +from Acquisition import aq_inner, aq_parent +from Globals import InitializeClass, DTMLFile + +class ActionInformation(SimpleItemWithProperties): + """ + Represent a single action which the user can select from a list + and execut in some context. + """ + _isActionInformation = 1 + __allow_access_to_unprotected_subobjects__ = 1 + + manage_options = (SimpleItemWithProperties.manage_options[:1] + + ({'label': 'Actions', + 'action': 'manage_editActionsForm'},) + + SimpleItemWithProperties.manage_options[1:]) + security = ClassSecurityInfo() + security.declareProtected(CMFCorePermissions.ManagePortal + , 'manage_editProperties' + , 'manage_changeProperties' + , 'manage_propertiesForm' + ) + + _basic_properties = ( + {'id': 'title', 'type': 'string', 'mode': 'w', 'label': 'Title'} + , {'id': 'description', 'type': 'text', 'mode': 'w', + 'label': 'Description'} + , {'id': 'category', 'type': 'string', 'mode': 'w', + 'label': 'Category'} + , {'id': 'priority', 'type': 'boolean', 'mode': 'w', 'label': 'Priority'} + ) + + title = '' + description = '' + category = '' + priority = 0 + visible = 1 + _action = '' + + def __init__(self + , id + , title='' + , description='' + , category='object' + , condition='' + , permissions=() + , priority=10 + , visible=1 + , action=''): + """ + Setup an instance + """ + self.id = id + self.title = title + self.description = description + self.category = category + self.condition = condition + self.permissions = permissions + self.priority = priority + self.visible = visible + self._action = action + + + security.declareProtected(CMFCorePermissions.View, 'Title') + def Title(self): + """ + Return the Action title - name + """ + if self.title: + return self.title + else: + return self.getId() + + security.declareProtected(CMFCorePermissions.View, 'Description') + def Description(self): + """ + Return a description of the action + """ + return self.description + + security.declarePrivate('testCondition') + def testCondition(self, ec): + """ + Evaluate condition and return 0 or 1 + """ + if self.condition: + return self.condition(ec) + else: + return 1 + + security.declarePublic('getAction') + def getAction(self, ec): + """ + Return the action, which is an TALES expresssion + """ + if self._action: + aa = self._action(ec) + else: + aa = '' + action = {} + action['id'] = self.id + action['name'] = self.Title() + action['url'] = aa + action['permissions'] = self.getPermissions() + action['category'] = self.getCategory() + action['visible'] = self.getVisibility() + return action + + security.declarePublic('getActionExpression') + def getActionExpression(self): + """ + If not an empty string or None, return the + text of the expression otherwise return '' + """ + if self._action: + return self._action.text + else: + return self._action + + security.declarePublic('getCondition') + def getCondition(self): + """ + If not an empty string or None, return the + text of the expression otherwise + return '' + """ + if self.condition: + return self.condition.text + else: + return self.condition + + security.declarePublic('getPermission') + def getPermissions(self): + """ + Return the permission if any required for a user to + execute the action + """ + return self.permissions + + security.declarePublic('getCategory') + def getCategory(self): + """ + Return the category for which the action is + """ + if self.category: + return self.category + else: + return 'object' + + security.declarePublic('getVisibility') + def getVisibility(self): + """ + Return boolean for whether the action + is visible in the UI + """ + return self.visible + + security.declarePublic('getPriority') + def getPriority(self): + """ + Return integer priority for sorting + Not used....keep and implement or toss? + """ + if self.priority: + return self.priority + else: + return 10 + +InitializeClass(ActionInformation) + +class oai: + #Provided for backwards compatability + # Provides information that may be needed when constructing the list of + # available actions. + __allow_access_to_unprotected_subobjects__ = 1 + + def __init__(self, tool, folder, object=None): + self.portal = portal = aq_parent(aq_inner(tool)) + membership = getToolByName(tool, 'portal_membership') + self.isAnonymous = membership.isAnonymousUser() + self.portal_url = portal.absolute_url() + if folder is not None: + self.folder_url = folder.absolute_url() + self.folder = folder + else: + self.folder_url = self.portal_url + self.folder = portal + self.content = object + if object is not None: + self.content_url = object.absolute_url() + else: + self.content_url = None + + def __getitem__(self, name): + # Mapping interface for easy string formatting. + if name[:1] == '_': + raise KeyError, name + if hasattr(self, name): + return getattr(self, name) + raise KeyError, name + === CMF/CMFCore/ActionProviderBase.py 1.1 => 1.2 === +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## + +from OFS.SimpleItem import SimpleItem +from Globals import DTMLFile +from CMFCorePermissions import ManagePortal +from utils import _dtmldir, cookString +from AccessControl import ClassSecurityInfo +from ActionInformation import ActionInformation +from Expression import Expression + + +"""Basic action list tool. + +$Id$ +""" +__version__='$Revision$'[11:-2] + +class ActionProviderBase: + """ + Provide ActionTabs and management methods for ActionProviders + """ + _actions = [] + security = ClassSecurityInfo() + _actions_form = DTMLFile( 'editToolsActions', _dtmldir ) + + manage_options = ({ 'label' : 'Actions', 'action' : 'manage_editActionsForm' } + , + ) + + security.declarePrivate('listActions') + def listActions(self): + """ + Return all the actions defined by a tool + """ + if self._actions: + return self._actions + else: + return None + + security.declareProtected(ManagePortal, 'manage_editActionsForm') + def manage_editActionsForm(self, REQUEST, manage_tabs_message=None): + """ + Shows the 'Actions' management tab. + """ + actions = [] + if self.listActions() is not None: + for a in self.listActions(): + a1 = {} + a1['id'] = a.getId() + a1['name'] = a.Title() + p = a.getPermissions() + if p: + a1['permission'] = p[0] + else: + a1['permission'] = '' + a1['category'] = a.getCategory() or 'object' + a1['visible'] = a.getVisibility() + if a._action: + a1['action'] = a.getActionExpression() + else: + a1['action'] = '' + if a.condition: + a1['condition'] = a.getCondition() + else: + a1['condition'] = '' + actions.append(a1) + # possible_permissions is in AccessControl.Role.RoleManager. + pp = self.possible_permissions() + return self._actions_form(self, REQUEST, + actions=actions, + possible_permissions=pp, + management_view='Actions', + manage_tabs_message=manage_tabs_message) + + security.declareProtected(ManagePortal, 'addAction') + def addAction( self + , id + , name + , action + , condition + , permission + , category + , visible=1 + , REQUEST=None + ): + """ + Adds an action to the list. + """ + al = self._actions + if not name: + raise ValueError('A name is required.') + if action: + a_expr = Expression(text=str(action)) + else: + a_expr = '' + if condition: + c_expr = Expression(text=str(condition)) + else: + c_expr = '' + al.append(ActionInformation(id=str(id) + , title=str(name) + , action=a_expr + , condition=c_expr + , permissions=(str(permission),) + , category=str(category) + , visible=int(visible) + )) + self._actions = al + if REQUEST is not None: + return self.manage_editActionsForm( + REQUEST, manage_tabs_message='Added.') + + security.declareProtected(ManagePortal, 'changeActions') + def changeActions(self, properties=None, REQUEST=None): + """ + Changes the _actions. + """ + if properties is None: + properties = REQUEST + actions = [] + for idx in range(len(self._actions)): + s_idx = str(idx) + action = { + 'id': str(properties.get('id_' + s_idx, '')), + 'name': str(properties.get('name_' + s_idx, '')), + 'action': str(properties.get('action_' + s_idx, '')), + 'condition': str(properties.get('condition_' + s_idx, '')), + 'permissions': + (properties.get('permission_' + s_idx, ()),), + 'category': str(properties.get('category_' + s_idx, 'object')), + 'visible': properties.get('visible_' + s_idx, 0), + } + if not action['name']: + raise ValueError('A name is required.') + a = self._actions[idx] + a.id = action['id'] + a.title = action['name'] + if action['action'] is not '': + a._action = Expression(text=action['action']) + else: + a._action = '' + if action['condition'] is not '': + a.condition = Expression(text=action['condition']) + else: + a.condition = '' + a.permissions = action['permissions'] + a.category = action['category'] + a.visible = action['visible'] + if REQUEST is not None: + return self.manage_editActionsForm(REQUEST, manage_tabs_message= + 'Actions changed.') + + security.declareProtected(ManagePortal, 'deleteActions') + def deleteActions(self, selections=(), REQUEST=None): + """ + Deletes actions. + """ + actions = list(self._actions) + sels = list(map(int, selections)) # Convert to a list of integers. + sels.sort() + sels.reverse() + for idx in sels: + del actions[idx] + self._actions = actions + if REQUEST is not None: + return self.manage_editActionsForm( + REQUEST, manage_tabs_message=( + 'Deleted %d action(s).' % len(sels))) + + security.declareProtected(ManagePortal, 'moveUpActions') + def moveUpActions(self, selections=(), REQUEST=None): + """ + Moves the specified actions up one slot. + """ + actions = list(self._actions) + sels = list(map(int, selections)) # Convert to a list of integers. + sels.sort() + for idx in sels: + idx2 = idx - 1 + if idx2 < 0: + # Wrap to the bottom. + idx2 = len(actions) - 1 + # Swap. + a = actions[idx2] + actions[idx2] = actions[idx] + actions[idx] = a + self._actions = actions + if REQUEST is not None: + return self.manage_editActionsForm( + REQUEST, manage_tabs_message=( + 'Moved up %d action(s).' % len(sels))) + + security.declareProtected(ManagePortal, 'moveDownActions') + def moveDownActions(self, selections=(), REQUEST=None): + """ + Moves the specified actions down one slot. + """ + actions = list(self._actions) + sels = list(map(int, selections)) # Convert to a list of integers. + sels.sort() + sels.reverse() + for idx in sels: + idx2 = idx + 1 + if idx2 >= len(actions): + # Wrap to the top. + idx2 = 0 + # Swap. + a = actions[idx2] + actions[idx2] = actions[idx] + actions[idx] = a + self._actions = actions + if REQUEST is not None: + return self.manage_editActionsForm( + REQUEST, manage_tabs_message=( + 'Moved down %d action(s).' % len(sels))) === CMF/CMFCore/Expression.py 1.1 => 1.2 === +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (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 +# +############################################################################## +''' +Expressions in a web-configurable workflow. +$Id$ +''' +__version__='$Revision$'[11:-2] + +import Globals +from Globals import Persistent +from Acquisition import aq_inner, aq_parent +from AccessControl import getSecurityManager, ClassSecurityInfo + +from utils import getToolByName +from Products.PageTemplates.Expressions import getEngine +from Products.PageTemplates.TALES import SafeMapping +from Products.PageTemplates.PageTemplate import ModuleImporter + + +class Expression (Persistent): + text = '' + _v_compiled = None + + security = ClassSecurityInfo() + + def __init__(self, text): + self.text = text + self._v_compiled = getEngine().compile(text) + + def __call__(self, econtext): + compiled = self._v_compiled + if compiled is None: + compiled = self._v_compiled = getEngine().compile(self.text) + # ?? Maybe expressions should manipulate the security + # context stack. + res = compiled(econtext) + if isinstance(res, Exception): + raise res + #print 'returning %s from %s' % (`res`, self.text) + return res + +Globals.InitializeClass(Expression) + + +def createExprContext(folder, portal, object): + ''' + An expression context provides names for TALES expressions. + ''' + pm = getToolByName(portal, 'portal_membership') + if object is None: + object_url = '' + else: + object_url = object.absolute_url() + if pm.isAnonymousUser(): + member = None + else: + member = pm.getAuthenticatedMember() + data = { + 'object_url': object_url, + 'folder_url': folder.absolute_url(), + 'portal_url': portal.absolute_url(), + 'object': object, + 'folder': folder, + 'portal': portal, + 'nothing': None, + 'request': getattr( object, 'REQUEST', None ), + 'modules': ModuleImporter, + 'member': member, + } + return getEngine().getContext(data) + === CMF/CMFCore/ActionsTool.py 1.19 => 1.20 === -from utils import UniqueObject, _getAuthenticatedUser, _checkPermission -from utils import getToolByName, _dtmldir +import OFS +from utils import UniqueObject, SimpleItemWithProperties, _getAuthenticatedUser, _checkPermission +from utils import getToolByName, _dtmldir, cookString import CMFCorePermissions from OFS.SimpleItem import SimpleItem from Globals import InitializeClass, DTMLFile, package_home @@ -27,60 +28,57 @@ from Acquisition import aq_base, aq_inner, aq_parent from AccessControl import ClassSecurityInfo from string import join +from Expression import Expression, createExprContext +from ActionInformation import ActionInformation, oai +from ActionProviderBase import ActionProviderBase -class ActionInformation: - # Provides information that may be needed when constructing the list of - # available actions. - __allow_access_to_unprotected_subobjects__ = 1 - - def __init__(self, tool, folder, object=None): - self.portal = portal = aq_parent(aq_inner(tool)) - membership = getToolByName(tool, 'portal_membership') - self.isAnonymous = membership.isAnonymousUser() - self.portal_url = portal.absolute_url() - if folder is not None: - self.folder_url = folder.absolute_url() - self.folder = folder - else: - self.folder_url = self.portal_url - self.folder = portal - self.content = object - if object is not None: - self.content_url = object.absolute_url() - else: - self.content_url = None - def __getitem__(self, name): - # Mapping interface for easy string formatting. - if name[:1] == '_': - raise KeyError, name - if hasattr(self, name): - return getattr(self, name) - raise KeyError, name - - -class ActionsTool (UniqueObject, SimpleItem): +class ActionsTool(UniqueObject, OFS.Folder.Folder, ActionProviderBase): """ Weave together the various sources of "actions" which are apropos to the current user and context. """ id = 'portal_actions' + _actions = [ActionInformation(id='folderContents' + , title='Folder contents' + , action=Expression( + text='string: ${folder_url}/folder_contents') + , condition=Expression( + text='python: folder is not object') + , permissions=('List folder contents',) + , category='object' + , visible=1 + ) + , ActionInformation(id='folderContents' + , title='Folder contents' + , action=Expression( + text='string: ${folder_url}/folder_contents') + , condition=Expression( + text='python: folder is object') + , permissions=('List folder contents',) + , category='folder' + , visible=1 + )] + meta_type = 'CMF Actions Tool' - action_providers = ( 'portal_actions' - , 'portal_memberdata' - , 'portal_registration' - , 'portal_discussion' - , 'portal_membership' - , 'portal_workflow' - , 'portal_undo' - ) + action_providers = ('portal_membership' + , 'portal_actions' + , 'portal_registration' + , 'portal_discussion' + , 'portal_undo' + , 'portal_syndication' + , 'portal_workflow' + , 'portal_properties') security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } - , - ) + SimpleItem.manage_options + manage_options = ( ActionProviderBase.manage_options + + ({'label' : 'Action Providers', 'action' : 'manage_actionProviders'} + , { 'label' : 'Overview', 'action' : 'manage_overview' } + , + ) + OFS.Folder.Folder.manage_options + ) # # ZMI methods @@ -88,19 +86,55 @@ security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) manage_overview = DTMLFile( 'explainActionsTool', _dtmldir ) + manage_actionProviders = DTMLFile('manageActionProviders', _dtmldir) # # Programmatically manipulate the list of action providers # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Lists actions available through the tool. + """ + return self._actions + security.declareProtected( CMFCorePermissions.ManagePortal , 'listActionProviders' ) - def listActionProviders( self ): + def listActionProviders(self): """ returns a sequence of action providers known by this tool """ return self.action_providers + security.declareProtected(CMFCorePermissions.ManagePortal + , 'manage_aproviders') + def manage_aproviders(self + , apname='' + , chosen=() + , add_provider=0 + , del_provider=0 + , REQUEST=None): + """ + Manage TTW Action Providers + """ + providers = list(self.listActionProviders()) + new_providers = [] + if add_provider: + providers.append(apname) + elif del_provider: + for item in providers: + if item not in chosen: + new_providers.append(item) + providers = new_providers + self.action_providers = providers + if REQUEST is not None: + return self.manage_actionProviders(self + , REQUEST + , manage_tabs_message='Properties changed.') + + + security.declareProtected( CMFCorePermissions.ManagePortal , 'addActionProvider' ) @@ -111,26 +145,6 @@ p_new = p_old + ( provider_name, ) self.action_providers = p_new - security.declarePrivate('listActions') - def listActions(self, info): - """ - List actions available from this tool - """ - if info.isAnonymous: - return None - else: - actions = [] - folder_url = info.folder_url - content_url = info.content_url - if folder_url is not None: - actions.append( - { 'name' : 'Folder contents' - , 'url' : folder_url + '/folder_contents' - , 'permissions' : ['List folder contents'] - , 'category' : 'folder' - }) - return actions - security.declareProtected( CMFCorePermissions.ManagePortal , 'deleteActionProvider' ) @@ -162,16 +176,24 @@ break else: folder = aq_parent(aq_inner(folder)) - - info = ActionInformation(self, folder, object) - + ec = createExprContext(folder, portal, object) + ai_objs = [] actions = [] + info = oai(self, folder, object) # Include actions from specific tools. for provider_name in self.listActionProviders(): provider = getattr(self, provider_name) a = provider.listActions(info) - if a: - actions.extend(list(a)) + if a and type(a[0]) is not type({}): + ai_objs.extend(list(a)) + else: + for i in a: + actions.append(i) + + if ai_objs: + for ai in ai_objs: + if ai.testCondition(ec): + actions.append(ai.getAction(ec)) # Include actions from object. if object is not None: @@ -181,7 +203,7 @@ if ti is not None: defs = ti.getActions() if defs: - c_url = info.content_url + c_url = object.absolute_url() for d in defs: a = d['action'] if a: @@ -197,7 +219,7 @@ 'visible': d.get('visible', 1), }) if hasattr(base, 'listActions'): - a = object.listActions(info) + a = object.listActions() if a: actions.extend(list(a)) === CMF/CMFCore/CatalogTool.py 1.21 => 1.22 === from AccessControl import ClassSecurityInfo from utils import mergedLocalRoles +from ActionProviderBase import ActionProviderBase +from ActionInformation import ActionInformation +from Expression import Expression import os import CMFCorePermissions from Acquisition import aq_base @@ -64,16 +67,19 @@ return list(allowed.keys()) -class CatalogTool (UniqueObject, ZCatalog): +class CatalogTool (UniqueObject, ZCatalog, ActionProviderBase): '''This is a ZCatalog that filters catalog queries. ''' id = 'portal_catalog' meta_type = 'CMF Catalog' security = ClassSecurityInfo() + _actions = [] - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ZCatalog.manage_options + + ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + ZCatalog.manage_options + )) def __init__(self): ZCatalog.__init__(self, self.getId()) @@ -82,6 +88,14 @@ # # Subclass extension interface # + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of action information instances + provided via tool + """ + return self._actions + security.declarePublic( 'enumerateIndexes' ) # Subclass can call def enumerateIndexes( self ): # Return a list of ( index_name, type ) pairs for the initial === CMF/CMFCore/MemberDataTool.py 1.12 => 1.13 === from CMFCorePermissions import ViewManagementScreens import CMFCorePermissions +from ActionProviderBase import ActionProviderBase _marker = [] # Create a new marker object. -class MemberDataTool (UniqueObject, SimpleItem, PropertyManager): +class MemberDataTool (UniqueObject, SimpleItem, PropertyManager, ActionProviderBase): '''This tool wraps user objects, making them act as Member objects. ''' id = 'portal_memberdata' meta_type = 'CMF Member Data Tool' + _actions = [] _v_temps = None _properties = () security = ClassSecurityInfo() - manage_options=( ( { 'label' : 'Overview' + manage_options=( ActionProviderBase.manage_options + + ({ 'label' : 'Overview' , 'action' : 'manage_overview' } , { 'label' : 'Contents' @@ -83,8 +86,11 @@ # 'portal_memberdata' interface methods # security.declarePrivate('listActions') - def listActions(self, info): - return None + def listActions(self, info=None): + """ + Return actions provided via tool. + """ + return self._actions security.declarePrivate('getMemberDataContents') def getMemberDataContents(self): === CMF/CMFCore/MembershipTool.py 1.16 => 1.17 === from CMFCorePermissions import ManagePortal import CMFCorePermissions +from ActionProviderBase import ActionProviderBase import Acquisition default_member_content = '''Default page for %s @@ -37,22 +38,23 @@ in the Tool Box on the left. ''' -class MembershipTool (UniqueObject, SimpleItem): +class MembershipTool (UniqueObject, SimpleItem, ActionProviderBase): # This tool accesses member data through an acl_users object. # It can be replaced with something that accesses member data in # a different way. id = 'portal_membership' meta_type = 'CMF Membership Tool' - + _actions = [] security = ClassSecurityInfo() - manage_options=( { 'label' : 'Overview' - , 'action' : 'manage_overview' - } - , { 'label' : 'Configuration' + manage_options=( ({ 'label' : 'Configuration' , 'action' : 'manage_mapRoles' - } - ) + SimpleItem.manage_options + },) + + ActionProviderBase.manage_options + + ( { 'label' : 'Overview' + , 'action' : 'manage_overview' + }, + ) + SimpleItem.manage_options) # # ZMI methods @@ -387,7 +389,7 @@ security.declarePrivate('listActions') - def listActions(self, info): + def listActions(self): return None security.declarePublic('getHomeFolder') === CMF/CMFCore/PortalFolder.py 1.28 => 1.29 === , 'category' : 'folder' } - , { 'id' : 'syndication' - , 'name' : 'Syndication' - , 'action' : 'synPropertiesForm' - , 'permissions' : (ManageProperties,) - , 'category' : 'folder' - } ) } , === CMF/CMFCore/RegistrationTool.py 1.7 => 1.8 === import CMFCorePermissions import string, random +from ActionProviderBase import ActionProviderBase -class RegistrationTool (UniqueObject, SimpleItem): +class RegistrationTool (UniqueObject, SimpleItem, ActionProviderBase): # This tool creates and modifies users by making calls # to portal_membership. id = 'portal_registration' @@ -37,9 +38,10 @@ security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = (ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + SimpleItem.manage_options + ) + SimpleItem.manage_options) # # ZMI methods @@ -51,9 +53,6 @@ # # 'portal_registration' interface methods # - security.declarePrivate('listActions') - def listActions(self, info): - return none security.declarePublic('isRegistrationAllowed') def isRegistrationAllowed(self, REQUEST): === CMF/CMFCore/SkinsTool.py 1.11 => 1.12 === from AccessControl import ClassSecurityInfo from CMFCorePermissions import ManagePortal, AccessContentsInformation +from ActionProviderBase import ActionProviderBase +from ActionInformation import ActionInformation +from Expression import Expression from OFS.Image import Image from OFS.DTMLMethod import DTMLMethod @@ -53,20 +56,23 @@ 'action':'manage_propertiesForm'}] return tuple(rval) -class SkinsTool(UniqueObject, SkinsContainer, PortalFolder): +class SkinsTool(UniqueObject, SkinsContainer, PortalFolder, ActionProviderBase): ''' This tool is used to supply skins to a portal. ''' id = 'portal_skins' meta_type = 'CMF Skins Tool' + _actions = [] cookie_persistence = 0 security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( modifiedOptions() + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + modifiedOptions() + ) + ActionProviderBase.manage_options + ) def __init__(self): self.selections = PersistentMapping() @@ -89,6 +95,14 @@ request_varname = 'portal_skin' allow_any = 0 selections = None + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of actions information instances + provided by the tool. + """ + return self._actions security.declareProtected(ManagePortal, 'manage_propertiesForm') manage_propertiesForm = DTMLFile('dtml/skinProps', globals()) === CMF/CMFCore/TypesTool.py 1.26 => 1.27 === from Acquisition import aq_base import Products, CMFCorePermissions +from ActionProviderBase import ActionProviderBase +from ActionInformation import ActionInformation +from Expression import Expression from CMFCorePermissions import View, ManagePortal, AccessContentsInformation @@ -501,18 +504,21 @@ , ScriptableTypeInformation.meta_type ) -class TypesTool( UniqueObject, OFS.Folder.Folder ): +class TypesTool( UniqueObject, OFS.Folder.Folder, ActionProviderBase ): """ Provides a configurable registry of portal content types. """ id = 'portal_types' meta_type = 'CMF Types Tool' + _actions = [] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( OFS.Folder.Folder.manage_options + + ActionProviderBase.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + OFS.Folder.Folder.manage_options + )) # # ZMI methods @@ -520,6 +526,14 @@ security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) manage_overview = DTMLFile( 'explainTypesTool', _dtmldir ) + + security.declarePrivate('listActions') + def listActions(self, info=None): + """ + Return a list of action information instances + for actions provided via tool + """ + return self._actions def all_meta_types(self): all = TypesTool.inheritedAttribute('all_meta_types')(self) === CMF/CMFCore/UndoTool.py 1.4 => 1.5 === from string import split from AccessControl import ClassSecurityInfo - +from Expression import Expression +from ActionInformation import ActionInformation +from ActionProviderBase import ActionProviderBase from CMFCorePermissions import ManagePortal, UndoChanges, ListUndoableChanges -class UndoTool (UniqueObject, SimpleItem): +class UndoTool (UniqueObject, SimpleItem, ActionProviderBase): id = 'portal_undo' meta_type = 'CMF Undo Tool' # This tool is used to undo changes. + _actions = [ActionInformation(id='undo' + , title='Undo' + , action=Expression( + text='string: ${portal_url}/undo_form') + , condition=Expression( + text='member') + , permissions=(ListUndoableChanges,) + , category='global' + , visible=1 + )] security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = ( ActionProviderBase.manage_options + + SimpleItem.manage_options + + ({ 'label' : 'Overview', 'action' : 'manage_overview' } , - ) + SimpleItem.manage_options - + )) # # ZMI methods # @@ -46,14 +59,11 @@ manage_overview = DTMLFile( 'explainUndoTool', _dtmldir ) security.declarePrivate('listActions') - def listActions( self, info ): - if info.isAnonymous: - return [] - return [ { 'name': 'Undo' - , 'url': 'undo_form' - , 'permissions': [ ListUndoableChanges ] - , 'category': 'global' - } ] + def listActions(self, info=None): + """ + List actions available through tool + """ + return self._actions # # 'portal_undo' interface methods === CMF/CMFCore/WorkflowTool.py 1.19 => 1.20 === security = ClassSecurityInfo() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } - , { 'label' : 'Workflows' + manage_options = ( { 'label' : 'Workflows' , 'action' : 'manage_selectWorkflows' } + , { 'label' : 'Overview', 'action' : 'manage_overview' } ) + Folder.manage_options # From andrew@zope.com Fri Jan 4 19:50:35 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 14:50:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/dtml - editToolsActions.dtml:1.2 manageActionProviders.dtml:1.2 Message-ID: <200201041950.g04JoZt11174@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv10417/CMFCore/dtml Added Files: editToolsActions.dtml manageActionProviders.dtml Log Message: *Merging andrew_ttw_actions-branch into the head *Adds TTW configuration of Action Providers *Allows tools to have actions managed TTW *Added unittests for ActionTool, ActionInformation, and Expression *Added TTW action management to all stock CMF tools *Added Expression module for allowing actions and conditions of actions to be TALES. === CMF/CMFCore/dtml/editToolsActions.dtml 1.1 => 1.2 === + + + + + + + &dtml-form_title; + + +

&dtml-form_title;

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+ Name +
+
+
+ +
+
+
+ Id +
+
+
+ +
+
+
+ Action +
+
+
+ +
+
+
+ Condition +
+
+
+ +
+
+
+ Permission +
+
+
+ +
+
+
+ Category +
+
+
+ +
+
+
+ Visible? +
+
+
+ + + + +
+
+
+
+ +
+ + + + + + + +
+
+ +
+ +

+Add an action +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ Name +
+
+
+ +
+
+
+ Id +
+
+
+ +
+
+
+ Action +
+
+
+ +
+
+
+ Condition +
+
+
+ +
+
+
+ Permission +
+
+
+ +
+
+
+ Category +
+
+
+ +
+
+
+ Visible? +
+
+
+ + +
+
+ +
+ +
+ +
+ + + + + + === CMF/CMFCore/dtml/manageActionProviders.dtml 1.1 => 1.2 === + + +

Action Providers

+
+ + + + + + + + + + + + + + + + + + +
Name
+ + &dtml-sequence-item; +
+ +
+ + +
+
+ + From andrew@zope.com Fri Jan 4 19:50:35 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 14:50:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/interfaces - portal_actions.py:1.7 Message-ID: <200201041950.g04JoZ611179@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/interfaces In directory cvs.zope.org:/tmp/cvs-serv10417/CMFCore/interfaces Modified Files: portal_actions.py Log Message: *Merging andrew_ttw_actions-branch into the head *Adds TTW configuration of Action Providers *Allows tools to have actions managed TTW *Added unittests for ActionTool, ActionInformation, and Expression *Added TTW action management to all stock CMF tools *Added Expression module for allowing actions and conditions of actions to be TALES. === CMF/CMFCore/interfaces/portal_actions.py 1.6 => 1.7 === # listActions__roles__ = () # No permission. def listActions(info): - '''Returns a list of mappings describing actions. Each action + '''Support for the old list of mappings is currently supported: + Returns a list of mappings describing actions. Each action should contain the keys "name", "url", "permissions", and "category", conforming to the specs outlined in portal_actions.listFilteredActionsFor(). The info argument @@ -85,5 +86,6 @@ folder_url content content_url + The new way of doing this is.... ''' From andrew@zope.com Fri Jan 4 19:50:35 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 4 Jan 2002 14:50:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_ActionInformation.py:1.2 test_ActionsTool.py:1.2 test_Expression.py:1.2 test_all.py:1.7 Message-ID: <200201041950.g04JoZj11185@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv10417/CMFCore/tests Modified Files: test_all.py Added Files: test_ActionInformation.py test_ActionsTool.py test_Expression.py Log Message: *Merging andrew_ttw_actions-branch into the head *Adds TTW configuration of Action Providers *Allows tools to have actions managed TTW *Added unittests for ActionTool, ActionInformation, and Expression *Added TTW action management to all stock CMF tools *Added Expression module for allowing actions and conditions of actions to be TALES. === CMF/CMFCore/tests/test_ActionInformation.py 1.1 => 1.2 === +from Products.CMFDefault.MembershipTool import * +from Products.CMFCore.PortalContent import PortalContent +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.Expression import Expression, createExprContext + +class DummyMembershipTool: + def isAnonymousUser(self): + return 1 + +class DummyContent(PortalContent, OFS.SimpleItem.Item): + """ + """ + meta_type = 'Dummy' + url = 'foo_url' + + def __init__(self, id, url=None): + self.url = url + + def absolute_url(self): + return self.url + +class ActionInformationTests(unittest.TestCase): + + def setUp( self ): + get_transaction().begin() + self.connection = Zope.DB.open() + root = self.root = self.connection.root()[ 'Application' ] + root._setObject('portal', DummyContent('portal', 'url_portal')) + portal = self.portal = self.root.portal + portal.portal_membership = DummyMembershipTool() + self.folder = DummyContent('foo', 'url_foo') + self.object = DummyContent('bar', 'url_bar') + + def test_basic_construction(self): + ai = ActionInformation(id='view' + ) + self.assertEqual(ai.getId(), 'view') + self.assertEqual(ai.Title(), 'view') + self.assertEqual(ai.Description(), '') + self.assertEqual(ai.getCondition(), '') + self.assertEqual(ai.getActionExpression(), '') + self.assertEqual(ai.getVisibility(), 1) + self.assertEqual(ai.getCategory(), 'object') + self.assertEqual(ai.getPermissions(), ()) + + def test_construction_with_Expressions(self): + ai = ActionInformation(id='view' + , title='View' + , action=Expression( + text='view') + , condition=Expression( + text='member') + , category='global' + , visible=0) + self.assertEqual(ai.getId(), 'view') + self.assertEqual(ai.Title(), 'View') + self.assertEqual(ai.Description(), '') + self.assertEqual(ai.getCondition(), 'member') + self.assertEqual(ai.getActionExpression(), 'view') + self.assertEqual(ai.getVisibility(), 0) + self.assertEqual(ai.getCategory(), 'global') + self.assertEqual(ai.getPermissions(), ()) + + def test_Condition(self): + portal = self.portal + folder = self.folder + object = self.object + ai = ActionInformation(id='view' + , title='View' + , action=Expression( + text='view') + , condition=Expression( + text='member') + , category='global' + , visible=1) + ec = createExprContext(folder, portal, object) + self.failIf(ai.testCondition(ec)) + + def tearDown( self ): + get_transaction().abort() + self.connection.close() + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ActionInformationTests)) + return suite + +def run(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + run() === CMF/CMFCore/tests/test_ActionsTool.py 1.1 => 1.2 === +from Products.CMFCore.ActionsTool import * +from Products.CMFDefault.URLTool import * +import ZPublisher.HTTPRequest + +class ActionsToolTests( unittest.TestCase ): + + def setUp( self ): + get_transaction().begin() + self.connection = Zope.DB.open() + root = self.root = self.connection.root()[ 'Application' ] + + env = { 'SERVER_NAME' : 'http://localhost' + , 'SERVER_PORT' : '80' + } + root.REQUEST = ZPublisher.HTTPRequest.HTTPRequest( None, env, None ) + + root._setObject( 'portal_actions', ActionsTool() ) + root._setObject('foo', URLTool() ) + self.tool = root.portal_actions + self.ut = root.foo + self.tool.action_providers = ('portal_actions',) + + def test_actionProviders(self): + tool = self.tool + self.assertEqual(tool.listActionProviders(), ('portal_actions',)) + + def test_addActionProvider(self): + tool = self.tool + tool.addActionProvider('foo') + self.assertEqual(tool.listActionProviders(), + ('portal_actions', 'foo')) + + def test_delActionProvider(self): + tool = self.tool + tool.deleteActionProvider('foo') + self.assertEqual(tool.listActionProviders(), + ('portal_actions',)) + + def tearDown( self ): + get_transaction().abort() + self.connection.close() + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ActionsToolTests)) + return suite + +def run(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + run() === CMF/CMFCore/tests/test_Expression.py 1.1 => 1.2 === +from AccessControl.SecurityManagement import newSecurityManager +from AccessControl.SecurityManagement import noSecurityManager +from AccessControl import SecurityManager +from Products.CMFCore.ActionsTool import * +from Products.CMFCore.ActionInformation import ActionInformation +from Products.CMFCore.PortalContent import PortalContent +from Products.CMFCore.Expression import Expression, createExprContext +import ZPublisher.HTTPRequest + +class PermissiveSecurityPolicy: + """ + Stub out the existing security policy for unit testing purposes. + """ + # + # Standard SecurityPolicy interface + # + def validate( self + , accessed=None + , container=None + , name=None + , value=None + , context=None + , roles=None + , *args + , **kw): + return 1 + + def checkPermission( self, permission, object, context) : + if permission == 'forbidden permission': + return 0 + return 1 + +class UnitTestUser( Acquisition.Implicit ): + """ + Stubbed out manager for unit testing purposes. + """ + def getId( self ): + return 'unit_tester' + + getUserName = getId + + def allowed( self, object, object_roles=None ): + # for testing permissions on actions + if object.getId() == 'actions_dummy': + if 'Anonymous' in object_roles: + return 1 + else: + return 0 + return 1 + +class DummyMembershipTool: + def __init__(self, anon=1): + self.anon = anon + + def isAnonymousUser(self): + return self.anon + + def getAuthenticatedMember(self): + return "member" + +class DummyContent(PortalContent, OFS.SimpleItem.Item): + """ + """ + meta_type = 'Dummy' + url = 'foo_url' + + def __init__(self, id, url=None): + self.id = id + self.url = url + + def absolute_url(self): + return self.url + +class ExpressionTests( unittest.TestCase ): + + def setUp( self ): + get_transaction().begin() + self._policy = PermissiveSecurityPolicy() + self._oldPolicy = SecurityManager.setSecurityPolicy(self._policy) + self.connection = Zope.DB.open() + root = self.root = self.connection.root()[ 'Application' ] + newSecurityManager(None, UnitTestUser().__of__( self.root )) + root._setObject('portal', DummyContent('portal', 'url_portal')) + portal = self.portal = self.root.portal + self.folder = DummyContent('foo', 'url_foo') + self.object = DummyContent('bar', 'url_bar') + self.ai = ActionInformation(id='view' + , title='View' + , action=Expression( + text='view') + , condition=Expression( + text='member') + , category='global' + , visible=1) + + def test_anonymous_ec(self): + self.portal.portal_membership = DummyMembershipTool() + ec = createExprContext(self.folder, self.portal, self.object) + member = ec.global_vars['member'] + self.failIf(member) + + + def test_authenticatedUser_ec(self): + self.portal.portal_membership = DummyMembershipTool(anon=0) + ec = createExprContext(self.folder, self.portal, self.object) + member = ec.global_vars['member'] + self.assertEqual(member, 'member') + + def test_ec_context(self): + self.portal.portal_membership = DummyMembershipTool() + ec = createExprContext(self.folder, self.portal, self.object) + object = ec.global_vars['object'] + portal = ec.global_vars['portal'] + folder = ec.global_vars['folder'] + self.failUnless(object) + self.assertEqual(object.id, 'bar') + self.assertEqual(object.absolute_url(), 'url_bar') + self.failUnless(portal) + self.assertEqual(portal.id, 'portal') + self.assertEqual(portal.absolute_url(), 'url_portal') + self.failUnless(folder) + self.assertEqual(folder.id, 'foo') + self.assertEqual(folder.absolute_url(), 'url_foo') + + + + def tearDown( self ): + get_transaction().abort() + self.connection.close() + noSecurityManager() + SecurityManager.setSecurityPolicy(self._oldPolicy) + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(ExpressionTests)) + return suite + +def run(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + run() === CMF/CMFCore/tests/test_all.py 1.6 => 1.7 === from Products.CMFCore.tests import test_PortalFolder from Products.CMFCore.tests import test_TypesTool +from Products.CMFCore.tests import test_ActionsTool +from Products.CMFCore.tests import test_ActionInformation +from Products.CMFCore.tests import test_Expression from Products.CMFCore.tests import test_CatalogTool def test_suite(): @@ -10,6 +13,9 @@ suite.addTest( test_ContentTypeRegistry.test_suite() ) suite.addTest( test_PortalFolder.test_suite() ) suite.addTest( test_TypesTool.test_suite() ) + suite.addTest( test_ActionsTool.test_suite() ) + suite.addTest( test_ActionInformation.test_suite() ) + suite.addTest( test_Expression.test_suite() ) suite.addTest( test_CatalogTool.test_suite() ) return suite From tseaver@zope.com Fri Jan 4 20:00:41 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:00:41 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore/dtml - addTypeInfo.dtml:1.2 Message-ID: <200201042000.g04K0f013529@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv13507/CMFCore/dtml Modified Files: addTypeInfo.dtml Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === Products/CMFCore/dtml/addTypeInfo.dtml 1.1 => 1.2 === - or 'Add Factory-based Type Information'"> + @@ -11,7 +10,7 @@
- + - +
From tseaver@zope.com Fri Jan 4 20:00:41 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:00:41 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore/tests - test_TypesTool.py:1.8 Message-ID: <200201042000.g04K0fj13531@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv13507/CMFCore/tests Modified Files: test_TypesTool.py Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === Products/CMFCore/tests/test_TypesTool.py 1.7 => 1.8 === getUserName = getId + def has_permission(self, permission, obj): + # For types tool tests dealing with filtered_meta_types + return 1 + def allowed( self, object, object_roles=None ): # for testing permissions on actions if object.getId() == 'actions_dummy': @@ -105,6 +109,10 @@ def extra_meta_types(): return ( { 'name' : 'Dummy', 'action' : 'manage_addFolder' }, ) +class DummyTypeInfo(TypeInformation): + """ new class of type info object """ + meta_type = "Dummy Test Type Info" + class TypesToolTests( unittest.TestCase ): def setUp( self ): @@ -182,10 +190,34 @@ custom_view = utils._getViewFor( dummy, view='view2' )() unpermitted_view = utils._getViewFor( dummy, view='edit' )() - assert default_view == 'view' - assert custom_view == 'view2' - assert unpermitted_view != 'edit' - assert unpermitted_view == 'view' + self.failUnlessEqual(default_view, 'view') + self.failUnlessEqual(custom_view, 'view2') + self.failIf(unpermitted_view == 'edit') + self.failUnlessEqual(unpermitted_view, 'view') + + def test_AddingOtherTypeInfos(self): + addTypeFactory(DummyTypeInfo) + tool = self.root.portal_types + type_type = DummyTypeInfo.meta_type + + fmt = [ mt['name'] for mt in tool.filtered_meta_types() ] + self.failUnless(DummyTypeInfo.meta_type in fmt, + "Subfactory meta type not registered") + + atif = tool.manage_addTypeInfoForm(self.root.REQUEST, + type_type=type_type) + self.failUnless(atif.find(type_type) > -1, + "'%s' not found in type info form" % type_type) + + tool.manage_addTypeInformation(id='foo_default', type_type=None) + fd = tool.foo_default + self.failUnless(isinstance(fd, FactoryTypeInformation)) + self.failIf(isinstance(fd, DummyTypeInfo)) + + tool.manage_addTypeInformation(id='foo_sub', type_type=type_type) + fs = tool.foo_sub + self.failUnless(isinstance(fs, DummyTypeInfo), fs.__class__) + class TypeInfoTests( unittest.TestCase ): @@ -539,6 +571,8 @@ ti.constructInstance( folder, 'dust' ) majyk_dust = folder._getOb( 'majyk_dust' ) self.assertEqual( majyk_dust.id, 'majyk_dust' ) + + def test_suite(): suite = unittest.TestSuite() From tseaver@zope.com Fri Jan 4 20:01:11 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:01:11 -0500 Subject: [CMF-checkins] CVS: Releases/CMF - CHANGES.txt:1.42 Message-ID: <200201042001.g04K1BE13587@cvs.baymountain.com> Update of /cvs-repository/Releases/CMF In directory cvs.zope.org:/tmp/cvs-serv13507 Modified Files: CHANGES.txt Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === Releases/CMF/CHANGES.txt 1.41 => 1.42 === workflows) to the core set of CMF products. + - Extended TypesTool to permit registration of new TypeInformation + implementations (Tracker #409, thanks to Jeffrey Shell for the + work!) + + - Made all tool-generated actions configurable through-the-web, + via an "Actions" tab on each tool; made the list of ActionProviders + configurable TTW as well. + - Enabled querying actions from workflow tool in absence of actions tool (Tracker #401). From tseaver@zope.com Fri Jan 4 20:01:11 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:01:11 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore - TypesTool.py:1.28 Message-ID: <200201042001.g04K1Bn13590@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore In directory cvs.zope.org:/tmp/cvs-serv13507/CMFCore Modified Files: TypesTool.py Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === Products/CMFCore/TypesTool.py 1.27 => 1.28 === from utils import UniqueObject, SimpleItemWithProperties, tuplize from utils import _dtmldir, _checkPermission, cookString +import urllib import string from AccessControl import getSecurityManager, ClassSecurityInfo from Acquisition import aq_base @@ -33,6 +34,22 @@ _marker = [] # Create a new marker. + +_type_factories = {} +allowedTypes = ( 'Script (Python)' + , 'Python Method' + , 'DTML Method' + , 'External Method' + ) + +def addTypeFactory(factory, id=None): + # modeled after WorkflowTool.addWorkflowFactory() + global allowedTypes + if id is None: + id = getattr(factory, 'id', '') or getattr(factory, 'meta_type', '') + _type_factories[id] = factory + allowedTypes = allowedTypes + (factory.meta_type,) + class TypeInformation (SimpleItemWithProperties): """ Base class for information about a content type. @@ -434,7 +451,7 @@ return ob InitializeClass( FactoryTypeInformation ) - +addTypeFactory(FactoryTypeInformation) class ScriptableTypeInformation( TypeInformation ): """ @@ -489,20 +506,13 @@ return ob InitializeClass( ScriptableTypeInformation ) - +addTypeFactory(ScriptableTypeInformation) # Provide aliases for backward compatibility. ContentFactoryMetadata = FactoryTypeInformation ContentTypeInformation = ScriptableTypeInformation -allowedTypes = ( 'Script (Python)' - , 'Python Method' - , 'DTML Method' - , 'External Method' - , FactoryTypeInformation.meta_type - , ScriptableTypeInformation.meta_type - ) class TypesTool( UniqueObject, OFS.Folder.Folder, ActionProviderBase ): """ @@ -537,14 +547,16 @@ def all_meta_types(self): all = TypesTool.inheritedAttribute('all_meta_types')(self) - return ( - {'name':FactoryTypeInformation.meta_type, - 'action':'manage_addFactoryTIForm', - 'permission':'Manage portal'}, - {'name':ScriptableTypeInformation.meta_type, - 'action':'manage_addScriptableTIForm', - 'permission':'Manage portal'}, - ) + tuple(all) + factypes = [] + for name, fac in _type_factories.items(): + query = urllib.urlencode({'type_type': name}) + factypes.append({ + 'name': fac.meta_type, + 'action': 'manage_addTypeInfoForm?%s' % query, + 'permission': CMFCorePermissions.ManagePortal, + }) + factypes.extend(all) + return factypes def filtered_meta_types(self, user=None): # Filters the list of available meta types. @@ -579,24 +591,17 @@ _addTIForm = DTMLFile( 'addTypeInfo', _dtmldir ) - security.declareProtected(ManagePortal, 'manage_addFactoryTIForm') - def manage_addFactoryTIForm(self, REQUEST): - ' ' - return self._addTIForm(self, REQUEST, scriptable='', - types=self.listDefaultTypeInformation()) - - security.declareProtected(ManagePortal, 'manage_addScriptableTIForm') - def manage_addScriptableTIForm(self, REQUEST): - ' ' - return self._addTIForm(self, REQUEST, scriptable='1', + security.declareProtected(ManagePortal, 'manage_addTypeInfoForm') + def manage_addTypeInfoForm(self, REQUEST={}, type_type=''): + """ Return the type info form while keeping the list of + prefab type information up to date """ + return self._addTIForm(self, REQUEST, type_type=type_type, types=self.listDefaultTypeInformation()) security.declareProtected(ManagePortal, 'manage_addTypeInformation') - def manage_addTypeInformation(self, id=None, scriptable='', + def manage_addTypeInformation(self, id=None, type_type=None, typeinfo_name=None, RESPONSE=None): - """ - Create a TypeInformation in self. - """ + """ Create a TypeInformation in self. """ fti = None if typeinfo_name: info = self.listDefaultTypeInformation() @@ -610,8 +615,9 @@ id = fti.get('id', None) if not id: raise 'Bad Request', 'An id is required.' - if scriptable: - klass = ScriptableTypeInformation + + if type_type in _type_factories.keys(): + klass = _type_factories[type_type] else: klass = FactoryTypeInformation id = str(id) From tseaver@zope.com Fri Jan 4 20:20:06 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:20:06 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - WorkflowTool.py:1.18.2.1 Message-ID: <200201042020.g04KK6a18415@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv18360/CMFCore Modified Files: Tag: CMF-1_2-branch WorkflowTool.py Log Message: - Enabled querying actions from workflow tool in absence of actions tool (Tracker #401). === CMF/CMFCore/WorkflowTool.py 1.18 => 1.18.2.1 === _marker = [] # Create a new marker object. +class WorkflowInformation: + """ + Shim implementation of ActionInformation, to enable + querying actions without mediation of the 'portal_actions' tool. + """ + def __init__(self, object): + self.content = object + self.content_url = object.absolute_url() + self.portal_url = self.folder_url = '' + + def __getitem__(self, name): + if name[:1] == '_': + raise KeyError, name + if hasattr(self, name): + return getattr(self, name) + raise KeyError, name + class WorkflowTool (UniqueObject, Folder): ''' @@ -345,6 +362,14 @@ if v is not None: vars.update(v) return vars + + security.declarePublic('getActionsFor') + def getActionsFor(self, ob): + ''' + Return a list of action dictionaries for 'ob', just as though + queried via 'ActionsTool.listFilteredActionsFor'. + ''' + return self.listActions( WorkflowInformation( ob ) ) security.declarePrivate('listActions') def listActions(self, info): From tseaver@zope.com Fri Jan 4 20:25:32 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:25:32 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_TypesTool.py:1.7.2.2 Message-ID: <200201042025.g04KPWS19621@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv19603/CMFCore/tests Modified Files: Tag: CMF-1_2-branch test_TypesTool.py Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === CMF/CMFCore/tests/test_TypesTool.py 1.7.2.1 => 1.7.2.2 === getUserName = getId + def has_permission(self, permission, obj): + # For types tool tests dealing with filtered_meta_types + return 1 + def allowed( self, object, object_roles=None ): # for testing permissions on actions if object.getId() == 'actions_dummy': @@ -105,6 +109,10 @@ def extra_meta_types(): return ( { 'name' : 'Dummy', 'action' : 'manage_addFolder' }, ) +class DummyTypeInfo(TypeInformation): + """ new class of type info object """ + meta_type = "Dummy Test Type Info" + class TypesToolTests( unittest.TestCase ): def setUp( self ): @@ -182,10 +190,34 @@ custom_view = utils._getViewFor( dummy, view='view2' )() unpermitted_view = utils._getViewFor( dummy, view='edit' )() - assert default_view == 'view' - assert custom_view == 'view2' - assert unpermitted_view != 'edit' - assert unpermitted_view == 'view' + self.failUnlessEqual(default_view, 'view') + self.failUnlessEqual(custom_view, 'view2') + self.failIf(unpermitted_view == 'edit') + self.failUnlessEqual(unpermitted_view, 'view') + + def test_AddingOtherTypeInfos(self): + addTypeFactory(DummyTypeInfo) + tool = self.root.portal_types + type_type = DummyTypeInfo.meta_type + + fmt = [ mt['name'] for mt in tool.filtered_meta_types() ] + self.failUnless(DummyTypeInfo.meta_type in fmt, + "Subfactory meta type not registered") + + atif = tool.manage_addTypeInfoForm(self.root.REQUEST, + type_type=type_type) + self.failUnless(atif.find(type_type) > -1, + "'%s' not found in type info form" % type_type) + + tool.manage_addTypeInformation(id='foo_default', type_type=None) + fd = tool.foo_default + self.failUnless(isinstance(fd, FactoryTypeInformation)) + self.failIf(isinstance(fd, DummyTypeInfo)) + + tool.manage_addTypeInformation(id='foo_sub', type_type=type_type) + fs = tool.foo_sub + self.failUnless(isinstance(fs, DummyTypeInfo), fs.__class__) + class TypeInfoTests( unittest.TestCase ): @@ -539,6 +571,8 @@ ti.constructInstance( folder, 'dust' ) majyk_dust = folder._getOb( 'majyk_dust' ) self.assertEqual( majyk_dust.id, 'majyk_dust' ) + + def test_suite(): suite = unittest.TestSuite() From tseaver@zope.com Fri Jan 4 20:25:32 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:25:32 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/dtml - addTypeInfo.dtml:1.1.16.1 Message-ID: <200201042025.g04KPW219619@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv19603/CMFCore/dtml Modified Files: Tag: CMF-1_2-branch addTypeInfo.dtml Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === CMF/CMFCore/dtml/addTypeInfo.dtml 1.1 => 1.1.16.1 === - or 'Add Factory-based Type Information'"> + @@ -11,7 +10,7 @@ - + - @@ -85,23 +87,24 @@ - - - @@ -85,23 +87,24 @@ - - - @@ -85,23 +87,24 @@ - - - - - - - - - + + + + + + + +
From tseaver@zope.com Fri Jan 4 20:26:02 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:26:02 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - TypesTool.py:1.26.2.2 Message-ID: <200201042026.g04KQ2K19643@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv19603/CMFCore Modified Files: Tag: CMF-1_2-branch TypesTool.py Log Message: - Extended TypesTool to permit registration of new TypeInformation implementations (Tracker #409, thanks to Jeffrey Shell for the work!) === CMF/CMFCore/TypesTool.py 1.26.2.1 => 1.26.2.2 === from utils import _dtmldir, _checkPermission, cookString import string +import urllib from AccessControl import getSecurityManager, ClassSecurityInfo try: from AccessControl import Unauthorized @@ -34,6 +35,22 @@ _marker = [] # Create a new marker. + +_type_factories = {} +allowedTypes = ( 'Script (Python)' + , 'Python Method' + , 'DTML Method' + , 'External Method' + ) + +def addTypeFactory(factory, id=None): + # modeled after WorkflowTool.addWorkflowFactory() + global allowedTypes + if id is None: + id = getattr(factory, 'id', '') or getattr(factory, 'meta_type', '') + _type_factories[id] = factory + allowedTypes = allowedTypes + (factory.meta_type,) + class TypeInformation (SimpleItemWithProperties): """ Base class for information about a content type. @@ -435,7 +452,7 @@ return ob InitializeClass( FactoryTypeInformation ) - +addTypeFactory(FactoryTypeInformation) class ScriptableTypeInformation( TypeInformation ): """ @@ -490,20 +507,13 @@ return ob InitializeClass( ScriptableTypeInformation ) - +addTypeFactory(ScriptableTypeInformation) # Provide aliases for backward compatibility. ContentFactoryMetadata = FactoryTypeInformation ContentTypeInformation = ScriptableTypeInformation -allowedTypes = ( 'Script (Python)' - , 'Python Method' - , 'DTML Method' - , 'External Method' - , FactoryTypeInformation.meta_type - , ScriptableTypeInformation.meta_type - ) class TypesTool( UniqueObject, OFS.Folder.Folder ): """ @@ -527,14 +537,17 @@ def all_meta_types(self): all = TypesTool.inheritedAttribute('all_meta_types')(self) - return ( - {'name':FactoryTypeInformation.meta_type, - 'action':'manage_addFactoryTIForm', - 'permission':'Manage portal'}, - {'name':ScriptableTypeInformation.meta_type, - 'action':'manage_addScriptableTIForm', - 'permission':'Manage portal'}, - ) + tuple(all) + factypes = [] + add_fac = factypes.append + for name, fac in _type_factories.items(): + query = urllib.urlencode({'type_type': name}) + factypes.append({ + 'name': fac.meta_type, + 'action': 'manage_addTypeInfoForm?%s' % query, + 'permission': CMFCorePermissions.ManagePortal, + }) + factypes.extend(all) + return factypes def filtered_meta_types(self, user=None): # Filters the list of available meta types. @@ -569,24 +582,17 @@ _addTIForm = DTMLFile( 'addTypeInfo', _dtmldir ) - security.declareProtected(ManagePortal, 'manage_addFactoryTIForm') - def manage_addFactoryTIForm(self, REQUEST): - ' ' - return self._addTIForm(self, REQUEST, scriptable='', - types=self.listDefaultTypeInformation()) - - security.declareProtected(ManagePortal, 'manage_addScriptableTIForm') - def manage_addScriptableTIForm(self, REQUEST): - ' ' - return self._addTIForm(self, REQUEST, scriptable='1', + security.declareProtected(ManagePortal, 'manage_addTypeInfoForm') + def manage_addTypeInfoForm(self, REQUEST={}, type_type=''): + """ Return the type info form while keeping the list of + prefab type information up to date """ + return self._addTIForm(self, REQUEST, type_type=type_type, types=self.listDefaultTypeInformation()) security.declareProtected(ManagePortal, 'manage_addTypeInformation') - def manage_addTypeInformation(self, id=None, scriptable='', + def manage_addTypeInformation(self, id=None, type_type=None, typeinfo_name=None, RESPONSE=None): - """ - Create a TypeInformation in self. - """ + """ Create a TypeInformation in self. """ fti = None if typeinfo_name: info = self.listDefaultTypeInformation() @@ -600,8 +606,9 @@ id = fti.get('id', None) if not id: raise 'Bad Request', 'An id is required.' - if scriptable: - klass = ScriptableTypeInformation + + if type_type in _type_factories.keys(): + klass = _type_factories[type_type] else: klass = FactoryTypeInformation id = str(id) From tseaver@zope.com Fri Jan 4 20:26:13 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:26:13 -0500 Subject: [CMF-checkins] CVS: CMF - INSTALL.txt:1.4.2.1 Message-ID: <200201042026.g04KQDw19689@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv19682 Modified Files: Tag: CMF-1_2-branch INSTALL.txt Log Message: - Update list of supported Zope versions. === CMF/INSTALL.txt 1.4 => 1.4.2.1 === Requirements - - Zope v. 2.3.2 or later (*not* 2.4a1) + - Zope v. 2.3.3, 2.4.3 or 2.5 and later (*not* 2.4.2 or 2.4.1) Assumptions From tseaver@zope.com Fri Jan 4 20:27:34 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 15:27:34 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.7 HISTORY.txt:1.31.4.1 Message-ID: <200201042027.g04KRY120099@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv20091 Modified Files: Tag: CMF-1_2-branch CHANGES.txt HISTORY.txt Log Message: - Packaging changelog / history. === CMF/CHANGES.txt 1.36.2.6 => 1.36.2.7 === - - - (__future__) Exposed role management for manager users on - the default roster: managers can now "promote" members - without going to the ZMI. - - - (__future__) Added "custom schema" mechanism for content - objetcts: site managers can now define additional - propertysheets for a content type, which will then be - reflected in instances created from the type. - - - (__future__) Added simple link extraction / checking for - textual content. Link checking also works for Link objects. +1.2 - - (__future__) Added "composite content" types (see the - "dogbowl proposal", - (http://cmf.zope.org/rqmts/proposals/compounds/compoundproposal.txt). + Features Added - - (__future__) Added DCWorkflow (through-the-web configurable - workflows) to the core set of CMF products. + - Extended TypesTool to permit registration of new TypeInformation + implementations (Tracker #409, thanks to Jeffrey Shell for the + work!) -1.2 + - Enabled querying actions from workflow tool in absence + of actions tool (Tracker #401). Bugs Fixed @@ -42,294 +30,3 @@ - Made 'CMFCore/interfaces/__init__.py' non-empty, to remove suspicion that the file was corrupted in the download (Tracker #426). - -1.2 beta (2001/12/11) - - New features - - - Added docs from the crack ZC docs guys; these docs live in - the top-level 'docs' directory. - - - Merged CMFDecor product's artifacts into CMFCore / - CMFDefault; theses aretifacts allow use of filesystem-based - Zope Page Templates as skins. - - Note that the CMFDecor skin is the one which will be - receiving all our development focus: we will fix bugs in the - DTML skins, but are not likely to invest significant effort - in upgrading it. - - - Hooked 'manage_addFolder' to allow creation of PortalFolders - from both WebDAV, FTP, and ZMI. - - - Improved tracebacks from broken FSDTMLMethods, which no longer - indicate that every problem is in RestrictedDTML. - - - Made it possible to add CookieCrumblers in nested folders. - You can just drop in a cookie crumbler anywhere to change the - login form for that area of the site. In fact, now you don't - have to create a user folder just to change the login - process. - - - Made Link objects editable via FTP / WebDAV. - - - Merged Chris Withers' FSSQLMethod into CMFCore. - - - Added documentation for installing from CVS. - - - Moved permission checking inside personalize_form to make - sure Anonymous cannot access it without logging in (CMF Tracker - Issue 349, thanks go to Bill Anderson). - - - Added initial CMF use cases as FSSTXMethods in CMFDefault/help. - - - Made validation methods of 'portal_metadata' available to - scripts. - - - Made skinned 'index_html' reflect generic view on folder - content, rather than simple title/description of the portal. - - - Added "Change and View" submit button to content editing - forms; added check for this button to POST handlers in CMFDefault, - and indirected redirect targets in those methods through - 'getActionByID'. - - - Added knob for skin cookie persistence to SkinsTool's - "properties" tab. The default policy (unchanged) is that - skin cookies expire at the end of the browser session. if - Skin Cookie Persistence is checked the cookie will last a - full yesr. - - - Added an API to the 'portal_actions' interface for querying, - adding, and removing action providers. - - - Added a "multi-review" form, enabling a reviewer to publish - or reject multiple items at once, using a common comment. - - - Added ZMI tab to DirectoryView to allow re-basing the - filesystem path. - - - Added "breadcrumbs" to CMFDecor skins. - - - Added initial support for WebDAV locaking to PortalContent. - - - Added SortCriterion to list of criterion types for Topics, - to permit sorting of results. - - - Added "Local Roles" action to folders to ease collaboration. - - - Add scarecrow assertions for the CMF-centric interfaces, and - made the actual interfaces compatible with the standard - Zope Interface package. - - - Made FSSTXMethod display skinnable, and added ZPT version. - - - Added 'visible' attribute to TypeInformation actions, to - permit indirection (via 'getActionById') without exposing the - action in the CMF UI. - - - Extended MetadataTool to allow adding / removing element specs - (i.e., it can now manage policies for "custom" schemas, as - well as Dublin Core). - - Bug fixes - - - Refactored content and metadata editing methods of - DefaultDublinCoreImpl, Document, and NewsItem to disentangle - the excessive coupling. Each "path" for editing now has a - "presentation"-level method, which directs traffic and - reindexes the object; the underlying methods are much - simplified. - - - Fixed inner / named links in Document / News Item (thanks to - Kenichi Sato for the patch!) - - - Ensured that editing methods handle WebDAV locks correctly, - using a new 'failIfLocked' assertion. - - - Added cookString method to CMFCore.utils for taking a string - and making it id friendly, it also does a string.lower on the - resultant regex. Changed TypesTool to utilize cookString to - ensure that action ids are properly formated if the name is - being used as the id. - - - Added 'getReply' to CMFDefault.DiscussionItem.DiscussionItemContainer, - to permit access to an individual reply without needing to - do traversal. - - - Corrected pass-through of 'file' and 'seatbelt' arguments in - new 'CMFDefault.Document.edit' method; also sync'ed ZMI edit - method for documents with standard protocol (Tracker #417). - - - Added cookString method to CMFCore.utils for taking a string and - making it id friendly, it also does a string.lower on the resultant - regex. - - - Made examples in INSTALL.txt less terse, and added notes on - Windows-specific issues (thanks to Johan Mukhsein for the - suggestions). - - - Made error message generated by FSPropertiesObject capture the - offending line and line #; also, added logic to allow blank - lines and comment lines beginning with '#' (tracker #338). - - - Added fixup to Link objects for user-entered URLs which don't - supply scheme: for example, fix up 'www.zope.com' to - 'http://www.zope.com'. (tracker #361) - - - Updated CMFCore.CatalogTool to allow new, optional 'idxs' - argument to 'catalog_object' (tracker #405). - - - Added a workaround for the problem where the CookieCrumber - set cookies even though the user entered an incorrect password. - RESPONSE.unauthorized() now cancels the cookie response - header. The new login_form and logged_in form both try to - invoke unauthorized(), so make sure you install the new - forms. - - - Implement the notional 'search results item' interface for - SkinnedFolder. - - - Corrected solecism in Topic (use of 'criteria' for singular); - removed the need to know about the funky generated IDs for - criterion objects. Fixed derived bug in skins (tracker #408). - - - Modified error-logging code to avoid potential leaks of - traceback frame. - - - Made Document's 'manage_FTPget' use 'EditableBody', rather than - accessing 'text' attribute directly (improves reusability). - Likewise 'Document.SearchableText'. - - - Merged Seb Bacon's refactoring of 'getDefaultView' into - 'CMFCore.utils._getView'; clients can now specify a view by name, - as well. - - - Made the default content type for Image 'image/png', instead - of the unintuitive 'text/html' inherited from DefaultDublinCoreImpl - (tracker #384). - - - Corrected typo in ActionsTool which broke actions for the root - portal object (tracker #379). - - - Updated the MemberDataTool to use an OOBTree, instead of the - old-style BTree, to store member data wrappers (CMF Tracker 375). - - - Corrected 'personalize_form' to use 'getProperty' where feasible, - rather than relying on direct attribute access (tracker #372). - - - Removed an incompatibility with LoginManager in - CMFCore.MembershipTool (tracker #365). - - - Removed an infinite loop condition that arises when - MembershipTool.createMemberArea gets called inside wrapUser - (this could only happen if "Create Member Area" was checked - in the Membership tool.) - - - Added new 'TitleOrId' skin method, and updated skins to - depend on it rather than SimpleItem.title_or_id. - - - Made unit tests runnable without reliance on 'zctl test'. - - - Corrected initial column set in catalog to include - "ModificationDate" instead of "ModifiedDate". - - - Ensured that object is recatalogued (e.g., after setting - 'portal_type'; thanks to Florent Guillaume). - - - Removed silly dependency of 'CatalogTool.searchResults' on - REQUEST (catalog already does the Right Thing (tm) when no - REQUEST is available). Note that this requires updates to - the 'search' skins, as they didn't pass it in. - - - Changed redirect target after rejecting an item to the search - page for pending content items; this resolves the problem - that the non-Manager reviewr who rejects an item no longer - has View permission on it, and therefore gets an Unauthorized - when redirecting to the object's view action. - - - Moved generation of the "Add to Favorites" and "My Favorites" - links from the CMFCore/ActionsTool into the - CMFDefault/MembershipTool, which is a more logical location - for it because it relies on information from the membership tool. - - - Made Topic directly publishable (like PortalContent), using its - 'view' action (or the first action found, if view is not present). - - - Set title for newly-created member folders; fixes - breadcrumbs that expect a title on the object. - - - Allow URLs with query strings in StructuredText links. - - - Update 'news_box' to search based on 'Type', rather than - 'meta_type'. - - - Fix 'SkinnedFolder.Creator()' to call 'getOwner()' with - info argument. - - - Made CookieCrumbler check for 'WEBDAV_SOURCE_PORT' - environment variable (supplied by Zope 2.4.1+), and bail on - intercepting authentication if found. - - - Included 'Owner' in list of significant roles returned by - 'MembershipTool.getPortalRoles' (e.g., so that the - "Local Roles" interface can allow assignment of it). - - - Allow users with local role of "Reviewer" to see the "pending - review" action. - - - Made TypesTool, rather than individual type objects, - responsible for generating "immediate view" URL; type - objects now return the newly-created object, which makes - scripting them much simpler. - - - Remove fossilized reference to 'getSecurityManager' from - 'PortalContent._verifyActionPermissions'. - - - Modified the redirect after "Add to Favorites" to us the - view action, rather than 'view'. - - - Fixed 'Document.guessFormat()' to use - 'utils.html_headcheck()' instead of 'bodyfinder' regex to - detect structured text versus html; avoids recursion limit - for large HTML files. - - - Removed spurious 'afterCreate' protocol for content objects. - - - Added mapping of 'css' file extension to FSDTMLMethods. - - - Modifed PortalFolder.listFolderContents to handle - permission-based filtering; duplicates what skip_unauthorized - is doing in DocumentTemplate/DT_IN.py (but works for ZPT as - well). - - - Modified CMFDefault.RegistrationTool.addMember to avoid - flunking validation if properties are not passed (Tracker - #335). - - - Applied patch from Chris Withers to 'register' skin method; - the patch which avoids quoting problems for the error message - if a problem occurs (Tracker #339). - - - Added 'DiscussionItem.replyCount' (Tracker #328). - 'DiscussionItem.hasReplies' now returns only a boolean value. - Standard skins don't call 'replyCount' due to performance - concerns. - - - Factored content filtering logic into a Python Script. - - - Improved handling of multiple rename targets (thanks to Chris - Withers for the patch.) - - - Completed conversion of form targets from DTML Methods to - Python Scripts. - - - Improved compatibility with Zope 2.4: - - o support for new "restricted execution" mode; - - o support for new catalog initialization API. - - o updated 'test_all' unit test drivers to use standard - 'unittest' module from Python 2.1 (it no longer has - 'JUnitTestTextRunner' class). === CMF/HISTORY.txt 1.31 => 1.31.4.1 === + + New features + + - Added docs from the crack ZC docs guys; these docs live in + the top-level 'docs' directory. + + - Merged CMFDecor product's artifacts into CMFCore / + CMFDefault; theses aretifacts allow use of filesystem-based + Zope Page Templates as skins. + + Note that the CMFDecor skin is the one which will be + receiving all our development focus: we will fix bugs in the + DTML skins, but are not likely to invest significant effort + in upgrading it. + + - Hooked 'manage_addFolder' to allow creation of PortalFolders + from both WebDAV, FTP, and ZMI. + + - Improved tracebacks from broken FSDTMLMethods, which no longer + indicate that every problem is in RestrictedDTML. + + - Made it possible to add CookieCrumblers in nested folders. + You can just drop in a cookie crumbler anywhere to change the + login form for that area of the site. In fact, now you don't + have to create a user folder just to change the login + process. + + - Made Link objects editable via FTP / WebDAV. + + - Merged Chris Withers' FSSQLMethod into CMFCore. + + - Added documentation for installing from CVS. + + - Moved permission checking inside personalize_form to make + sure Anonymous cannot access it without logging in (CMF Tracker + Issue 349, thanks go to Bill Anderson). + + - Added initial CMF use cases as FSSTXMethods in CMFDefault/help. + + - Made validation methods of 'portal_metadata' available to + scripts. + + - Made skinned 'index_html' reflect generic view on folder + content, rather than simple title/description of the portal. + + - Added "Change and View" submit button to content editing + forms; added check for this button to POST handlers in CMFDefault, + and indirected redirect targets in those methods through + 'getActionByID'. + + - Added knob for skin cookie persistence to SkinsTool's + "properties" tab. The default policy (unchanged) is that + skin cookies expire at the end of the browser session. if + Skin Cookie Persistence is checked the cookie will last a + full yesr. + + - Added an API to the 'portal_actions' interface for querying, + adding, and removing action providers. + + - Added a "multi-review" form, enabling a reviewer to publish + or reject multiple items at once, using a common comment. + + - Added ZMI tab to DirectoryView to allow re-basing the + filesystem path. + + - Added "breadcrumbs" to CMFDecor skins. + + - Added initial support for WebDAV locaking to PortalContent. + + - Added SortCriterion to list of criterion types for Topics, + to permit sorting of results. + + - Added "Local Roles" action to folders to ease collaboration. + + - Add scarecrow assertions for the CMF-centric interfaces, and + made the actual interfaces compatible with the standard + Zope Interface package. + + - Made FSSTXMethod display skinnable, and added ZPT version. + + - Added 'visible' attribute to TypeInformation actions, to + permit indirection (via 'getActionById') without exposing the + action in the CMF UI. + + - Extended MetadataTool to allow adding / removing element specs + (i.e., it can now manage policies for "custom" schemas, as + well as Dublin Core). + + Bug fixes + + - Refactored content and metadata editing methods of + DefaultDublinCoreImpl, Document, and NewsItem to disentangle + the excessive coupling. Each "path" for editing now has a + "presentation"-level method, which directs traffic and + reindexes the object; the underlying methods are much + simplified. + + - Fixed inner / named links in Document / News Item (thanks to + Kenichi Sato for the patch!) + + - Ensured that editing methods handle WebDAV locks correctly, + using a new 'failIfLocked' assertion. + + - Added cookString method to CMFCore.utils for taking a string + and making it id friendly, it also does a string.lower on the + resultant regex. Changed TypesTool to utilize cookString to + ensure that action ids are properly formated if the name is + being used as the id. + + - Added 'getReply' to CMFDefault.DiscussionItem.DiscussionItemContainer, + to permit access to an individual reply without needing to + do traversal. + + - Corrected pass-through of 'file' and 'seatbelt' arguments in + new 'CMFDefault.Document.edit' method; also sync'ed ZMI edit + method for documents with standard protocol (Tracker #417). + + - Added cookString method to CMFCore.utils for taking a string and + making it id friendly, it also does a string.lower on the resultant + regex. + + - Made examples in INSTALL.txt less terse, and added notes on + Windows-specific issues (thanks to Johan Mukhsein for the + suggestions). + + - Made error message generated by FSPropertiesObject capture the + offending line and line #; also, added logic to allow blank + lines and comment lines beginning with '#' (tracker #338). + + - Added fixup to Link objects for user-entered URLs which don't + supply scheme: for example, fix up 'www.zope.com' to + 'http://www.zope.com'. (tracker #361) + + - Updated CMFCore.CatalogTool to allow new, optional 'idxs' + argument to 'catalog_object' (tracker #405). + + - Added a workaround for the problem where the CookieCrumber + set cookies even though the user entered an incorrect password. + RESPONSE.unauthorized() now cancels the cookie response + header. The new login_form and logged_in form both try to + invoke unauthorized(), so make sure you install the new + forms. + + - Implement the notional 'search results item' interface for + SkinnedFolder. + + - Corrected solecism in Topic (use of 'criteria' for singular); + removed the need to know about the funky generated IDs for + criterion objects. Fixed derived bug in skins (tracker #408). + + - Modified error-logging code to avoid potential leaks of + traceback frame. + + - Made Document's 'manage_FTPget' use 'EditableBody', rather than + accessing 'text' attribute directly (improves reusability). + Likewise 'Document.SearchableText'. + + - Merged Seb Bacon's refactoring of 'getDefaultView' into + 'CMFCore.utils._getView'; clients can now specify a view by name, + as well. + + - Made the default content type for Image 'image/png', instead + of the unintuitive 'text/html' inherited from DefaultDublinCoreImpl + (tracker #384). + + - Corrected typo in ActionsTool which broke actions for the root + portal object (tracker #379). + + - Updated the MemberDataTool to use an OOBTree, instead of the + old-style BTree, to store member data wrappers (CMF Tracker 375). + + - Corrected 'personalize_form' to use 'getProperty' where feasible, + rather than relying on direct attribute access (tracker #372). + + - Removed an incompatibility with LoginManager in + CMFCore.MembershipTool (tracker #365). + + - Removed an infinite loop condition that arises when + MembershipTool.createMemberArea gets called inside wrapUser + (this could only happen if "Create Member Area" was checked + in the Membership tool.) + + - Added new 'TitleOrId' skin method, and updated skins to + depend on it rather than SimpleItem.title_or_id. + + - Made unit tests runnable without reliance on 'zctl test'. + + - Corrected initial column set in catalog to include + "ModificationDate" instead of "ModifiedDate". + + - Ensured that object is recatalogued (e.g., after setting + 'portal_type'; thanks to Florent Guillaume). + + - Removed silly dependency of 'CatalogTool.searchResults' on + REQUEST (catalog already does the Right Thing (tm) when no + REQUEST is available). Note that this requires updates to + the 'search' skins, as they didn't pass it in. + + - Changed redirect target after rejecting an item to the search + page for pending content items; this resolves the problem + that the non-Manager reviewr who rejects an item no longer + has View permission on it, and therefore gets an Unauthorized + when redirecting to the object's view action. + + - Moved generation of the "Add to Favorites" and "My Favorites" + links from the CMFCore/ActionsTool into the + CMFDefault/MembershipTool, which is a more logical location + for it because it relies on information from the membership tool. + + - Made Topic directly publishable (like PortalContent), using its + 'view' action (or the first action found, if view is not present). + + - Set title for newly-created member folders; fixes + breadcrumbs that expect a title on the object. + + - Allow URLs with query strings in StructuredText links. + + - Update 'news_box' to search based on 'Type', rather than + 'meta_type'. + + - Fix 'SkinnedFolder.Creator()' to call 'getOwner()' with + info argument. + + - Made CookieCrumbler check for 'WEBDAV_SOURCE_PORT' + environment variable (supplied by Zope 2.4.1+), and bail on + intercepting authentication if found. + + - Included 'Owner' in list of significant roles returned by + 'MembershipTool.getPortalRoles' (e.g., so that the + "Local Roles" interface can allow assignment of it). + + - Allow users with local role of "Reviewer" to see the "pending + review" action. + + - Made TypesTool, rather than individual type objects, + responsible for generating "immediate view" URL; type + objects now return the newly-created object, which makes + scripting them much simpler. + + - Remove fossilized reference to 'getSecurityManager' from + 'PortalContent._verifyActionPermissions'. + + - Modified the redirect after "Add to Favorites" to us the + view action, rather than 'view'. + + - Fixed 'Document.guessFormat()' to use + 'utils.html_headcheck()' instead of 'bodyfinder' regex to + detect structured text versus html; avoids recursion limit + for large HTML files. + + - Removed spurious 'afterCreate' protocol for content objects. + + - Added mapping of 'css' file extension to FSDTMLMethods. + + - Modifed PortalFolder.listFolderContents to handle + permission-based filtering; duplicates what skip_unauthorized + is doing in DocumentTemplate/DT_IN.py (but works for ZPT as + well). + + - Modified CMFDefault.RegistrationTool.addMember to avoid + flunking validation if properties are not passed (Tracker + #335). + + - Applied patch from Chris Withers to 'register' skin method; + the patch which avoids quoting problems for the error message + if a problem occurs (Tracker #339). + + - Added 'DiscussionItem.replyCount' (Tracker #328). + 'DiscussionItem.hasReplies' now returns only a boolean value. + Standard skins don't call 'replyCount' due to performance + concerns. + + - Factored content filtering logic into a Python Script. + + - Improved handling of multiple rename targets (thanks to Chris + Withers for the patch.) + + - Completed conversion of form targets from DTML Methods to + Python Scripts. + + - Improved compatibility with Zope 2.4: + + o support for new "restricted execution" mode; + + o support for new catalog initialization API. + + o updated 'test_all' unit test drivers to use standard + 'unittest' module from Python 2.1 (it no longer has + 'JUnitTestTextRunner' class). + 1.1 final (2001/06/20) New features From tseaver@zope.com Sat Jan 5 01:24:33 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:24:33 -0500 Subject: [CMF-checkins] CVS: Products/CMFDefault/skins/generic - doFormSearch.py:1.1.2.1 search.dtml:1.6.16.1 search_form.dtml:1.7.20.1 Message-ID: <200201050124.g051OX424392@cvs.baymountain.com> Update of /cvs-repository/Products/CMFDefault/skins/generic In directory cvs.zope.org:/tmp/cvs-serv24332/CMFDefault/skins/generic Modified Files: Tag: tseaver-tracker_332-branch search.dtml search_form.dtml Added Files: Tag: tseaver-tracker_332-branch doFormSearch.py Log Message: - Worked around Opera's strange insistence on selecting an option, even for multi-select lists (Tracker #332). === Added File Products/CMFDefault/skins/generic/doFormSearch.py === ## Script (Python) "doFormSearch" ##parameters=REQUEST ##title=Pre-process form variables, then return catalog query results. ## form_vars = {} select_vars = ( 'review_state' , 'Subject' , 'created' , 'Type' ) for k, v in REQUEST.form.items(): if k in select_vars: if same_type( v, [] ): v = filter( None, v ) if not v: continue form_vars[ k ] = v return context.portal_catalog( form_vars ) === Products/CMFDefault/skins/generic/search.dtml 1.6 => 1.6.16.1 ===

Search Results

- +

Found items matching "&dtml-SearchableText;".

=== Products/CMFDefault/skins/generic/search_form.dtml 1.7 => 1.7.20.1 ===
- + + + +
As a reviewer, you may search for items based on their @@ -56,6 +57,7 @@
Subject
Subject +
Find new items since... - + @@ -115,14 +118,13 @@
Item type - +
You may limit your results to particular kinds of From tseaver@zope.com Sat Jan 5 01:29:40 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:29:40 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/generic - doFormSearch.py:1.1.4.1 search.dtml:1.6.4.1 search_form.dtml:1.7.8.1 Message-ID: <200201050129.g051Tec25594@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/generic In directory cvs.zope.org:/tmp/cvs-serv25574/CMFDefault/skins/generic Modified Files: Tag: CMF-1_2-branch search.dtml search_form.dtml Added Files: Tag: CMF-1_2-branch doFormSearch.py Log Message: - Worked around Opera's strange insistence on selecting an option, even for multi-select lists (Tracker #332). === Added File CMF/CMFDefault/skins/generic/doFormSearch.py === ## Script (Python) "doFormSearch" ##parameters=REQUEST ##title=Pre-process form variables, then return catalog query results. ## form_vars = {} select_vars = ( 'review_state' , 'Subject' , 'created' , 'Type' ) for k, v in REQUEST.form.items(): if k in select_vars: if same_type( v, [] ): v = filter( None, v ) if not v: continue form_vars[ k ] = v return context.portal_catalog( form_vars ) === CMF/CMFDefault/skins/generic/search.dtml 1.6 => 1.6.4.1 ===

Search Results

- +

Found items matching "&dtml-SearchableText;".

=== CMF/CMFDefault/skins/generic/search_form.dtml 1.7 => 1.7.8.1 ===
- + + + +
As a reviewer, you may search for items based on their @@ -56,6 +57,7 @@
Subject
Subject +
Find new items since... - + @@ -115,14 +118,13 @@
Item type - +
You may limit your results to particular kinds of From tseaver@zope.com Sat Jan 5 01:30:09 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:30:09 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.8 Message-ID: <200201050130.g051U9x25676@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv25574 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Worked around Opera's strange insistence on selecting an option, even for multi-select lists (Tracker #332). === CMF/CHANGES.txt 1.36.2.7 => 1.36.2.8 === Bugs Fixed + - Worked around Opera's strange insistence on selecting an option, + even for multi-select lists (Tracker #332). + - Hardened CMFCore to initialize correctly in the absence of the PageTemplates product (Tracker #430). From tseaver@zope.com Sat Jan 5 01:30:20 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:30:20 -0500 Subject: [CMF-checkins] CVS: Products/CMFDefault/skins/generic - doFormSearch.py:1.2 search.dtml:1.7 search_form.dtml:1.8 Message-ID: <200201050130.g051UK225693@cvs.baymountain.com> Update of /cvs-repository/Products/CMFDefault/skins/generic In directory cvs.zope.org:/tmp/cvs-serv25627/CMFDefault/skins/generic Modified Files: search.dtml search_form.dtml Added Files: doFormSearch.py Log Message: - Merge fix for Tracker #332 from 1.2 branch. === Products/CMFDefault/skins/generic/doFormSearch.py 1.1 => 1.2 === +##parameters=REQUEST +##title=Pre-process form variables, then return catalog query results. +## +form_vars = {} +select_vars = ( 'review_state' + , 'Subject' + , 'created' + , 'Type' + ) + +for k, v in REQUEST.form.items(): + + if k in select_vars: + + if same_type( v, [] ): + v = filter( None, v ) + if not v: + continue + + form_vars[ k ] = v + +return context.portal_catalog( form_vars ) === Products/CMFDefault/skins/generic/search.dtml 1.6 => 1.7 ===

Search Results

- +

Found items matching "&dtml-SearchableText;".

=== Products/CMFDefault/skins/generic/search_form.dtml 1.7 => 1.8 ===
- + + + +
As a reviewer, you may search for items based on their @@ -56,6 +57,7 @@
Subject
Subject +
Find new items since... - + @@ -115,14 +118,13 @@
Item type - +
You may limit your results to particular kinds of From tseaver@zope.com Sat Jan 5 01:59:29 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:59:29 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/content - favorite_view.dtml:1.1.30.1 Message-ID: <200201050159.g051xTr32518@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/content In directory cvs.zope.org:/tmp/cvs-serv32006/skins/content Modified Files: Tag: tseaver-tracker_419-branch favorite_view.dtml Log Message: - Ensure that Favorites display the correct, absolute URL to their target, without needing to have tag set (Tracker #419). === CMF/CMFDefault/skins/content/favorite_view.dtml 1.1 => 1.1.30.1 === -

-"> -

-

+

">

From tseaver@zope.com Sat Jan 5 01:59:29 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:59:29 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/zpt_content - favorite_view.pt:1.2.16.1 Message-ID: <200201050159.g051xTg32520@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/zpt_content In directory cvs.zope.org:/tmp/cvs-serv32006/skins/zpt_content Modified Files: Tag: tseaver-tracker_419-branch favorite_view.pt Log Message: - Ensure that Favorites display the correct, absolute URL to their target, without needing to have tag set (Tracker #419). === CMF/CMFDefault/skins/zpt_content/favorite_view.pt 1.2 => 1.2.16.1 ===

Link: /index_html

From tseaver@zope.com Sat Jan 5 01:59:59 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 20:59:59 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.13.16.1 Message-ID: <200201050159.g051xxL32556@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv32006 Modified Files: Tag: tseaver-tracker_419-branch Favorite.py Log Message: - Ensure that Favorites display the correct, absolute URL to their target, without needing to have tag set (Tracker #419). === CMF/CMFDefault/Favorite.py 1.13 => 1.13.16.1 === portal_url = getToolByName(self, 'portal_url') if self.remote_url: - return portal_url.getPortalPath() + '/' + self.remote_url + return portal_url() + '/' + self.remote_url else: - return portal_url.getPortalPath() + return portal_url() def getIcon(self, relative_to_portal=0): """ From tseaver@zope.com Sat Jan 5 02:01:24 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:01:24 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/content - favorite_view.dtml:1.1.16.1 Message-ID: <200201050201.g0521O432692@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/content In directory cvs.zope.org:/tmp/cvs-serv32669/CMFDefault/skins/content Modified Files: Tag: CMF-1_2-branch favorite_view.dtml Log Message: - Merge fix for Tracker #419 from branch. === CMF/CMFDefault/skins/content/favorite_view.dtml 1.1 => 1.1.16.1 === -

-"> -

-

+

">

From tseaver@zope.com Sat Jan 5 02:01:24 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:01:24 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/zpt_content - favorite_view.pt:1.2.2.1 Message-ID: <200201050201.g0521Oc32694@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/zpt_content In directory cvs.zope.org:/tmp/cvs-serv32669/CMFDefault/skins/zpt_content Modified Files: Tag: CMF-1_2-branch favorite_view.pt Log Message: - Merge fix for Tracker #419 from branch. === CMF/CMFDefault/skins/zpt_content/favorite_view.pt 1.2 => 1.2.2.1 ===

Link: /index_html

From tseaver@zope.com Sat Jan 5 02:01:53 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:01:53 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.9 Message-ID: <200201050201.g0521rx32704@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv32669 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Merge fix for Tracker #419 from branch. === CMF/CHANGES.txt 1.36.2.8 => 1.36.2.9 === Bugs Fixed + - Ensure that Favorites display the correct, absolute URL to their + target, without needing to have tag set (Tracker #419). + - Worked around Opera's strange insistence on selecting an option, even for multi-select lists (Tracker #332). From tseaver@zope.com Sat Jan 5 02:01:53 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:01:53 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.13.2.1 Message-ID: <200201050201.g0521rd32707@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv32669/CMFDefault Modified Files: Tag: CMF-1_2-branch Favorite.py Log Message: - Merge fix for Tracker #419 from branch. === CMF/CMFDefault/Favorite.py 1.13 => 1.13.2.1 === portal_url = getToolByName(self, 'portal_url') if self.remote_url: - return portal_url.getPortalPath() + '/' + self.remote_url + return portal_url() + '/' + self.remote_url else: - return portal_url.getPortalPath() + return portal_url() def getIcon(self, relative_to_portal=0): """ From tseaver@zope.com Sat Jan 5 02:02:34 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:02:34 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/content - favorite_view.dtml:1.2 Message-ID: <200201050202.g0522Yq00674@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/content In directory cvs.zope.org:/tmp/cvs-serv656/CMFDefault/skins/content Modified Files: favorite_view.dtml Log Message: - Merge fix for Tracker #419 from branch. === CMF/CMFDefault/skins/content/favorite_view.dtml 1.1 => 1.2 === -

-"> -

-

+

">

From tseaver@zope.com Sat Jan 5 02:02:34 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:02:34 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/zpt_content - favorite_view.pt:1.3 Message-ID: <200201050202.g0522YR00676@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/zpt_content In directory cvs.zope.org:/tmp/cvs-serv656/CMFDefault/skins/zpt_content Modified Files: favorite_view.pt Log Message: - Merge fix for Tracker #419 from branch. === CMF/CMFDefault/skins/zpt_content/favorite_view.pt 1.2 => 1.3 ===

Link: /index_html

From tseaver@zope.com Sat Jan 5 02:03:04 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 21:03:04 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.14 Message-ID: <200201050203.g05234P00821@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv656/CMFDefault Modified Files: Favorite.py Log Message: - Merge fix for Tracker #419 from branch. === CMF/CMFDefault/Favorite.py 1.13 => 1.14 === portal_url = getToolByName(self, 'portal_url') if self.remote_url: - return portal_url.getPortalPath() + '/' + self.remote_url + return portal_url() + '/' + self.remote_url else: - return portal_url.getPortalPath() + return portal_url() def getIcon(self, relative_to_portal=0): """ From tseaver@zope.com Sat Jan 5 03:05:59 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 22:05:59 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - DirectoryView.py:1.16.2.1 Message-ID: <200201050305.g0535xr15114@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv15008/CMFCore Modified Files: Tag: CMF-1_2-branch DirectoryView.py Log Message: - Added 'CMFDefault.Upgrade.upgrade_decor_skins' external method to convert existing sites which had installed skin directories from the now-deprecated 'CMFDecor' product (Tracker #434). Added note explaining the issue, and the workaround, to 'ISSUES.txt'. === CMF/CMFCore/DirectoryView.py 1.16 => 1.16.2.1 === security.declareProtected(ManagePortal, 'manage_properties') - def manage_properties( self, dirpath, REQUEST ): + def manage_properties( self, dirpath, REQUEST=None ): """ Update the directory path of the DV. """ self.__dict__['_real']._dirpath = dirpath - REQUEST['RESPONSE'].redirect( '%s/manage_propertiesForm' - % self.absolute_url() ) + if REQUEST is not None: + REQUEST['RESPONSE'].redirect( '%s/manage_propertiesForm' + % self.absolute_url() ) security.declareProtected(AccessContentsInformation, 'getCustomizableObject') From tseaver@zope.com Sat Jan 5 03:05:59 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 22:05:59 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/Extensions - Upgrade.py:1.1.2.1 Message-ID: <200201050305.g0535xC15116@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/Extensions In directory cvs.zope.org:/tmp/cvs-serv15008/CMFDefault/Extensions Added Files: Tag: CMF-1_2-branch Upgrade.py Log Message: - Added 'CMFDefault.Upgrade.upgrade_decor_skins' external method to convert existing sites which had installed skin directories from the now-deprecated 'CMFDecor' product (Tracker #434). Added note explaining the issue, and the workaround, to 'ISSUES.txt'. === Added File CMF/CMFDefault/Extensions/Upgrade.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """ Utility functions for upgrading CMFDefault-based sites. """ from Acquisition import aq_inner import string def upgrade_decor_skins( self ): """ Upgrade old skin diretories loaded from 'CMFDecor' to load from 'CMFDefault' (and zap the 'zpt_images' one). """ log = [] DELETED_SKINS = ( 'zpt_images' , ) MOVED_SKINS = ( 'zpt_content' , 'zpt_control' , 'zpt_generic' ) skins_tool = aq_inner( self ).portal_skins # start from CMFSite! for deleted in DELETED_SKINS: try: skins_tool._delObject( deleted ) except AttributeError: pass else: log.append( 'Deleted CMFDecor skin directory: %s' % deleted ) for moved in MOVED_SKINS: skin_dir = getattr( skins_tool, moved, None ) if skin_dir is not None: skin_dir.manage_properties( dirpath='Products/CMFDefault/skins/%s' % moved ) log.append( 'Updated CMFDecor skin directory to CMFDefault: %s' % moved ) return string.join( log, '\n' ) From tseaver@zope.com Sat Jan 5 03:06:29 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 4 Jan 2002 22:06:29 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.10 ISSUES.txt:1.2.14.1 Message-ID: <200201050306.g0536TS15175@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv15008 Modified Files: Tag: CMF-1_2-branch CHANGES.txt ISSUES.txt Log Message: - Added 'CMFDefault.Upgrade.upgrade_decor_skins' external method to convert existing sites which had installed skin directories from the now-deprecated 'CMFDecor' product (Tracker #434). Added note explaining the issue, and the workaround, to 'ISSUES.txt'. === CMF/CHANGES.txt 1.36.2.9 => 1.36.2.10 === Bugs Fixed + - Added 'CMFDefault.Upgrade.upgrade_decor_skins' external method to + convert existing sites which had installed skin directories from the + now-deprecated 'CMFDecor' product (Tracker #434). Added note + explaining the issue, and the workaround, to 'ISSUES.txt'. + - Ensure that Favorites display the correct, absolute URL to their target, without needing to have tag set (Tracker #419). === CMF/ISSUES.txt 1.2 => 1.2.14.1 === +CMF 1.2: Known Issues Overview - This document describes known issues with the beta release of - the Content Management Framework (CMF), version 1.0. + This document describes known issues with the release of + the Content Management Framework (CMF), version 1.2. For more information on the CMF, please see the "website", http://cmf.zope.org. - Package and Module Name Changes - In order to improve the future maintainability of the CMF, - and as part of the "rebranding" of the product from "Portal - Toolkit" to "Content Management Framework", we have moved the - code base to a new set of packages: - - 'CMF' -- the "top-level" package, replaces 'ZopePTK'. - - 'CMFCore' -- provides essential interfaces and services of - the framework. All CMF sites will use this package; - most will not replace the services it provides. Most - modules in this package came from the old 'PTKBase' - package. - - 'CMFDefault' -- provides a set of content objects and - services which allow construction of a useful CMF site - "out of the box"; while many sites will use these - objects directly, many will extend or replace them. Most - modules in this package came from the old 'PTKDemo' - package. - - 'CMFTopic' -- provides a new, "add-in" content object, the - Topic. Topics represent "canned" catalog queries, and - are useful for presenting "logical collections" of - content, based on common metadata. - - CVS Changes - - Until this release, the "canonical" CVS repository for the - PTK/CMF has been on the public CVS mirror, cvs.zope.org. - This placement has been somewhat problematic: because all - the *other* CVS code is replicated from Digital Creations' - internal CVS repository, tags and branches created on PTK/CMF - files tended to get "forgotten" during the synchronization. - Going forward, we will keep the CMF repository alongside the - main Zope repository, and replicate it to the public mirror. - - Migrating Existing PTK Sites - - The changes to package and module names (mentioned above) - have significant impacts for existing PTK sites: instances - created from classes defined in 'PTKBase' or 'PTKDemo' will - need to be "re-seated" as instances of their cognates in - 'CMFCore' or 'CMFDefault'. In the past, we have arranged to - do such reseating "in place"; for this change, however, we - plan not to require a "copying" migration, in order to remove - the need to maintain the backward-compatibility cruft - required by the in-place strategy. - - We will provide a script shortly to aid this migration (in - fact, it will be the script which we use to migrate the - "dogbowl" site this week. - - N.B.: This script is 'CMFDefault.migrate_ptk.py'. + Issue: 'CMFDecor'-based skins lost in upgrade to CMF 1.2 + + When migrating a site to 1.2 from CMF, where the skin directories + from the 'CMFDecor' product had been installed, such directories + are no longer present (the product has been merged into 'CMFDefault'). + + See the "tracker issue", + http://www.zope.org/Products/PTK/Tracker/434 + + Workaround + + 1. Create an ExternalMethod in the root of your CMF site + using the following properties: + + *Id* -- upgrade_decor_skins + + *Title* -- Upgrade CMFDecor Skin Directories + + *Module Name* -- CMFDefault.Upgrade + + *Function Name* -- upgrade_decor_skins + + 2. Run this method by clicking on its "Test" tab. From andrew@zope.com Mon Jan 7 16:26:27 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 11:26:27 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - SyndicationTool.py:1.11 Message-ID: <200201071626.g07GQR011475@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv11414/CMFDefault Modified Files: SyndicationTool.py Log Message: *Tracker #421 fix *added aq_base(obj) to in enableSyndication and isSyndicationAllowed methods to make sure it's not acquired from a parent object. === CMF/CMFDefault/SyndicationTool.py 1.10 => 1.11 === from Globals import HTMLFile, package_home, InitializeClass import string -from Acquisition import aq_inner, aq_parent +from Acquisition import aq_base, aq_inner, aq_parent from DateTime import DateTime from AccessControl import ClassSecurityInfo, SecurityManagement from Products.CMFCore.CMFCorePermissions import ManagePortal @@ -177,7 +177,7 @@ if not self.isSiteSyndicationAllowed(): raise 'Syndication is Disabled' else: - if hasattr(obj, 'syndication_information'): + if hasattr(aq_base(obj), 'syndication_information'): raise 'Syndication Information Exists' syInfo = SyndicationInformation() obj._setObject('syndication_information', syInfo) @@ -235,8 +235,7 @@ particular obj is enabled, allowing for turning on only specific folders for syndication. """ - #import pdb; pdb.set_trace() - syInfo = getattr(obj, 'syndication_information', + syInfo = getattr(aq_base(obj), 'syndication_information', None) if syInfo is None: return 0 From tseaver@zope.com Mon Jan 7 17:14:03 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 12:14:03 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.11 Message-ID: <200201071714.g07HE3e23110@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv23049 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Allow sub-folders to have different syndication properties than parents (Tracker #421). === CMF/CHANGES.txt 1.36.2.10 => 1.36.2.11 === Bugs Fixed + - Allowed sub-folders to have different syndication properties + than parents (Tracker #421). + - Added 'CMFDefault.Upgrade.upgrade_decor_skins' external method to convert existing sites which had installed skin directories from the now-deprecated 'CMFDecor' product (Tracker #434). Added note From tseaver@zope.com Mon Jan 7 17:14:03 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 12:14:03 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - SyndicationTool.py:1.9.2.1 Message-ID: <200201071714.g07HE3b23113@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv23049/CMFDefault Modified Files: Tag: CMF-1_2-branch SyndicationTool.py Log Message: - Allow sub-folders to have different syndication properties than parents (Tracker #421). === CMF/CMFDefault/SyndicationTool.py 1.9 => 1.9.2.1 === from Globals import HTMLFile, package_home, InitializeClass import string -from Acquisition import aq_inner, aq_parent +from Acquisition import aq_base, aq_inner, aq_parent from DateTime import DateTime from AccessControl import ClassSecurityInfo, SecurityManagement from Products.CMFCore.CMFCorePermissions import ManagePortal @@ -155,7 +155,7 @@ if not self.isSiteSyndicationAllowed(): raise 'Syndication is Disabled' else: - if hasattr(obj, 'syndication_information'): + if hasattr(aq_base(obj), 'syndication_information'): raise 'Syndication Information Exists' syInfo = SyndicationInformation() obj._setObject('syndication_information', syInfo) @@ -213,8 +213,7 @@ particular obj is enabled, allowing for turning on only specific folders for syndication. """ - #import pdb; pdb.set_trace() - syInfo = getattr(obj, 'syndication_information', + syInfo = getattr(aq_base(obj), 'syndication_information', None) if syInfo is None: return 0 From andrew@zope.com Mon Jan 7 18:46:13 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 13:46:13 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - MembershipTool.py:1.19 Message-ID: <200201071846.g07IkD611469@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv11416/CMFDefault Modified Files: MembershipTool.py Log Message: *Bug in addToFavorites action; was portal_url now object_url === CMF/CMFDefault/MembershipTool.py 1.18 => 1.19 === , description='Add this item to your favorites' , action=Expression( - text='string: ${portal_url}/addtoFavorites') + text='string: ${object_url}/addtoFavorites') , permissions=(View,) , category='user' , condition=Expression( From andrew@zope.com Mon Jan 7 18:53:05 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 13:53:05 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/control - addtoFavorites.py:1.2.18.1 Message-ID: <200201071853.g07Ir5A13228@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/control In directory cvs.zope.org:/tmp/cvs-serv12741/CMFDefault/skins/control Modified Files: Tag: andrew-tracker440_branch addtoFavorites.py Log Message: *added TitleOrId to addtoFavorites python script for cases where something might not have a title. === CMF/CMFDefault/skins/control/addtoFavorites.py 1.2 => 1.2.18.1 === new_id='fav_' + str(int( context.ZopeTime())) myPath=context.portal_url.getRelativeUrl(context) -targetFolder.invokeFactory( 'Favorite', id=new_id, title=context.Title(), remote_url=myPath) +targetFolder.invokeFactory( 'Favorite', id=new_id, title=context.TitleOrId(), remote_url=myPath) url = '%s/%s' % (context.absolute_url(), context.getTypeInfo().getActionById('view','')) return context.REQUEST.RESPONSE.redirect(url) From andrew@zope.com Mon Jan 7 18:56:27 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 13:56:27 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/control - addtoFavorites.py:1.3 Message-ID: <200201071856.g07IuRB13901@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/control In directory cvs.zope.org:/tmp/cvs-serv13894/CMFDefault/skins/control Modified Files: addtoFavorites.py Log Message: *merge to head change for tracker #440. === CMF/CMFDefault/skins/control/addtoFavorites.py 1.2 => 1.3 === new_id='fav_' + str(int( context.ZopeTime())) myPath=context.portal_url.getRelativeUrl(context) -targetFolder.invokeFactory( 'Favorite', id=new_id, title=context.Title(), remote_url=myPath) +targetFolder.invokeFactory( 'Favorite', id=new_id, title=context.TitleOrId(), remote_url=myPath) url = '%s/%s' % (context.absolute_url(), context.getTypeInfo().getActionById('view','')) return context.REQUEST.RESPONSE.redirect(url) From tseaver@zope.com Mon Jan 7 19:27:32 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 14:27:32 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.12 Message-ID: <200201071927.g07JRW421748@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv21390 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Use ID to label Favorite when target has an empty Title (Tracker #440). === CMF/CHANGES.txt 1.36.2.11 => 1.36.2.12 === Bugs Fixed + - Use ID to label Favorite when target has an empty Title (Tracker #440). + - Allowed sub-folders to have different syndication properties than parents (Tracker #421). From tseaver@zope.com Mon Jan 7 19:27:32 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 14:27:32 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/control - addtoFavorites.py:1.2.4.1 Message-ID: <200201071927.g07JRWx21750@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/control In directory cvs.zope.org:/tmp/cvs-serv21390/CMFDefault/skins/control Modified Files: Tag: CMF-1_2-branch addtoFavorites.py Log Message: - Use ID to label Favorite when target has an empty Title (Tracker #440). === CMF/CMFDefault/skins/control/addtoFavorites.py 1.2 => 1.2.4.1 === new_id='fav_' + str(int( context.ZopeTime())) myPath=context.portal_url.getRelativeUrl(context) -targetFolder.invokeFactory( 'Favorite', id=new_id, title=context.Title(), remote_url=myPath) +targetFolder.invokeFactory( 'Favorite', id=new_id, title=context.TitleOrId(), remote_url=myPath) url = '%s/%s' % (context.absolute_url(), context.getTypeInfo().getActionById('view','')) return context.REQUEST.RESPONSE.redirect(url) From tseaver@zope.com Mon Jan 7 19:34:26 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 14:34:26 -0500 Subject: [CMF-checkins] CVS: Products/CMFTopic/skins/zpt_topic - listc_edit.pt:1.1.22.1 Message-ID: <200201071934.g07JYQE23660@cvs.baymountain.com> Update of /cvs-repository/Products/CMFTopic/skins/zpt_topic In directory cvs.zope.org:/tmp/cvs-serv23591/CMFTopic/skins/zpt_topic Modified Files: Tag: tseaver-listcrit_operator-branch listc_edit.pt Log Message: - Add 'operator' attribute to ListCriterion, to permit overriding the implicit 'or' operator used for keyword indexes such as 'Subject'. === Products/CMFTopic/skins/zpt_topic/listc_edit.pt 1.1 => 1.1.22.1 === -
- - - - -
 
- Value: - -
+ + + + +
  + Value:
+
+ Operator:
+ +
From tseaver@zope.com Mon Jan 7 19:34:27 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 14:34:27 -0500 Subject: [CMF-checkins] CVS: Products/CMFTopic/tests - test_ListC.py:1.6.10.1 Message-ID: <200201071934.g07JYRo23662@cvs.baymountain.com> Update of /cvs-repository/Products/CMFTopic/tests In directory cvs.zope.org:/tmp/cvs-serv23591/CMFTopic/tests Modified Files: Tag: tseaver-listcrit_operator-branch test_ListC.py Log Message: - Add 'operator' attribute to ListCriterion, to permit overriding the implicit 'or' operator used for keyword indexes such as 'Subject'. === Products/CMFTopic/tests/test_ListC.py 1.6 => 1.6.10.1 === items = listc.getCriteriaItems() - self.assertEqual( items[0][1], tuple( abc ) ) + self.failUnless( 'foofield' in map( lambda x: x[0], items ) ) + self.failUnless( tuple( abc ) in map( lambda x: x[1], items ) ) + + def test_operator( self ): + + from Products.CMFTopic.ListCriterion import ListCriterion + listc = ListCriterion( 'foo', 'foofield' ) + + abc = [ 'a', 'b', 'c' ] + + listc.edit( abc ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 1 ) + + listc.edit( abc, 'or' ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 2 ) + self.failUnless( ( 'foofield_operator', 'or' ) in items ) + + listc.edit( abc, 'and' ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 2 ) + self.failUnless( ( 'foofield_operator', 'and' ) in items ) def test_suite(): return unittest.makeSuite( ListCriterionTests ) From tseaver@zope.com Mon Jan 7 19:34:56 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 14:34:56 -0500 Subject: [CMF-checkins] CVS: Products/CMFTopic - ListCriterion.py:1.8.10.1 Message-ID: <200201071934.g07JYuS23672@cvs.baymountain.com> Update of /cvs-repository/Products/CMFTopic In directory cvs.zope.org:/tmp/cvs-serv23591/CMFTopic Modified Files: Tag: tseaver-listcrit_operator-branch ListCriterion.py Log Message: - Add 'operator' attribute to ListCriterion, to permit overriding the implicit 'or' operator used for keyword indexes such as 'Subject'. === Products/CMFTopic/ListCriterion.py 1.8 => 1.8.10.1 === from AccessControl import ClassSecurityInfo -import string, operator +import string class ListCriterion( AbstractCriterion ): """ @@ -39,7 +39,7 @@ security = ClassSecurityInfo() - _editableAttributes = ( 'value', ) + _editableAttributes = ( 'value', 'operator' ) def __init__( self, id, field ): self.id = id @@ -52,6 +52,7 @@ Restore to original value. """ self.value = ( '', ) # *Not* '()', which won't do at all! + self.operator = None security.declareProtected( TopicPermissions.ChangeTopics, 'getEditForm' ) def getEditForm( self ): @@ -62,7 +63,7 @@ return "listc_edit" security.declareProtected( TopicPermissions.ChangeTopics, 'edit' ) - def edit( self, value=None ): + def edit( self, value=None, operator=None ): """ Update the value we match against. """ @@ -73,6 +74,11 @@ value = string.split( value, '\n' ) self.value = tuple( value ) + if not operator: + operator = None + + self.operator = operator + security.declareProtected( CMFCorePermissions.View, 'getCriteriaItems' ) def getCriteriaItems( self ): """ @@ -80,8 +86,17 @@ (used by 'Topic.buildQuery()'). """ # filter out empty strings - value = tuple( filter( operator.truth, self.value ) ) - return operator.truth( value ) and ( ( self.field, self.value ), ) or () + result = [] + + value = tuple( filter( None, self.value ) ) + if not value: + return () + result.append( ( self.field, self.value ), ) + + if self.operator is not None: + result.append( ( '%s_operator' % self.field, self.operator ) ) + + return tuple( result ) From tseaver@zope.com Mon Jan 7 19:34:56 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 7 Jan 2002 14:34:56 -0500 Subject: [CMF-checkins] CVS: Products/CMFTopic/skins/topic - listc_edit.dtml:1.3.30.1 Message-ID: <200201071934.g07JYuQ23675@cvs.baymountain.com> Update of /cvs-repository/Products/CMFTopic/skins/topic In directory cvs.zope.org:/tmp/cvs-serv23591/CMFTopic/skins/topic Modified Files: Tag: tseaver-listcrit_operator-branch listc_edit.dtml Log Message: - Add 'operator' attribute to ListCriterion, to permit overriding the implicit 'or' operator used for keyword indexes such as 'Subject'. === Products/CMFTopic/skins/topic/listc_edit.dtml 1.3 => 1.3.30.1 ===
 Value:
-
+ Value:
+
+ Operator:
+ + + +
From andrew@zope.com Mon Jan 7 22:01:27 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 17:01:27 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.42.6.1 Message-ID: <200201072201.g07M1RO25482@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv25420 Modified Files: Tag: andrew-tracker431_branch CHANGES.txt Log Message: *added external method to delete, add, and rebuild indexes for CMF sites which started on Zope2.3 and are migrating to Zope2.4 === CMF/CHANGES.txt 1.42 => 1.42.6.1 === Bug Fixes + - Added external method update_catalogIndexes.py to run as part of a upgrade + to CMFs migrating to Zope2.4+ from from CMF sites which were built using + Zope2.3 catalog + - Made links emitted by 'topic_view' play nice with virtual hosting (Tracker #433). From andrew@zope.com Mon Jan 7 22:01:27 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 17:01:27 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/Extensions - update_catalogIndexes.py:1.1.2.1 Message-ID: <200201072201.g07M1RS25484@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/Extensions In directory cvs.zope.org:/tmp/cvs-serv25420/CMFDefault/Extensions Added Files: Tag: andrew-tracker431_branch update_catalogIndexes.py Log Message: *added external method to delete, add, and rebuild indexes for CMF sites which started on Zope2.3 and are migrating to Zope2.4 === Added File CMF/CMFDefault/Extensions/update_catalogIndexes.py === from Products.CMFCore.utils import getToolByName def update_catalogIndexes(self): ''' External method to drop, re-add, and rebuild catalog Indexes for migrated CMF sites from Zope 2.3 to 2.4+. ''' rIndexes = {'allowedRolesAndUsers': 'KeywordIndex' , 'effective': 'FieldIndex' , 'expires': 'FieldIndex'} ct = getToolByName(self, 'portal_catalog') map(lambda x, ct=ct: ct.delIndex(x), rIndexes.keys()) map(lambda x, ct=ct: ct.addIndex(x[0], x[1]), rIndexes.items()) ct.manage_reindexIndex(ids=rIndexes.keys()) return 'Catalog Indexes rebuilt.' From andrew@zope.com Mon Jan 7 22:27:15 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 17:27:15 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.13 Message-ID: <200201072227.g07MRFA31535@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv31287 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *external method for tracker 431 *updated CHANGES.txt to reflect bug fix. === CMF/CHANGES.txt 1.36.2.12 => 1.36.2.13 === Bugs Fixed + - Added external method update_catalogIndexes.py to run as part of a + upgrade to CMFs migrating to Zope2.4+ from from CMF sites which were built + using Zope2.3 catalog + - Use ID to label Favorite when target has an empty Title (Tracker #440). - Allowed sub-folders to have different syndication properties From andrew@zope.com Mon Jan 7 22:27:15 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 7 Jan 2002 17:27:15 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/Extensions - update_catalogIndexes.py:1.1.4.1 Message-ID: <200201072227.g07MRF531536@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/Extensions In directory cvs.zope.org:/tmp/cvs-serv31287/CMFDefault/Extensions Added Files: Tag: CMF-1_2-branch update_catalogIndexes.py Log Message: *external method for tracker 431 *updated CHANGES.txt to reflect bug fix. === Added File CMF/CMFDefault/Extensions/update_catalogIndexes.py === from Products.CMFCore.utils import getToolByName def update_catalogIndexes(self): ''' External method to drop, re-add, and rebuild catalog Indexes for migrated CMF sites from Zope 2.3 to 2.4+. ''' rIndexes = {'allowedRolesAndUsers': 'KeywordIndex' , 'effective': 'FieldIndex' , 'expires': 'FieldIndex'} ct = getToolByName(self, 'portal_catalog') map(lambda x, ct=ct: ct.delIndex(x), rIndexes.keys()) map(lambda x, ct=ct: ct.addIndex(x[0], x[1]), rIndexes.items()) ct.manage_reindexIndex(ids=rIndexes.keys()) return 'Catalog Indexes rebuilt.' From tseaver@zope.com Tue Jan 8 11:54:21 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 06:54:21 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic/tests - test_ListC.py:1.7 Message-ID: <200201081154.g08BsLS08004@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic/tests In directory cvs.zope.org:/tmp/cvs-serv7926/tests Modified Files: test_ListC.py Log Message: - Merge fix for Tracker #442 from branch. === CMF/CMFTopic/tests/test_ListC.py 1.6 => 1.7 === items = listc.getCriteriaItems() - self.assertEqual( items[0][1], tuple( abc ) ) + self.failUnless( 'foofield' in map( lambda x: x[0], items ) ) + self.failUnless( tuple( abc ) in map( lambda x: x[1], items ) ) + + def test_operator( self ): + + from Products.CMFTopic.ListCriterion import ListCriterion + listc = ListCriterion( 'foo', 'foofield' ) + + abc = [ 'a', 'b', 'c' ] + + listc.edit( abc ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 1 ) + + listc.edit( abc, 'or' ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 2 ) + self.failUnless( ( 'foofield_operator', 'or' ) in items ) + + listc.edit( abc, 'and' ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 2 ) + self.failUnless( ( 'foofield_operator', 'and' ) in items ) def test_suite(): return unittest.makeSuite( ListCriterionTests ) From tseaver@zope.com Tue Jan 8 11:54:21 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 06:54:21 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic/skins/zpt_topic - listc_edit.pt:1.2 Message-ID: <200201081154.g08BsLd08003@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic/skins/zpt_topic In directory cvs.zope.org:/tmp/cvs-serv7926/skins/zpt_topic Modified Files: listc_edit.pt Log Message: - Merge fix for Tracker #442 from branch. === CMF/CMFTopic/skins/zpt_topic/listc_edit.pt 1.1 => 1.2 === - - - - - - - - - - -   -
- Value: - - - + + + + + + + + + + +   + + Value:
+
+ Operator:
+ + + From tseaver@zope.com Tue Jan 8 11:54:51 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 06:54:51 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic - ListCriterion.py:1.9 Message-ID: <200201081154.g08Bspv08009@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic In directory cvs.zope.org:/tmp/cvs-serv7926 Modified Files: ListCriterion.py Log Message: - Merge fix for Tracker #442 from branch. === CMF/CMFTopic/ListCriterion.py 1.8 => 1.9 === from AccessControl import ClassSecurityInfo -import string, operator +import string class ListCriterion( AbstractCriterion ): """ @@ -39,7 +39,7 @@ security = ClassSecurityInfo() - _editableAttributes = ( 'value', ) + _editableAttributes = ( 'value', 'operator' ) def __init__( self, id, field ): self.id = id @@ -52,6 +52,7 @@ Restore to original value. """ self.value = ( '', ) # *Not* '()', which won't do at all! + self.operator = None security.declareProtected( TopicPermissions.ChangeTopics, 'getEditForm' ) def getEditForm( self ): @@ -62,7 +63,7 @@ return "listc_edit" security.declareProtected( TopicPermissions.ChangeTopics, 'edit' ) - def edit( self, value=None ): + def edit( self, value=None, operator=None ): """ Update the value we match against. """ @@ -73,6 +74,11 @@ value = string.split( value, '\n' ) self.value = tuple( value ) + if not operator: + operator = None + + self.operator = operator + security.declareProtected( CMFCorePermissions.View, 'getCriteriaItems' ) def getCriteriaItems( self ): """ @@ -80,8 +86,17 @@ (used by 'Topic.buildQuery()'). """ # filter out empty strings - value = tuple( filter( operator.truth, self.value ) ) - return operator.truth( value ) and ( ( self.field, self.value ), ) or () + result = [] + + value = tuple( filter( None, self.value ) ) + if not value: + return () + result.append( ( self.field, self.value ), ) + + if self.operator is not None: + result.append( ( '%s_operator' % self.field, self.operator ) ) + + return tuple( result ) From tseaver@zope.com Tue Jan 8 11:54:51 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 06:54:51 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic/skins/topic - listc_edit.dtml:1.4 Message-ID: <200201081154.g08Bsp008012@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic/skins/topic In directory cvs.zope.org:/tmp/cvs-serv7926/skins/topic Modified Files: listc_edit.dtml Log Message: - Merge fix for Tracker #442 from branch. === CMF/CMFTopic/skins/topic/listc_edit.dtml 1.3 => 1.4 ===   - Value:
- + + Value:
+
+ Operator:
+ + + + From tseaver@zope.com Tue Jan 8 12:35:30 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 07:35:30 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic/skins/zpt_topic - listc_edit.pt:1.1.4.1 Message-ID: <200201081235.g08CZUD17481@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic/skins/zpt_topic In directory cvs.zope.org:/tmp/cvs-serv17452/CMFTopic/skins/zpt_topic Modified Files: Tag: CMF-1_2-branch listc_edit.pt Log Message: - Merge fix for tracker #442 from branch. === CMF/CMFTopic/skins/zpt_topic/listc_edit.pt 1.1 => 1.1.4.1 === - - - - - - - - - - -   -
- Value: - - - + + + + + + + + + + +   + + Value:
+
+ Operator:
+ + + From tseaver@zope.com Tue Jan 8 12:35:30 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 07:35:30 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic/tests - test_ListC.py:1.5.8.2 Message-ID: <200201081235.g08CZUX17483@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic/tests In directory cvs.zope.org:/tmp/cvs-serv17452/CMFTopic/tests Modified Files: Tag: CMF-1_2-branch test_ListC.py Log Message: - Merge fix for tracker #442 from branch. === CMF/CMFTopic/tests/test_ListC.py 1.5.8.1 => 1.5.8.2 === items = listc.getCriteriaItems() - self.assertEqual( items[0][1], tuple( abc ) ) + self.failUnless( 'foofield' in map( lambda x: x[0], items ) ) + self.failUnless( tuple( abc ) in map( lambda x: x[1], items ) ) + + def test_operator( self ): + + from Products.CMFTopic.ListCriterion import ListCriterion + listc = ListCriterion( 'foo', 'foofield' ) + + abc = [ 'a', 'b', 'c' ] + + listc.edit( abc ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 1 ) + + listc.edit( abc, 'or' ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 2 ) + self.failUnless( ( 'foofield_operator', 'or' ) in items ) + + listc.edit( abc, 'and' ) + items = listc.getCriteriaItems() + self.assertEqual( len( items ), 2 ) + self.failUnless( ( 'foofield_operator', 'and' ) in items ) def test_suite(): return unittest.makeSuite( ListCriterionTests ) From tseaver@zope.com Tue Jan 8 12:35:59 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 07:35:59 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.14 Message-ID: <200201081235.g08CZxk17495@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv17452 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Merge fix for tracker #442 from branch. === CMF/CHANGES.txt 1.36.2.13 => 1.36.2.14 === of actions tool (Tracker #401). + - Added 'operator' attribute to CMFTopic.ListCriterion, to permit + specifying an operator ('and', for the most part) for indexes which + support it, e.g., KeywordIndex (Tracker #442). + Bugs Fixed - - Added external method update_catalogIndexes.py to run as part of a - upgrade to CMFs migrating to Zope2.4+ from from CMF sites which were built - using Zope2.3 catalog + - Added external method update_catalogIndexes.py to run as part of a + upgrade to CMFs migrating to Zope2.4+ from from CMF sites which were built + using Zope2.3 catalog - Use ID to label Favorite when target has an empty Title (Tracker #440). From tseaver@zope.com Tue Jan 8 12:36:00 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 07:36:00 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic - ListCriterion.py:1.7.2.2 Message-ID: <200201081236.g08Ca0w17498@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic In directory cvs.zope.org:/tmp/cvs-serv17452/CMFTopic Modified Files: Tag: CMF-1_2-branch ListCriterion.py Log Message: - Merge fix for tracker #442 from branch. === CMF/CMFTopic/ListCriterion.py 1.7.2.1 => 1.7.2.2 === from AccessControl import ClassSecurityInfo -import string, operator +import string class ListCriterion( AbstractCriterion ): """ @@ -39,7 +39,7 @@ security = ClassSecurityInfo() - _editableAttributes = ( 'value', ) + _editableAttributes = ( 'value', 'operator' ) def __init__( self, id, field ): self.id = id @@ -52,6 +52,7 @@ Restore to original value. """ self.value = ( '', ) # *Not* '()', which won't do at all! + self.operator = None security.declareProtected( TopicPermissions.ChangeTopics, 'getEditForm' ) def getEditForm( self ): @@ -62,7 +63,7 @@ return "listc_edit" security.declareProtected( TopicPermissions.ChangeTopics, 'edit' ) - def edit( self, value=None ): + def edit( self, value=None, operator=None ): """ Update the value we match against. """ @@ -73,6 +74,11 @@ value = string.split( value, '\n' ) self.value = tuple( value ) + if not operator: + operator = None + + self.operator = operator + security.declareProtected( CMFCorePermissions.View, 'getCriteriaItems' ) def getCriteriaItems( self ): """ @@ -80,8 +86,17 @@ (used by 'Topic.buildQuery()'). """ # filter out empty strings - value = tuple( filter( operator.truth, self.value ) ) - return operator.truth( value ) and ( ( self.field, self.value ), ) or () + result = [] + + value = tuple( filter( None, self.value ) ) + if not value: + return () + result.append( ( self.field, self.value ), ) + + if self.operator is not None: + result.append( ( '%s_operator' % self.field, self.operator ) ) + + return tuple( result ) From tseaver@zope.com Tue Jan 8 12:36:00 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 07:36:00 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic/skins/topic - listc_edit.dtml:1.3.12.1 Message-ID: <200201081236.g08Ca0g17501@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic/skins/topic In directory cvs.zope.org:/tmp/cvs-serv17452/CMFTopic/skins/topic Modified Files: Tag: CMF-1_2-branch listc_edit.dtml Log Message: - Merge fix for tracker #442 from branch. === CMF/CMFTopic/skins/topic/listc_edit.dtml 1.3 => 1.3.12.1 ===   - Value:
- + + Value:
+
+ Operator:
+ + + + From tseaver@zope.com Tue Jan 8 13:19:19 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 08:19:19 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Link.py:1.13.2.1.2.1 Message-ID: <200201081319.g08DJJS29018@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv28958 Modified Files: Tag: tseaver-tracker_364-branch Link.py Log Message: - Add ZMI interface for editing Link URL (Tracker #364). === CMF/CMFDefault/Link.py 1.13.2.1 => 1.13.2.1.2.1 === # ############################################################################## +""" Link instances represent explicit links-as-content. + +$Id$ """ -""" +__version__ = "$Revision$"[11:-2] -ADD_CONTENT_PERMISSION = 'Add portal content' +import string +import urlparse -import Globals, string, urlparse -from Globals import InitializeClass +from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo -from Products.CMFCore.PortalContent import PortalContent -from DublinCore import DefaultDublinCoreImpl from Products.CMFCore import CMFCorePermissions +from Products.CMFCore.PortalContent import PortalContent from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import keywordsplitter -from utils import parseHeadersBody + +from DublinCore import DefaultDublinCoreImpl +from utils import parseHeadersBody, _dtmldir factory_type_information = ( { 'id' : 'Link' , 'meta_type' : 'Link' @@ -66,7 +70,7 @@ , description='' ): """ - Add a Link + Add a Link instance to 'self'. """ o=Link( id, title, remote_url, description ) self._setObject(id,o) @@ -83,15 +87,10 @@ , DefaultDublinCoreImpl.__implements__ ) - meta_type='Link' + meta_type = 'Link' effective_date = expiration_date = None _isDiscussable = 1 - __ac_permissions__=( - (CMFCorePermissions.View, ('', 'view', 'getRemoteUrl')), - (CMFCorePermissions.ModifyPortalContent, ('edit',)), - ) - security = ClassSecurityInfo() def __init__( self @@ -106,8 +105,25 @@ self.remote_url=remote_url self.description=description - security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'edit') - def edit( self, remote_url ): + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_edit' ) + manage_edit = DTMLFile( 'zmi_editLink', _dtmldir ) + + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_editLink' ) + def manage_editLink( self, remote_url, REQUEST=None ): + """ + Update the Link via the ZMI. + """ + self._edit( remote_url ) + if REQUEST is not None: + REQUEST['RESPONSE'].redirect( self.absolute_url() + + '/manage_edit' + + '?manage_tabs_message=Link+updated' + ) + + security.declarePrivate( '_edit' ) + def _edit( self, remote_url ): """ Edit the Link """ @@ -119,7 +135,8 @@ if self.remote_url[-1] == '/': self.remote_url = self.remote_url[:-1] - edit = WorkflowAction(edit) + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'edit' ) + edit = WorkflowAction( _edit ) security.declareProtected( CMFCorePermissions.View, 'SearchableText' ) def SearchableText(self): @@ -135,6 +152,7 @@ """ return self.remote_url + security.declarePrivate( '_writeFromPUT' ) def _writeFromPUT( self, body ): headers = {} @@ -162,10 +180,10 @@ ) ## FTP handlers - security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'PUT') + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'PUT') def PUT(self, REQUEST, RESPONSE): """ - Handle HTTP (and presumably FTP?) PUT requests. + Handle HTTP / WebDAV / FTP PUT requests. """ self.dav__init(REQUEST, RESPONSE) body = REQUEST.get('BODY', '') @@ -175,7 +193,9 @@ security.declareProtected( CMFCorePermissions.View, 'manage_FTPget' ) def manage_FTPget(self): - "Get the link as text for FTP download (also used for the WebDAV SRC)" + """ + Get the link as text for WebDAV src / FTP download. + """ join = string.join lower = string.lower hdrlist = self.getMetadataHeaders() @@ -187,7 +207,9 @@ security.declareProtected( CMFCorePermissions.View, 'get_size' ) def get_size( self ): - """ Used for FTP and apparently the ZMI now too """ + """ + Used for FTP and apparently the ZMI now too + """ return len(self.manage_FTPget()) InitializeClass( Link ) From tseaver@zope.com Tue Jan 8 13:19:19 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 08:19:19 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/dtml - zmi_editLink.dtml:1.1.2.1 Message-ID: <200201081319.g08DJJZ29020@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/dtml In directory cvs.zope.org:/tmp/cvs-serv28958/dtml Added Files: Tag: CMF-1_2-branch zmi_editLink.dtml Log Message: - Add ZMI interface for editing Link URL (Tracker #364). === Added File CMF/CMFDefault/dtml/zmi_editLink.dtml ===

Edit &dtml-getId;

Remote Url

From tseaver@zope.com Tue Jan 8 13:24:43 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 08:24:43 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Link.py:1.13.2.2 Message-ID: <200201081324.g08DOhM30256@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv30247 Modified Files: Tag: CMF-1_2-branch Link.py Log Message: - Merge fix for Tracker #364 from branch. === CMF/CMFDefault/Link.py 1.13.2.1 => 1.13.2.2 === # ############################################################################## +""" Link instances represent explicit links-as-content. + +$Id$ """ -""" +__version__ = "$Revision$"[11:-2] -ADD_CONTENT_PERMISSION = 'Add portal content' +import string +import urlparse -import Globals, string, urlparse -from Globals import InitializeClass +from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo -from Products.CMFCore.PortalContent import PortalContent -from DublinCore import DefaultDublinCoreImpl from Products.CMFCore import CMFCorePermissions +from Products.CMFCore.PortalContent import PortalContent from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import keywordsplitter -from utils import parseHeadersBody + +from DublinCore import DefaultDublinCoreImpl +from utils import parseHeadersBody, _dtmldir factory_type_information = ( { 'id' : 'Link' , 'meta_type' : 'Link' @@ -66,7 +70,7 @@ , description='' ): """ - Add a Link + Add a Link instance to 'self'. """ o=Link( id, title, remote_url, description ) self._setObject(id,o) @@ -83,15 +87,10 @@ , DefaultDublinCoreImpl.__implements__ ) - meta_type='Link' + meta_type = 'Link' effective_date = expiration_date = None _isDiscussable = 1 - __ac_permissions__=( - (CMFCorePermissions.View, ('', 'view', 'getRemoteUrl')), - (CMFCorePermissions.ModifyPortalContent, ('edit',)), - ) - security = ClassSecurityInfo() def __init__( self @@ -106,8 +105,25 @@ self.remote_url=remote_url self.description=description - security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'edit') - def edit( self, remote_url ): + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_edit' ) + manage_edit = DTMLFile( 'zmi_editLink', _dtmldir ) + + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_editLink' ) + def manage_editLink( self, remote_url, REQUEST=None ): + """ + Update the Link via the ZMI. + """ + self._edit( remote_url ) + if REQUEST is not None: + REQUEST['RESPONSE'].redirect( self.absolute_url() + + '/manage_edit' + + '?manage_tabs_message=Link+updated' + ) + + security.declarePrivate( '_edit' ) + def _edit( self, remote_url ): """ Edit the Link """ @@ -119,7 +135,8 @@ if self.remote_url[-1] == '/': self.remote_url = self.remote_url[:-1] - edit = WorkflowAction(edit) + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'edit' ) + edit = WorkflowAction( _edit ) security.declareProtected( CMFCorePermissions.View, 'SearchableText' ) def SearchableText(self): @@ -135,6 +152,7 @@ """ return self.remote_url + security.declarePrivate( '_writeFromPUT' ) def _writeFromPUT( self, body ): headers = {} @@ -162,10 +180,10 @@ ) ## FTP handlers - security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'PUT') + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'PUT') def PUT(self, REQUEST, RESPONSE): """ - Handle HTTP (and presumably FTP?) PUT requests. + Handle HTTP / WebDAV / FTP PUT requests. """ self.dav__init(REQUEST, RESPONSE) body = REQUEST.get('BODY', '') @@ -175,7 +193,9 @@ security.declareProtected( CMFCorePermissions.View, 'manage_FTPget' ) def manage_FTPget(self): - "Get the link as text for FTP download (also used for the WebDAV SRC)" + """ + Get the link as text for WebDAV src / FTP download. + """ join = string.join lower = string.lower hdrlist = self.getMetadataHeaders() @@ -187,7 +207,9 @@ security.declareProtected( CMFCorePermissions.View, 'get_size' ) def get_size( self ): - """ Used for FTP and apparently the ZMI now too """ + """ + Used for FTP and apparently the ZMI now too + """ return len(self.manage_FTPget()) InitializeClass( Link ) From tseaver@zope.com Tue Jan 8 13:26:43 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 08:26:43 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.15 Message-ID: <200201081326.g08DQhe30389@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv30380 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: === CMF/CHANGES.txt 1.36.2.14 => 1.36.2.15 === support it, e.g., KeywordIndex (Tracker #442). + - Added ZMI interface for editing Link URL (Tracker #364). + Bugs Fixed - Added external method update_catalogIndexes.py to run as part of a From tseaver@zope.com Tue Jan 8 13:30:34 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 08:30:34 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Link.py:1.14 Message-ID: <200201081330.g08DUYv31557@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv31512 Modified Files: Link.py Log Message: - Merge Tracker #364 (ZMI link editing). === CMF/CMFDefault/Link.py 1.13 => 1.14 === # ############################################################################## +""" Link instances represent explicit links-as-content. + +$Id$ """ -""" +__version__ = "$Revision$"[11:-2] -ADD_CONTENT_PERMISSION = 'Add portal content' +import string +import urlparse -import Globals, string, urlparse -from Globals import InitializeClass +from Globals import InitializeClass, DTMLFile from AccessControl import ClassSecurityInfo -from Products.CMFCore.PortalContent import PortalContent -from DublinCore import DefaultDublinCoreImpl from Products.CMFCore import CMFCorePermissions +from Products.CMFCore.PortalContent import PortalContent from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import keywordsplitter -from utils import parseHeadersBody + +from DublinCore import DefaultDublinCoreImpl +from utils import parseHeadersBody, _dtmldir factory_type_information = ( { 'id' : 'Link' , 'meta_type' : 'Link' @@ -66,7 +70,7 @@ , description='' ): """ - Add a Link + Add a Link instance to 'self'. """ o=Link( id, title, remote_url, description ) self._setObject(id,o) @@ -83,15 +87,10 @@ , DefaultDublinCoreImpl.__implements__ ) - meta_type='Link' + meta_type = 'Link' effective_date = expiration_date = None _isDiscussable = 1 - __ac_permissions__=( - (CMFCorePermissions.View, ('', 'view', 'getRemoteUrl')), - (CMFCorePermissions.ModifyPortalContent, ('edit',)), - ) - security = ClassSecurityInfo() def __init__( self @@ -106,8 +105,25 @@ self.remote_url=remote_url self.description=description - security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'edit') - def edit( self, remote_url ): + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_edit' ) + manage_edit = DTMLFile( 'zmi_editLink', _dtmldir ) + + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_editLink' ) + def manage_editLink( self, remote_url, REQUEST=None ): + """ + Update the Link via the ZMI. + """ + self._edit( remote_url ) + if REQUEST is not None: + REQUEST['RESPONSE'].redirect( self.absolute_url() + + '/manage_edit' + + '?manage_tabs_message=Link+updated' + ) + + security.declarePrivate( '_edit' ) + def _edit( self, remote_url ): """ Edit the Link """ @@ -117,7 +133,8 @@ else: self.remote_url = 'http://' + remote_url - edit = WorkflowAction(edit) + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'edit' ) + edit = WorkflowAction( _edit ) security.declareProtected( CMFCorePermissions.View, 'SearchableText' ) def SearchableText(self): @@ -133,6 +150,7 @@ """ return self.remote_url + security.declarePrivate( '_writeFromPUT' ) def _writeFromPUT( self, body ): headers = {} @@ -160,10 +178,10 @@ ) ## FTP handlers - security.declareProtected(CMFCorePermissions.ModifyPortalContent, 'PUT') + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'PUT') def PUT(self, REQUEST, RESPONSE): """ - Handle HTTP (and presumably FTP?) PUT requests. + Handle HTTP / WebDAV / FTP PUT requests. """ self.dav__init(REQUEST, RESPONSE) body = REQUEST.get('BODY', '') @@ -173,7 +191,9 @@ security.declareProtected( CMFCorePermissions.View, 'manage_FTPget' ) def manage_FTPget(self): - "Get the link as text for FTP download (also used for the WebDAV SRC)" + """ + Get the link as text for WebDAV src / FTP download. + """ join = string.join lower = string.lower hdrlist = self.getMetadataHeaders() @@ -185,7 +205,9 @@ security.declareProtected( CMFCorePermissions.View, 'get_size' ) def get_size( self ): - """ Used for FTP and apparently the ZMI now too """ + """ + Used for FTP and apparently the ZMI now too + """ return len(self.manage_FTPget()) InitializeClass( Link ) From tseaver@zope.com Tue Jan 8 13:30:35 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 08:30:35 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/dtml - zmi_editLink.dtml:1.2 Message-ID: <200201081330.g08DUZw31558@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/dtml In directory cvs.zope.org:/tmp/cvs-serv31512/dtml Added Files: zmi_editLink.dtml Log Message: - Merge Tracker #364 (ZMI link editing). === CMF/CMFDefault/dtml/zmi_editLink.dtml 1.1 => 1.2 === + + +

Edit &dtml-getId;

+ +
+ + + + + + + + + + +
+ Remote Url + + +

+ +
+
+ + From tseaver@zope.com Tue Jan 8 18:19:46 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:19:46 -0500 Subject: [CMF-checkins] CVS: Products/CMFDefault - Document.py:1.40.14.1 Link.py:1.14.2.1 utils.py:1.8.24.1 Message-ID: <200201081819.g08IJkU04005@cvs.baymountain.com> Update of /cvs-repository/Products/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv3440 Modified Files: Tag: tseaver-tracker_407-branch Document.py Link.py utils.py Log Message: - Clean up emission of RFC822-style headers (Tracker #407): o Headers must be terminated with CRLF; o Header values with embedded newlines must pad continuation lines with leading whitespace). - Refactor test_Link to use 'assertEquals'. === Products/CMFDefault/Document.py 1.40 => 1.40.14.1 === from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import _format_stx, keywordsplitter -from utils import parseHeadersBody, SimpleHTMLParser, bodyfinder, _dtmldir +from utils import parseHeadersBody, formatRFC822Headers +from utils import SimpleHTMLParser, bodyfinder, _dtmldir factory_type_information = ( { 'id' : 'Document' , 'meta_type' : 'Document' @@ -400,9 +401,8 @@ 'body': self.EditableBody(), } else: - hdrtext = join(map(lambda x: '%s: %s' % ( - x[0], x[1]), hdrlist), '\n') - bodytext = '%s\n\n%s' % ( hdrtext, self.text ) + hdrtext = formatRFC822Headers( hdrlist ) + bodytext = '%s\r\n\r\n%s' % ( hdrtext, self.text ) return bodytext === Products/CMFDefault/Link.py 1.14 => 1.14.2.1 === from DublinCore import DefaultDublinCoreImpl -from utils import parseHeadersBody, _dtmldir +from utils import formatRFC822Headers, parseHeadersBody, _dtmldir factory_type_information = ( { 'id' : 'Link' , 'meta_type' : 'Link' @@ -88,6 +88,7 @@ ) meta_type = 'Link' + URL_FORMAT = format = 'text/url' effective_date = expiration_date = None _isDiscussable = 1 @@ -158,7 +159,7 @@ lines = string.split( body, '\n' ) self.edit( lines[0] ) - headers['Format'] = 'text/url' + headers['Format'] = self.URL_FORMAT new_subject = keywordsplitter(headers) headers['Subject'] = new_subject or self.Subject() haveheader = headers.has_key @@ -197,8 +198,7 @@ join = string.join lower = string.lower hdrlist = self.getMetadataHeaders() - hdrtext = join( map( lambda x: '%s: %s' % ( x[0], x[1] ) - , hdrlist), '\n' ) + hdrtext = formatRFC822Headers( hdrlist ) bodytext = '%s\n\n%s' % ( hdrtext, self.getRemoteUrl() ) return bodytext === Products/CMFDefault/utils.py 1.8 => 1.8.24.1 === _dtmldir = os.path.join( package_home( globals() ), 'dtml' ) +def formatRFC822Headers( headers ): + """ + Convert the key-value pairs in 'headers' to valid RFC822-style + headers, including adding leading whitespace to elements which + contain newlines in order to preserve continuation-line semantics. + """ + munged = [] + linesplit = re.compile( r'[\n\r]+?' ) + + for key, value in headers: + + vallines = linesplit.split( value ) + munged.append( '%s: %s' % ( key, join( vallines, '\r\n ' ) ) ) + + return join( munged, '\r\n' ) + + def parseHeadersBody( body, headers=None ): """ Parse any leading 'RFC-822'-ish headers from an uploaded From tseaver@zope.com Tue Jan 8 18:19:46 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:19:46 -0500 Subject: [CMF-checkins] CVS: Products/CMFDefault/tests - test_Document.py:1.20.14.1 test_Link.py:1.2.20.1 Message-ID: <200201081819.g08IJk804007@cvs.baymountain.com> Update of /cvs-repository/Products/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv3440/tests Modified Files: Tag: tseaver-tracker_407-branch test_Document.py test_Link.py Log Message: - Clean up emission of RFC822-style headers (Tracker #407): o Headers must be terminated with CRLF; o Header values with embedded newlines must pad continuation lines with leading whitespace). - Refactor test_Link to use 'assertEquals'. === Products/CMFDefault/tests/test_Document.py 1.20 => 1.20.14.1 === d.PUT(REQUEST, RESPONSE=fakeResponse()) - simple_lines = string.split( SIMPLE_HTML, '\n' ) - get_lines = string.split( d.manage_FTPget(), '\n' ) + rnlinesplit = re.compile( r'\r?\n?' ) + simple_lines = rnlinesplit.split( SIMPLE_HTML ) + get_lines = rnlinesplit.split( d.manage_FTPget() ) # strip off headers meta_pattern = re.compile( r'meta name="([a-z]*)" ' @@ -408,8 +409,11 @@ d = Document( 'foo' ) d.PUT(REQUEST, RESPONSE=fakeResponse()) - simple_lines = string.split( SIMPLE_STRUCTUREDTEXT, '\n' ) - get_lines = string.split( d.manage_FTPget(), '\n' ) + rnlinesplit = re.compile( r'\r?\n?' ) + + get_text = d.manage_FTPget() + simple_lines = rnlinesplit.split( SIMPLE_STRUCTUREDTEXT ) + get_lines = rnlinesplit.split( get_text ) # strip off headers simple_headers = [] === Products/CMFDefault/tests/test_Link.py 1.2 => 1.2.20.1 === +import unittest, string, re from Products.CMFDefault.Link import Link BASIC_STRUCTUREDTEXT = '''\ @@ -9,42 +9,60 @@ http://www.zope.org ''' -class LinkTests(unittest.TestCase): +STX_W_CONTINUATION = '''\ +Title: Zope Community +Description: Link to the Zope Community website, + including hundreds of contributed Zope products. +Subject: open source; Zope; community - def setUp( self ): - get_transaction().begin() +http://www.zope.org +''' - def tearDown( self ): - get_transaction().abort() +class LinkTests(unittest.TestCase): def test_Empty( self ): d = Link( 'foo' ) - self.failUnless( d.Title() == '' ) - self.failUnless( d.Description() == '' ) - self.failUnless( d.getRemoteUrl() == '' ) + self.assertEqual( d.Title(), '' ) + self.assertEqual( d.Description(), '' ) + self.assertEqual( d.getRemoteUrl(), '' ) def test_StructuredText( self ): d = Link('foo') d._writeFromPUT( body=BASIC_STRUCTUREDTEXT ) - self.failUnless( d.Title() == 'Zope Community' ) - self.failUnless( - d.Description() == 'Link to the Zope Community website.' ) - self.failUnless( len(d.Subject()) == 3 ) - self.failUnless( d.getRemoteUrl() == 'http://www.zope.org' ) + self.assertEqual( d.Title(), 'Zope Community' ) + self.assertEqual( d.Description() + , 'Link to the Zope Community website.' ) + self.assertEqual( len(d.Subject()), 3 ) + self.assertEqual( d.getRemoteUrl(), 'http://www.zope.org' ) + + def test_StructuredText_w_Continuation( self ): + + d = Link('foo') + d._writeFromPUT( body=STX_W_CONTINUATION ) + rnlinesplit = re.compile( r'\r?\n?' ) + desc_lines = rnlinesplit.split( d.Description() ) + + self.assertEqual( d.Title(), 'Zope Community' ) + self.assertEqual( desc_lines[0] + , 'Link to the Zope Community website,' ) + self.assertEqual( desc_lines[1] + , 'including hundreds of contributed Zope products.' ) + self.assertEqual( len(d.Subject()), 3 ) + self.assertEqual( d.getRemoteUrl(), 'http://www.zope.org' ) def test_fixupMissingScheme( self ): d = Link( 'foo' ) d.edit( 'http://foo.com' ) - self.failUnless( d.getRemoteUrl() == 'http://foo.com' ) + self.assertEqual( d.getRemoteUrl(), 'http://foo.com' ) d = Link( 'bar' ) d.edit( '//bar.com' ) - self.failUnless( d.getRemoteUrl() == 'http://bar.com' ) + self.assertEqual( d.getRemoteUrl(), 'http://bar.com' ) d = Link( 'baz' ) d.edit( 'baz.com' ) - self.failUnless( d.getRemoteUrl() == 'http://baz.com' ) + self.assertEqual( d.getRemoteUrl(), 'http://baz.com' ) From tseaver@zope.com Tue Jan 8 18:21:12 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:21:12 -0500 Subject: [CMF-checkins] CVS: Products/CMFDefault - Document.py:1.41 Link.py:1.15 utils.py:1.9 Message-ID: <200201081821.g08ILCB04168@cvs.baymountain.com> Update of /cvs-repository/Products/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv4154/CMFDefault Modified Files: Document.py Link.py utils.py Log Message: - Merge fix for Tracker #407 from branch. === Products/CMFDefault/Document.py 1.40 => 1.41 === from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import _format_stx, keywordsplitter -from utils import parseHeadersBody, SimpleHTMLParser, bodyfinder, _dtmldir +from utils import parseHeadersBody, formatRFC822Headers +from utils import SimpleHTMLParser, bodyfinder, _dtmldir factory_type_information = ( { 'id' : 'Document' , 'meta_type' : 'Document' @@ -400,9 +401,8 @@ 'body': self.EditableBody(), } else: - hdrtext = join(map(lambda x: '%s: %s' % ( - x[0], x[1]), hdrlist), '\n') - bodytext = '%s\n\n%s' % ( hdrtext, self.text ) + hdrtext = formatRFC822Headers( hdrlist ) + bodytext = '%s\r\n\r\n%s' % ( hdrtext, self.text ) return bodytext === Products/CMFDefault/Link.py 1.14 => 1.15 === from DublinCore import DefaultDublinCoreImpl -from utils import parseHeadersBody, _dtmldir +from utils import formatRFC822Headers, parseHeadersBody, _dtmldir factory_type_information = ( { 'id' : 'Link' , 'meta_type' : 'Link' @@ -88,6 +88,7 @@ ) meta_type = 'Link' + URL_FORMAT = format = 'text/url' effective_date = expiration_date = None _isDiscussable = 1 @@ -158,7 +159,7 @@ lines = string.split( body, '\n' ) self.edit( lines[0] ) - headers['Format'] = 'text/url' + headers['Format'] = self.URL_FORMAT new_subject = keywordsplitter(headers) headers['Subject'] = new_subject or self.Subject() haveheader = headers.has_key @@ -197,8 +198,7 @@ join = string.join lower = string.lower hdrlist = self.getMetadataHeaders() - hdrtext = join( map( lambda x: '%s: %s' % ( x[0], x[1] ) - , hdrlist), '\n' ) + hdrtext = formatRFC822Headers( hdrlist ) bodytext = '%s\n\n%s' % ( hdrtext, self.getRemoteUrl() ) return bodytext === Products/CMFDefault/utils.py 1.8 => 1.9 === _dtmldir = os.path.join( package_home( globals() ), 'dtml' ) +def formatRFC822Headers( headers ): + """ + Convert the key-value pairs in 'headers' to valid RFC822-style + headers, including adding leading whitespace to elements which + contain newlines in order to preserve continuation-line semantics. + """ + munged = [] + linesplit = re.compile( r'[\n\r]+?' ) + + for key, value in headers: + + vallines = linesplit.split( value ) + munged.append( '%s: %s' % ( key, join( vallines, '\r\n ' ) ) ) + + return join( munged, '\r\n' ) + + def parseHeadersBody( body, headers=None ): """ Parse any leading 'RFC-822'-ish headers from an uploaded From tseaver@zope.com Tue Jan 8 18:21:12 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:21:12 -0500 Subject: [CMF-checkins] CVS: Products/CMFDefault/tests - test_Document.py:1.21 test_Link.py:1.3 Message-ID: <200201081821.g08ILCj04170@cvs.baymountain.com> Update of /cvs-repository/Products/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv4154/CMFDefault/tests Modified Files: test_Document.py test_Link.py Log Message: - Merge fix for Tracker #407 from branch. === Products/CMFDefault/tests/test_Document.py 1.20 => 1.21 === d.PUT(REQUEST, RESPONSE=fakeResponse()) - simple_lines = string.split( SIMPLE_HTML, '\n' ) - get_lines = string.split( d.manage_FTPget(), '\n' ) + rnlinesplit = re.compile( r'\r?\n?' ) + simple_lines = rnlinesplit.split( SIMPLE_HTML ) + get_lines = rnlinesplit.split( d.manage_FTPget() ) # strip off headers meta_pattern = re.compile( r'meta name="([a-z]*)" ' @@ -408,8 +409,11 @@ d = Document( 'foo' ) d.PUT(REQUEST, RESPONSE=fakeResponse()) - simple_lines = string.split( SIMPLE_STRUCTUREDTEXT, '\n' ) - get_lines = string.split( d.manage_FTPget(), '\n' ) + rnlinesplit = re.compile( r'\r?\n?' ) + + get_text = d.manage_FTPget() + simple_lines = rnlinesplit.split( SIMPLE_STRUCTUREDTEXT ) + get_lines = rnlinesplit.split( get_text ) # strip off headers simple_headers = [] === Products/CMFDefault/tests/test_Link.py 1.2 => 1.3 === +import unittest, string, re from Products.CMFDefault.Link import Link BASIC_STRUCTUREDTEXT = '''\ @@ -9,42 +9,60 @@ http://www.zope.org ''' -class LinkTests(unittest.TestCase): +STX_W_CONTINUATION = '''\ +Title: Zope Community +Description: Link to the Zope Community website, + including hundreds of contributed Zope products. +Subject: open source; Zope; community - def setUp( self ): - get_transaction().begin() +http://www.zope.org +''' - def tearDown( self ): - get_transaction().abort() +class LinkTests(unittest.TestCase): def test_Empty( self ): d = Link( 'foo' ) - self.failUnless( d.Title() == '' ) - self.failUnless( d.Description() == '' ) - self.failUnless( d.getRemoteUrl() == '' ) + self.assertEqual( d.Title(), '' ) + self.assertEqual( d.Description(), '' ) + self.assertEqual( d.getRemoteUrl(), '' ) def test_StructuredText( self ): d = Link('foo') d._writeFromPUT( body=BASIC_STRUCTUREDTEXT ) - self.failUnless( d.Title() == 'Zope Community' ) - self.failUnless( - d.Description() == 'Link to the Zope Community website.' ) - self.failUnless( len(d.Subject()) == 3 ) - self.failUnless( d.getRemoteUrl() == 'http://www.zope.org' ) + self.assertEqual( d.Title(), 'Zope Community' ) + self.assertEqual( d.Description() + , 'Link to the Zope Community website.' ) + self.assertEqual( len(d.Subject()), 3 ) + self.assertEqual( d.getRemoteUrl(), 'http://www.zope.org' ) + + def test_StructuredText_w_Continuation( self ): + + d = Link('foo') + d._writeFromPUT( body=STX_W_CONTINUATION ) + rnlinesplit = re.compile( r'\r?\n?' ) + desc_lines = rnlinesplit.split( d.Description() ) + + self.assertEqual( d.Title(), 'Zope Community' ) + self.assertEqual( desc_lines[0] + , 'Link to the Zope Community website,' ) + self.assertEqual( desc_lines[1] + , 'including hundreds of contributed Zope products.' ) + self.assertEqual( len(d.Subject()), 3 ) + self.assertEqual( d.getRemoteUrl(), 'http://www.zope.org' ) def test_fixupMissingScheme( self ): d = Link( 'foo' ) d.edit( 'http://foo.com' ) - self.failUnless( d.getRemoteUrl() == 'http://foo.com' ) + self.assertEqual( d.getRemoteUrl(), 'http://foo.com' ) d = Link( 'bar' ) d.edit( '//bar.com' ) - self.failUnless( d.getRemoteUrl() == 'http://bar.com' ) + self.assertEqual( d.getRemoteUrl(), 'http://bar.com' ) d = Link( 'baz' ) d.edit( 'baz.com' ) - self.failUnless( d.getRemoteUrl() == 'http://baz.com' ) + self.assertEqual( d.getRemoteUrl(), 'http://baz.com' ) From tseaver@zope.com Tue Jan 8 18:25:57 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:25:57 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Document.py:1.39.2.3 Link.py:1.13.2.3 utils.py:1.8.4.1 Message-ID: <200201081825.g08IPvi05310@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv5291/CMFDefault Modified Files: Tag: CMF-1_2-branch Document.py Link.py utils.py Log Message: - Merge fix for Tracker #407 from branch. === CMF/CMFDefault/Document.py 1.39.2.2 => 1.39.2.3 === from DublinCore import DefaultDublinCoreImpl -from utils import parseHeadersBody, SimpleHTMLParser, bodyfinder, _dtmldir +from utils import parseHeadersBody, formatRFC822Headers +from utils import SimpleHTMLParser, bodyfinder, _dtmldir factory_type_information = ( { 'id' : 'Document' , 'meta_type' : 'Document' @@ -403,9 +404,8 @@ 'body': self.EditableBody(), } else: - hdrtext = join(map(lambda x: '%s: %s' % ( - x[0], x[1]), hdrlist), '\n') - bodytext = '%s\n\n%s' % ( hdrtext, self.text ) + hdrtext = formatRFC822Headers( hdrlist ) + bodytext = '%s\r\n\r\n%s' % ( hdrtext, self.text ) return bodytext === CMF/CMFDefault/Link.py 1.13.2.2 => 1.13.2.3 === from DublinCore import DefaultDublinCoreImpl -from utils import parseHeadersBody, _dtmldir +from utils import formatRFC822Headers, parseHeadersBody, _dtmldir factory_type_information = ( { 'id' : 'Link' , 'meta_type' : 'Link' @@ -88,6 +88,7 @@ ) meta_type = 'Link' + URL_FORMAT = format = 'text/url' effective_date = expiration_date = None _isDiscussable = 1 @@ -160,7 +161,7 @@ lines = string.split( body, '\n' ) self.edit( lines[0] ) - headers['Format'] = 'text/url' + headers['Format'] = self.URL_FORMAT new_subject = keywordsplitter(headers) headers['Subject'] = new_subject or self.Subject() haveheader = headers.has_key @@ -199,8 +200,7 @@ join = string.join lower = string.lower hdrlist = self.getMetadataHeaders() - hdrtext = join( map( lambda x: '%s: %s' % ( x[0], x[1] ) - , hdrlist), '\n' ) + hdrtext = formatRFC822Headers( hdrlist ) bodytext = '%s\n\n%s' % ( hdrtext, self.getRemoteUrl() ) return bodytext === CMF/CMFDefault/utils.py 1.8 => 1.8.4.1 === _dtmldir = os.path.join( package_home( globals() ), 'dtml' ) +def formatRFC822Headers( headers ): + """ + Convert the key-value pairs in 'headers' to valid RFC822-style + headers, including adding leading whitespace to elements which + contain newlines in order to preserve continuation-line semantics. + """ + munged = [] + linesplit = re.compile( r'[\n\r]+?' ) + + for key, value in headers: + + vallines = linesplit.split( value ) + munged.append( '%s: %s' % ( key, join( vallines, '\r\n ' ) ) ) + + return join( munged, '\r\n' ) + + def parseHeadersBody( body, headers=None ): """ Parse any leading 'RFC-822'-ish headers from an uploaded From tseaver@zope.com Tue Jan 8 18:25:57 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:25:57 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Document.py:1.19.2.2 test_Link.py:1.2.2.2 Message-ID: <200201081825.g08IPvD05312@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv5291/CMFDefault/tests Modified Files: Tag: CMF-1_2-branch test_Document.py test_Link.py Log Message: - Merge fix for Tracker #407 from branch. === CMF/CMFDefault/tests/test_Document.py 1.19.2.1 => 1.19.2.2 === d.PUT(REQUEST, RESPONSE=fakeResponse()) - simple_lines = string.split( SIMPLE_HTML, '\n' ) - get_lines = string.split( d.manage_FTPget(), '\n' ) + rnlinesplit = re.compile( r'\r?\n?' ) + simple_lines = rnlinesplit.split( SIMPLE_HTML ) + get_lines = rnlinesplit.split( d.manage_FTPget() ) # strip off headers meta_pattern = re.compile( r'meta name="([a-z]*)" ' @@ -408,8 +409,11 @@ d = Document( 'foo' ) d.PUT(REQUEST, RESPONSE=fakeResponse()) - simple_lines = string.split( SIMPLE_STRUCTUREDTEXT, '\n' ) - get_lines = string.split( d.manage_FTPget(), '\n' ) + rnlinesplit = re.compile( r'\r?\n?' ) + + get_text = d.manage_FTPget() + simple_lines = rnlinesplit.split( SIMPLE_STRUCTUREDTEXT ) + get_lines = rnlinesplit.split( get_text ) # strip off headers simple_headers = [] === CMF/CMFDefault/tests/test_Link.py 1.2.2.1 => 1.2.2.2 === +import unittest, string, re from Products.CMFDefault.Link import Link BASIC_STRUCTUREDTEXT = '''\ @@ -9,27 +9,45 @@ http://www.zope.org ''' -class LinkTests(unittest.TestCase): +STX_W_CONTINUATION = '''\ +Title: Zope Community +Description: Link to the Zope Community website, + including hundreds of contributed Zope products. +Subject: open source; Zope; community - def setUp( self ): - get_transaction().begin() +http://www.zope.org +''' - def tearDown( self ): - get_transaction().abort() +class LinkTests(unittest.TestCase): def test_Empty( self ): d = Link( 'foo' ) - self.failUnless( d.Title() == '' ) - self.failUnless( d.Description() == '' ) - self.failUnless( d.getRemoteUrl() == '' ) + self.assertEqual( d.Title(), '' ) + self.assertEqual( d.Description(), '' ) + self.assertEqual( d.getRemoteUrl(), '' ) def test_StructuredText( self ): d = Link('foo') d._writeFromPUT( body=BASIC_STRUCTUREDTEXT ) self.assertEqual( d.Title(), 'Zope Community' ) - self.assertEqual( - d.Description(), 'Link to the Zope Community website.' ) + self.assertEqual( d.Description() + , 'Link to the Zope Community website.' ) + self.assertEqual( len(d.Subject()), 3 ) + self.assertEqual( d.getRemoteUrl(), 'http://www.zope.org' ) + + def test_StructuredText_w_Continuation( self ): + + d = Link('foo') + d._writeFromPUT( body=STX_W_CONTINUATION ) + rnlinesplit = re.compile( r'\r?\n?' ) + desc_lines = rnlinesplit.split( d.Description() ) + + self.assertEqual( d.Title(), 'Zope Community' ) + self.assertEqual( desc_lines[0] + , 'Link to the Zope Community website,' ) + self.assertEqual( desc_lines[1] + , 'including hundreds of contributed Zope products.' ) self.assertEqual( len(d.Subject()), 3 ) self.assertEqual( d.getRemoteUrl(), 'http://www.zope.org' ) From tseaver@zope.com Tue Jan 8 18:26:27 2002 From: tseaver@zope.com (Tres Seaver) Date: Tue, 8 Jan 2002 13:26:27 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.16 Message-ID: <200201081826.g08IQRw05363@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv5291 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Merge fix for Tracker #407 from branch. === CMF/CHANGES.txt 1.36.2.15 => 1.36.2.16 === Bugs Fixed + - Cleaned up emission of RFC822-style headers (Tracker #407), + terminating eaders must be terminated with CRLF, and padding + continuation lines (for values with embedded newlines) with + leading whitespace). + - Added external method update_catalogIndexes.py to run as part of a upgrade to CMFs migrating to Zope2.4+ from from CMF sites which were built using Zope2.3 catalog From andrew@zope.com Tue Jan 8 21:05:01 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 16:05:01 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Link.py:1.13.2.4 utils.py:1.8.4.2 Message-ID: <200201082105.g08L51p24521@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv23994/CMFDefault Modified Files: Tag: CMF-1_2-branch Link.py utils.py Log Message: *Fixed utils.parseHeadersBody to properly handle windows and unix newlines. *Added full webdav code to support Link editing over webdav. *Updated these changes in CHANGES.txt === CMF/CMFDefault/Link.py 1.13.2.3 => 1.13.2.4 === from Products.CMFCore import CMFCorePermissions -from Products.CMFCore.PortalContent import PortalContent +from Products.CMFCore.PortalContent import PortalContent, NoWL +from Products.CMFCore.PortalContent import ResourceLockedError from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import keywordsplitter @@ -155,12 +156,10 @@ security.declarePrivate( '_writeFromPUT' ) def _writeFromPUT( self, body ): - headers = {} headers, body = parseHeadersBody(body, headers) lines = string.split( body, '\n' ) self.edit( lines[0] ) - headers['Format'] = self.URL_FORMAT new_subject = keywordsplitter(headers) headers['Subject'] = new_subject or self.Subject() @@ -169,7 +168,7 @@ if key != 'Format' and not haveheader(key): headers[key] = value - self.editMetadata(title=headers['Title'], + self._editMetadata(title=headers['Title'], subject=headers['Subject'], description=headers['Description'], contributors=headers['Contributors'], @@ -186,11 +185,18 @@ """ Handle HTTP / WebDAV / FTP PUT requests. """ - self.dav__init(REQUEST, RESPONSE) + if not NoWL: + self.dav__init(REQUEST, RESPONSE) + self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) body = REQUEST.get('BODY', '') - self._writeFromPUT( body ) - RESPONSE.setStatus(204) - return RESPONSE + try: + self._writeFromPUT( body ) + RESPONSE.setStatus(204) + return RESPONSE + except ResourceLockedError, msg: + get_transaction().abort() + RESPONSE.setStatus(423) + return RESPONSE security.declareProtected( CMFCorePermissions.View, 'manage_FTPget' ) def manage_FTPget(self): === CMF/CMFDefault/utils.py 1.8.4.1 => 1.8.4.2 === -def parseHeadersBody( body, headers=None ): +def parseHeadersBody( body, headers=None, rc=re.compile(r'\n|\r\n')): """ Parse any leading 'RFC-822'-ish headers from an uploaded document, returning a dictionary containing the headers @@ -61,7 +61,7 @@ Allow passing initial dictionary as headers. """ # Split the lines apart, taking into account Mac|Unix|Windows endings - lines = re.split(r'[\n\r]+?', body) + lines = rc.split(body) i = 0 if headers is None: @@ -71,9 +71,7 @@ hdrlist = [] for line in lines: - if line and line[-1] == '\r': - line = line[:-1] - if not line: + if not strip(line): break tokens = split( line, ': ' ) if len( tokens ) > 1: From andrew@zope.com Tue Jan 8 21:05:01 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 16:05:01 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.17 Message-ID: <200201082105.g08L51F24520@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv23994 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *Fixed utils.parseHeadersBody to properly handle windows and unix newlines. *Added full webdav code to support Link editing over webdav. *Updated these changes in CHANGES.txt === CMF/CHANGES.txt 1.36.2.16 => 1.36.2.17 === Bugs Fixed + - Fixed CMFDefault.utils.parseHeadersBody to properly handle the headers + generated on a windows app (i.e. Dreamweaver) with /r/n; added the + compiled regular expression object to the method signature. + + - Added full webdav sipport code to Link.py. Changed _writeFromPUT to call + _editMetadata instead of editMetadata. + - Cleaned up emission of RFC822-style headers (Tracker #407), - terminating eaders must be terminated with CRLF, and padding + terminating headers must be terminated with CRLF, and padding continuation lines (for values with embedded newlines) with leading whitespace). From andrew@zope.com Tue Jan 8 21:21:47 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 16:21:47 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Link.py:1.13.2.5 Message-ID: <200201082121.g08LLlP28389@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv28299/CMFDefault Modified Files: Tag: CMF-1_2-branch Link.py Log Message: *Fixed where new instances of Link were not getting the proper format set to 'text/url'. *Added unittests for format and URL_FORMAT. *Updated changes to CHANGES.txt === CMF/CMFDefault/Link.py 1.13.2.4 => 1.13.2.5 === self.remote_url=remote_url self.description=description + self.format=self.URL_FORMAT security.declareProtected( CMFCorePermissions.ModifyPortalContent , 'manage_edit' ) From andrew@zope.com Tue Jan 8 21:21:47 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 16:21:47 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Link.py:1.2.2.3 Message-ID: <200201082121.g08LLlk28391@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv28299/CMFDefault/tests Modified Files: Tag: CMF-1_2-branch test_Link.py Log Message: *Fixed where new instances of Link were not getting the proper format set to 'text/url'. *Added unittests for format and URL_FORMAT. *Updated changes to CHANGES.txt === CMF/CMFDefault/tests/test_Link.py 1.2.2.2 => 1.2.2.3 === self.assertEqual( d.Description(), '' ) self.assertEqual( d.getRemoteUrl(), '' ) + self.assertEqual( d.format, 'text/url' ) + self.assertEqual( d.URL_FORMAT, 'text/url') def test_StructuredText( self ): d = Link('foo') From andrew@zope.com Tue Jan 8 21:22:17 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 16:22:17 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.18 Message-ID: <200201082122.g08LMH328645@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv28299 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *Fixed where new instances of Link were not getting the proper format set to 'text/url'. *Added unittests for format and URL_FORMAT. *Updated changes to CHANGES.txt === CMF/CHANGES.txt 1.36.2.17 => 1.36.2.18 === Bugs Fixed + - Fixed setting the Link.format to URL_FORMAT so the initially returned + metadata headers would return 'text/url' properly. Added unittests. + - Fixed CMFDefault.utils.parseHeadersBody to properly handle the headers generated on a windows app (i.e. Dreamweaver) with /r/n; added the compiled regular expression object to the method signature. From andrew@zope.com Tue Jan 8 22:05:11 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 17:05:11 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.19 Message-ID: <200201082205.g08M5Bp06612@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv6542 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *Fixed a bug in Favorite.getObject to use restrictedTraverse on the portal object. === CMF/CHANGES.txt 1.36.2.18 => 1.36.2.19 === Bugs Fixed + - Fixed a bug in Favorites.getObject to use restrictedTraverse on the portal + object. + - Fixed setting the Link.format to URL_FORMAT so the initially returned metadata headers would return 'text/url' properly. Added unittests. From andrew@zope.com Tue Jan 8 22:05:11 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 8 Jan 2002 17:05:11 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.13.2.2 Message-ID: <200201082205.g08M5BS06614@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv6542/CMFDefault Modified Files: Tag: CMF-1_2-branch Favorite.py Log Message: *Fixed a bug in Favorite.getObject to use restrictedTraverse on the portal object. === CMF/CMFDefault/Favorite.py 1.13.2.1 => 1.13.2.2 === linking to """ - return self.restrictedTraverse(self.getRemoteUrl()) + portal_url = getToolByName(self, 'portal_url') + return portal_url.getPortalObject().retrictedTraverse(self.remote_url) def edit( self, remote_url ): """ From jens@zope.com Wed Jan 9 00:26:13 2002 From: jens@zope.com (Jens Vagelpohl) Date: Tue, 8 Jan 2002 19:26:13 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.13.2.2 In-Reply-To: <200201082205.g08M5BS06614@cvs.baymountain.com> Message-ID: <7CAB624E-0497-11D6-AE56-00039363690C@zope.com> andrew, there is a typo in your fix... you should check the code before you check it in... + return portal_url.getPortalObject().retrictedTraverse(self.remote_url) you typed "retrictedTraverse" instead of "restrictedTraverse" jens On Tuesday, January 8, 2002, at 05:05 , Andrew Sawyers wrote: > Update of /cvs-repository/CMF/CMFDefault > In directory cvs.zope.org:/tmp/cvs-serv6542/CMFDefault > > Modified Files: > Tag: CMF-1_2-branch > Favorite.py > Log Message: > > *Fixed a bug in Favorite.getObject to use restrictedTraverse on > the portal object. > > > === CMF/CMFDefault/Favorite.py 1.13.2.1 => 1.13.2.2 === > linking to > """ > - return self.restrictedTraverse(self.getRemoteUrl()) > + portal_url = getToolByName(self, 'portal_url') > + return > portal_url.getPortalObject().retrictedTraverse(self.remote_url) > > def edit( self, remote_url ): > """ > > > _______________________________________________ > CMF-checkins mailing list > CMF-checkins@python.org > http://lists.zope.org/mailman/listinfo/cmf-checkins From tseaver@zope.com Wed Jan 9 13:39:49 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:39:49 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.13.2.2.2.1 Message-ID: <200201091339.g09Ddng16198@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv16176 Modified Files: Tag: tseaver-tracker_444-branch Favorite.py Log Message: - Fix typo. - Add minimal unit test for Favorites. === CMF/CMFDefault/Favorite.py 1.13.2.2 => 1.13.2.2.2.1 === ############################################################################## """ -""" + Favorites represent references to other objects within the same + CMF site.. -ADD_CONTENT_PERMISSION = 'Add portal content' +$Id$ +""" +__version__ = "$Revision$"[11:-2] import Globals from Globals import HTMLFile, HTML @@ -116,7 +119,7 @@ linking to """ portal_url = getToolByName(self, 'portal_url') - return portal_url.getPortalObject().retrictedTraverse(self.remote_url) + return portal_url.getPortalObject().restrictedTraverse(self.remote_url) def edit( self, remote_url ): """ From tseaver@zope.com Wed Jan 9 13:39:49 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:39:49 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Favorite.py:1.1.2.1 Message-ID: <200201091339.g09DdnS16200@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv16176/tests Added Files: Tag: tseaver-tracker_444-branch test_Favorite.py Log Message: - Fix typo. - Add minimal unit test for Favorites. === Added File CMF/CMFDefault/tests/test_Favorite.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """ Unit tests for Favorites. $Id: test_Favorite.py,v 1.1.2.1 2002/01/09 13:39:48 tseaver Exp $ """ __version__ = "$Revision: 1.1.2.1 $"[11:-2] import unittest import Acquisition from Acquisition import aq_inner, aq_parent class DummyURLTool( Acquisition.Implicit ): root = 'DummyTool' def __call__( self ): return self.root getPortalPath = __call__ def getPortalObject( self ): return aq_parent( aq_inner( self ) ) def getIcon( self, relative=0 ): return 'Tool: %s' % relative class DummySite( Acquisition.Implicit ): def __init__( self, **kw ): self.__dict__.update( kw ) def restrictedTraverse( self, path ): return path and getattr( self, path ) or self def getIcon( self, relative=0 ): return 'Site: %s' % relative class FavoriteTests( unittest.TestCase ): def setUp( self ): self.tool = DummyURLTool() self.site = DummySite( portal_url=self.tool ) def _makeOne( self, *args, **kw ): from Products.CMFDefault.Favorite import Favorite f = apply( Favorite, args, kw ) return f.__of__( self.site ) def test_Empty( self ): f = self._makeOne( 'foo' ) self.assertEqual( f.getId(), 'foo' ) self.assertEqual( f.Title(), '' ) self.assertEqual( f.Description(), '' ) self.assertEqual( f.getRemoteUrl(), self.tool.root ) self.assertEqual( f.getObject(), self.site ) self.assertEqual( f.getIcon(), self.site.getIcon() ) self.assertEqual( f.getIcon(1), self.site.getIcon(1) ) def test_CtorArgs( self ): self.assertEqual( self._makeOne( 'foo' , title='Title' ).Title(), 'Title' ) self.assertEqual( self._makeOne( 'bar' , description='Description' ).Description(), 'Description' ) baz = self._makeOne( 'baz', remote_url='portal_url' ) self.assertEqual( baz.getObject(), self.tool ) self.assertEqual( baz.getRemoteUrl() , '%s/portal_url' % self.tool.root ) self.assertEqual( baz.getIcon(), self.tool.getIcon() ) def test_edit( self ): f = self._makeOne( 'foo' ) f.edit( 'portal_url' ) self.assertEqual( f.getObject(), self.tool ) self.assertEqual( f.getRemoteUrl() , '%s/portal_url' % self.tool.root ) self.assertEqual( f.getIcon(), self.tool.getIcon() ) def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.makeSuite( FavoriteTests ) ) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() From tseaver@zope.com Wed Jan 9 13:41:23 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:41:23 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.13.2.3 Message-ID: <200201091341.g09DfN416434@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv16420 Modified Files: Tag: CMF-1_2-branch Favorite.py Log Message: - Fix typo, add unit tests for Favoites (merged from branch). === CMF/CMFDefault/Favorite.py 1.13.2.2 => 1.13.2.3 === ############################################################################## """ -""" + Favorites represent references to other objects within the same + CMF site.. -ADD_CONTENT_PERMISSION = 'Add portal content' +$Id$ +""" +__version__ = "$Revision$"[11:-2] import Globals from Globals import HTMLFile, HTML @@ -116,7 +119,7 @@ linking to """ portal_url = getToolByName(self, 'portal_url') - return portal_url.getPortalObject().retrictedTraverse(self.remote_url) + return portal_url.getPortalObject().restrictedTraverse(self.remote_url) def edit( self, remote_url ): """ From tseaver@zope.com Wed Jan 9 13:41:23 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:41:23 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Favorite.py:1.1.4.1 Message-ID: <200201091341.g09DfN616435@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv16420/tests Added Files: Tag: CMF-1_2-branch test_Favorite.py Log Message: - Fix typo, add unit tests for Favoites (merged from branch). === Added File CMF/CMFDefault/tests/test_Favorite.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """ Unit tests for Favorites. $Id: test_Favorite.py,v 1.1.4.1 2002/01/09 13:41:23 tseaver Exp $ """ __version__ = "$Revision: 1.1.4.1 $"[11:-2] import unittest import Acquisition from Acquisition import aq_inner, aq_parent class DummyURLTool( Acquisition.Implicit ): root = 'DummyTool' def __call__( self ): return self.root getPortalPath = __call__ def getPortalObject( self ): return aq_parent( aq_inner( self ) ) def getIcon( self, relative=0 ): return 'Tool: %s' % relative class DummySite( Acquisition.Implicit ): def __init__( self, **kw ): self.__dict__.update( kw ) def restrictedTraverse( self, path ): return path and getattr( self, path ) or self def getIcon( self, relative=0 ): return 'Site: %s' % relative class FavoriteTests( unittest.TestCase ): def setUp( self ): self.tool = DummyURLTool() self.site = DummySite( portal_url=self.tool ) def _makeOne( self, *args, **kw ): from Products.CMFDefault.Favorite import Favorite f = apply( Favorite, args, kw ) return f.__of__( self.site ) def test_Empty( self ): f = self._makeOne( 'foo' ) self.assertEqual( f.getId(), 'foo' ) self.assertEqual( f.Title(), '' ) self.assertEqual( f.Description(), '' ) self.assertEqual( f.getRemoteUrl(), self.tool.root ) self.assertEqual( f.getObject(), self.site ) self.assertEqual( f.getIcon(), self.site.getIcon() ) self.assertEqual( f.getIcon(1), self.site.getIcon(1) ) def test_CtorArgs( self ): self.assertEqual( self._makeOne( 'foo' , title='Title' ).Title(), 'Title' ) self.assertEqual( self._makeOne( 'bar' , description='Description' ).Description(), 'Description' ) baz = self._makeOne( 'baz', remote_url='portal_url' ) self.assertEqual( baz.getObject(), self.tool ) self.assertEqual( baz.getRemoteUrl() , '%s/portal_url' % self.tool.root ) self.assertEqual( baz.getIcon(), self.tool.getIcon() ) def test_edit( self ): f = self._makeOne( 'foo' ) f.edit( 'portal_url' ) self.assertEqual( f.getObject(), self.tool ) self.assertEqual( f.getRemoteUrl() , '%s/portal_url' % self.tool.root ) self.assertEqual( f.getIcon(), self.tool.getIcon() ) def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.makeSuite( FavoriteTests ) ) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() From tseaver@zope.com Wed Jan 9 13:46:49 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:46:49 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_all.py:1.9.2.1 Message-ID: <200201091346.g09DknQ17655@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv17646/CMFDefault/tests Modified Files: Tag: CMF-1_2-branch test_all.py Log Message: - Include new Favorites test. === CMF/CMFDefault/tests/test_all.py 1.9 => 1.9.2.1 === from Products.CMFDefault.tests import test_NewsItem from Products.CMFDefault.tests import test_Link +from Products.CMFDefault.tests import test_Favorite from Products.CMFDefault.tests import test_Image from Products.CMFDefault.tests import test_MetadataTool from Products.CMFDefault.tests import test_utils @@ -17,6 +18,7 @@ suite.addTest( test_Document.test_suite() ) suite.addTest( test_NewsItem.test_suite() ) suite.addTest( test_Link.test_suite() ) + suite.addTest( test_Favorite.test_suite() ) suite.addTest( test_Image.test_suite() ) suite.addTest( test_MetadataTool.test_suite() ) suite.addTest( test_utils.test_suite() ) From tseaver@zope.com Wed Jan 9 13:48:22 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:48:22 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Favorite.py:1.15 Message-ID: <200201091348.g09DmMt18206@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv18193/CMFDefault Modified Files: Favorite.py Log Message: - Fix typo, add unit tests for Favoites (merged from branch). === CMF/CMFDefault/Favorite.py 1.14 => 1.15 === ############################################################################## """ -""" + Favorites represent references to other objects within the same + CMF site.. -ADD_CONTENT_PERMISSION = 'Add portal content' +$Id$ +""" +__version__ = "$Revision$"[11:-2] import Globals from Globals import HTMLFile, HTML @@ -115,7 +118,8 @@ Return the actual object that the Favorite is linking to """ - return self.restrictedTraverse(self.getRemoteUrl()) + portal_url = getToolByName(self, 'portal_url') + return portal_url.getPortalObject().restrictedTraverse(self.remote_url) def edit( self, remote_url ): """ From tseaver@zope.com Wed Jan 9 13:48:22 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 08:48:22 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Favorite.py:1.2 test_all.py:1.10 Message-ID: <200201091348.g09DmMg18209@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv18193/CMFDefault/tests Modified Files: test_all.py Added Files: test_Favorite.py Log Message: - Fix typo, add unit tests for Favoites (merged from branch). === CMF/CMFDefault/tests/test_Favorite.py 1.1 => 1.2 === +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +""" + Unit tests for Favorites. + +$Id$ +""" +__version__ = "$Revision$"[11:-2] + + +import unittest +import Acquisition +from Acquisition import aq_inner, aq_parent + +class DummyURLTool( Acquisition.Implicit ): + + root = 'DummyTool' + + def __call__( self ): + return self.root + + getPortalPath = __call__ + + def getPortalObject( self ): + return aq_parent( aq_inner( self ) ) + + def getIcon( self, relative=0 ): + return 'Tool: %s' % relative + +class DummySite( Acquisition.Implicit ): + + def __init__( self, **kw ): + self.__dict__.update( kw ) + + def restrictedTraverse( self, path ): + return path and getattr( self, path ) or self + + def getIcon( self, relative=0 ): + return 'Site: %s' % relative + +class FavoriteTests( unittest.TestCase ): + + def setUp( self ): + + self.tool = DummyURLTool() + self.site = DummySite( portal_url=self.tool ) + + def _makeOne( self, *args, **kw ): + + from Products.CMFDefault.Favorite import Favorite + + f = apply( Favorite, args, kw ) + return f.__of__( self.site ) + + def test_Empty( self ): + + f = self._makeOne( 'foo' ) + + self.assertEqual( f.getId(), 'foo' ) + self.assertEqual( f.Title(), '' ) + self.assertEqual( f.Description(), '' ) + self.assertEqual( f.getRemoteUrl(), self.tool.root ) + self.assertEqual( f.getObject(), self.site ) + self.assertEqual( f.getIcon(), self.site.getIcon() ) + self.assertEqual( f.getIcon(1), self.site.getIcon(1) ) + + def test_CtorArgs( self ): + + self.assertEqual( self._makeOne( 'foo' + , title='Title' + ).Title(), 'Title' ) + + self.assertEqual( self._makeOne( 'bar' + , description='Description' + ).Description(), 'Description' ) + + baz = self._makeOne( 'baz', remote_url='portal_url' ) + self.assertEqual( baz.getObject(), self.tool ) + self.assertEqual( baz.getRemoteUrl() + , '%s/portal_url' % self.tool.root ) + self.assertEqual( baz.getIcon(), self.tool.getIcon() ) + + def test_edit( self ): + + f = self._makeOne( 'foo' ) + f.edit( 'portal_url' ) + self.assertEqual( f.getObject(), self.tool ) + self.assertEqual( f.getRemoteUrl() + , '%s/portal_url' % self.tool.root ) + self.assertEqual( f.getIcon(), self.tool.getIcon() ) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest( unittest.makeSuite( FavoriteTests ) ) + return suite + +def run(): + unittest.TextTestRunner().run(test_suite()) + +if __name__ == '__main__': + run() + === CMF/CMFDefault/tests/test_all.py 1.9 => 1.10 === from Products.CMFDefault.tests import test_NewsItem from Products.CMFDefault.tests import test_Link +from Products.CMFDefault.tests import test_Favorite from Products.CMFDefault.tests import test_Image from Products.CMFDefault.tests import test_MetadataTool from Products.CMFDefault.tests import test_utils @@ -17,6 +18,7 @@ suite.addTest( test_Document.test_suite() ) suite.addTest( test_NewsItem.test_suite() ) suite.addTest( test_Link.test_suite() ) + suite.addTest( test_Favorite.test_suite() ) suite.addTest( test_Image.test_suite() ) suite.addTest( test_MetadataTool.test_suite() ) suite.addTest( test_utils.test_suite() ) From tseaver@zope.com Wed Jan 9 14:24:25 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 09:24:25 -0500 Subject: [CMF-checkins] CVS: CMF - ISSUES.txt:1.2.14.2 Message-ID: <200201091424.g09EOPr27289@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv27225 Modified Files: Tag: CMF-1_2-branch ISSUES.txt Log Message: - Note change to interface of content factory methods, especially impact on generated ZClass methods. === CMF/ISSUES.txt 1.2.14.1 => 1.2.14.2 === 2. Run this method by clicking on its "Test" tab. + + Issue: Content constructor methods interface change + + Factory methods used to construct content instances used to + return the URL of the "initial view" of the new object; since + August 2001, the interface has changed to require that the method + return the new object directly, wrapped in its container. This + change will most likely affect ZClass-based content using the + default DTML factory methods. + + Fix + + - Update all such methods to return the new object. This + update may be done more simply by replacing the generated + DTMLMethod used for ZClass construction with an equivalent + PythonScript. From tseaver@zope.com Wed Jan 9 14:30:08 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 09:30:08 -0500 Subject: [CMF-checkins] CVS: CMF - ISSUES.txt:1.2.14.3 Message-ID: <200201091430.g09EU8h28520@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv28510 Modified Files: Tag: CMF-1_2-branch ISSUES.txt Log Message: - Disentangle punny diction. === CMF/ISSUES.txt 1.2.14.2 => 1.2.14.3 === Issue: Content constructor methods interface change - Factory methods used to construct content instances used to + The CMF used to require that factory methods for content instances return the URL of the "initial view" of the new object; since August 2001, the interface has changed to require that the method return the new object directly, wrapped in its container. This From andrew@zope.com Wed Jan 9 19:43:34 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 9 Jan 2002 14:43:34 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - PortalContent.py:1.32.2.2 TypesTool.py:1.26.2.3 Message-ID: <200201091943.g09JhYR19586@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv18638/CMFCore Modified Files: Tag: CMF-1_2-branch PortalContent.py TypesTool.py Log Message: *Workflow notification has moved to the contstructInstance method on the TypesTool after the _setPortalTypeName method has been called on the object rather then in manage_afterAdd. === CMF/CMFCore/PortalContent.py 1.32.2.1 => 1.32.2.2 === # if aq_base(container) is not aq_base(self): - wf = getToolByName(self, 'portal_workflow', None) - if wf is not None: - wf.notifyCreated(self) self.indexObject() def manage_beforeDelete(self, item, container): === CMF/CMFCore/TypesTool.py 1.26.2.2 => 1.26.2.3 === from Globals import InitializeClass, DTMLFile from utils import UniqueObject, SimpleItemWithProperties, tuplize -from utils import _dtmldir, _checkPermission, cookString +from utils import _dtmldir, _checkPermission, cookString, getToolByName import string import urllib from AccessControl import getSecurityManager, ClassSecurityInfo @@ -448,7 +448,9 @@ if hasattr(ob, '_setPortalTypeName'): ob._setPortalTypeName(self.getId()) - + wf = getToolByName(ob, 'portal_workflow', None) + if wf is not None: + wf.notifyCreated(ob) return ob InitializeClass( FactoryTypeInformation ) From andrew@zope.com Wed Jan 9 19:44:04 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 9 Jan 2002 14:44:04 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.20 Message-ID: <200201091944.g09Ji4c19642@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv18638 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *Workflow notification has moved to the contstructInstance method on the TypesTool after the _setPortalTypeName method has been called on the object rather then in manage_afterAdd. === CMF/CHANGES.txt 1.36.2.19 => 1.36.2.20 === Bugs Fixed + - Fixed a bug where the workflow notifyCreated method was called during + manage_afterAdd in PortalContent, making it possible for the + notification to occur on the wrong workflow. The notification has + moved to the contstructInstance method on the TypesTool after + the _setPortalTypeName method has been called on the object. + - Fixed a bug in Favorites.getObject to use restrictedTraverse on the portal object. From tseaver@zope.com Wed Jan 9 22:28:13 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 17:28:13 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/dtml - addTypeInfo.dtml:1.1.16.2 Message-ID: <200201092228.g09MSDk25983@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv25819/CMFCore/dtml Modified Files: Tag: CMF-1_2-branch addTypeInfo.dtml Log Message: - Backing out Jeffrey's feature, as it breaks the add list for the types tool (Tracker #409 won't land for CMF 1.2 b2). === CMF/CMFCore/dtml/addTypeInfo.dtml 1.1.16.1 => 1.1.16.2 === + @@ -10,7 +11,7 @@
- +
From tseaver@zope.com Wed Jan 9 22:28:13 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 17:28:13 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_TypesTool.py:1.7.2.3 Message-ID: <200201092228.g09MSDE25985@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv25819/CMFCore/tests Modified Files: Tag: CMF-1_2-branch test_TypesTool.py Log Message: - Backing out Jeffrey's feature, as it breaks the add list for the types tool (Tracker #409 won't land for CMF 1.2 b2). === CMF/CMFCore/tests/test_TypesTool.py 1.7.2.2 => 1.7.2.3 === getUserName = getId - def has_permission(self, permission, obj): - # For types tool tests dealing with filtered_meta_types - return 1 - def allowed( self, object, object_roles=None ): # for testing permissions on actions if object.getId() == 'actions_dummy': @@ -109,10 +105,6 @@ def extra_meta_types(): return ( { 'name' : 'Dummy', 'action' : 'manage_addFolder' }, ) -class DummyTypeInfo(TypeInformation): - """ new class of type info object """ - meta_type = "Dummy Test Type Info" - class TypesToolTests( unittest.TestCase ): def setUp( self ): @@ -190,34 +182,10 @@ custom_view = utils._getViewFor( dummy, view='view2' )() unpermitted_view = utils._getViewFor( dummy, view='edit' )() - self.failUnlessEqual(default_view, 'view') - self.failUnlessEqual(custom_view, 'view2') - self.failIf(unpermitted_view == 'edit') - self.failUnlessEqual(unpermitted_view, 'view') - - def test_AddingOtherTypeInfos(self): - addTypeFactory(DummyTypeInfo) - tool = self.root.portal_types - type_type = DummyTypeInfo.meta_type - - fmt = [ mt['name'] for mt in tool.filtered_meta_types() ] - self.failUnless(DummyTypeInfo.meta_type in fmt, - "Subfactory meta type not registered") - - atif = tool.manage_addTypeInfoForm(self.root.REQUEST, - type_type=type_type) - self.failUnless(atif.find(type_type) > -1, - "'%s' not found in type info form" % type_type) - - tool.manage_addTypeInformation(id='foo_default', type_type=None) - fd = tool.foo_default - self.failUnless(isinstance(fd, FactoryTypeInformation)) - self.failIf(isinstance(fd, DummyTypeInfo)) - - tool.manage_addTypeInformation(id='foo_sub', type_type=type_type) - fs = tool.foo_sub - self.failUnless(isinstance(fs, DummyTypeInfo), fs.__class__) - + assert default_view == 'view' + assert custom_view == 'view2' + assert unpermitted_view != 'edit' + assert unpermitted_view == 'view' class TypeInfoTests( unittest.TestCase ): @@ -571,8 +539,6 @@ ti.constructInstance( folder, 'dust' ) majyk_dust = folder._getOb( 'majyk_dust' ) self.assertEqual( majyk_dust.id, 'majyk_dust' ) - - def test_suite(): suite = unittest.TestSuite() From tseaver@zope.com Wed Jan 9 22:28:43 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 17:28:43 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.21 Message-ID: <200201092228.g09MShE26439@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv25819 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: - Backing out Jeffrey's feature, as it breaks the add list for the types tool (Tracker #409 won't land for CMF 1.2 b2). === CMF/CHANGES.txt 1.36.2.20 => 1.36.2.21 === Features Added - - Extended TypesTool to permit registration of new TypeInformation - implementations (Tracker #409, thanks to Jeffrey Shell for the - work!) - - Enabled querying actions from workflow tool in absence of actions tool (Tracker #401). From tseaver@zope.com Wed Jan 9 22:28:43 2002 From: tseaver@zope.com (Tres Seaver) Date: Wed, 9 Jan 2002 17:28:43 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - TypesTool.py:1.26.2.4 Message-ID: <200201092228.g09MShL26442@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv25819/CMFCore Modified Files: Tag: CMF-1_2-branch TypesTool.py Log Message: - Backing out Jeffrey's feature, as it breaks the add list for the types tool (Tracker #409 won't land for CMF 1.2 b2). === CMF/CMFCore/TypesTool.py 1.26.2.3 => 1.26.2.4 === from utils import _dtmldir, _checkPermission, cookString, getToolByName import string -import urllib from AccessControl import getSecurityManager, ClassSecurityInfo try: from AccessControl import Unauthorized @@ -35,22 +34,6 @@ _marker = [] # Create a new marker. - -_type_factories = {} -allowedTypes = ( 'Script (Python)' - , 'Python Method' - , 'DTML Method' - , 'External Method' - ) - -def addTypeFactory(factory, id=None): - # modeled after WorkflowTool.addWorkflowFactory() - global allowedTypes - if id is None: - id = getattr(factory, 'id', '') or getattr(factory, 'meta_type', '') - _type_factories[id] = factory - allowedTypes = allowedTypes + (factory.meta_type,) - class TypeInformation (SimpleItemWithProperties): """ Base class for information about a content type. @@ -454,7 +437,7 @@ return ob InitializeClass( FactoryTypeInformation ) -addTypeFactory(FactoryTypeInformation) + class ScriptableTypeInformation( TypeInformation ): """ @@ -509,13 +492,20 @@ return ob InitializeClass( ScriptableTypeInformation ) -addTypeFactory(ScriptableTypeInformation) + # Provide aliases for backward compatibility. ContentFactoryMetadata = FactoryTypeInformation ContentTypeInformation = ScriptableTypeInformation +allowedTypes = ( 'Script (Python)' + , 'Python Method' + , 'DTML Method' + , 'External Method' + , FactoryTypeInformation.meta_type + , ScriptableTypeInformation.meta_type + ) class TypesTool( UniqueObject, OFS.Folder.Folder ): """ @@ -539,17 +529,14 @@ def all_meta_types(self): all = TypesTool.inheritedAttribute('all_meta_types')(self) - factypes = [] - add_fac = factypes.append - for name, fac in _type_factories.items(): - query = urllib.urlencode({'type_type': name}) - factypes.append({ - 'name': fac.meta_type, - 'action': 'manage_addTypeInfoForm?%s' % query, - 'permission': CMFCorePermissions.ManagePortal, - }) - factypes.extend(all) - return factypes + return ( + {'name':FactoryTypeInformation.meta_type, + 'action':'manage_addFactoryTIForm', + 'permission':'Manage portal'}, + {'name':ScriptableTypeInformation.meta_type, + 'action':'manage_addScriptableTIForm', + 'permission':'Manage portal'}, + ) + tuple(all) def filtered_meta_types(self, user=None): # Filters the list of available meta types. @@ -584,17 +571,24 @@ _addTIForm = DTMLFile( 'addTypeInfo', _dtmldir ) - security.declareProtected(ManagePortal, 'manage_addTypeInfoForm') - def manage_addTypeInfoForm(self, REQUEST={}, type_type=''): - """ Return the type info form while keeping the list of - prefab type information up to date """ - return self._addTIForm(self, REQUEST, type_type=type_type, + security.declareProtected(ManagePortal, 'manage_addFactoryTIForm') + def manage_addFactoryTIForm(self, REQUEST): + ' ' + return self._addTIForm(self, REQUEST, scriptable='', + types=self.listDefaultTypeInformation()) + + security.declareProtected(ManagePortal, 'manage_addScriptableTIForm') + def manage_addScriptableTIForm(self, REQUEST): + ' ' + return self._addTIForm(self, REQUEST, scriptable='1', types=self.listDefaultTypeInformation()) security.declareProtected(ManagePortal, 'manage_addTypeInformation') - def manage_addTypeInformation(self, id=None, type_type=None, + def manage_addTypeInformation(self, id=None, scriptable='', typeinfo_name=None, RESPONSE=None): - """ Create a TypeInformation in self. """ + """ + Create a TypeInformation in self. + """ fti = None if typeinfo_name: info = self.listDefaultTypeInformation() @@ -608,9 +602,8 @@ id = fti.get('id', None) if not id: raise 'Bad Request', 'An id is required.' - - if type_type in _type_factories.keys(): - klass = _type_factories[type_type] + if scriptable: + klass = ScriptableTypeInformation else: klass = FactoryTypeInformation id = str(id) From andrew@zope.com Thu Jan 10 16:26:23 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:26:23 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/Extensions - Upgrade.py:1.2 update_catalogIndexes.py:1.2 Message-ID: <200201101626.g0AGQNC05095@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/Extensions In directory cvs.zope.org:/tmp/cvs-serv5016/CMFDefault/Extensions Added Files: Upgrade.py update_catalogIndexes.py Log Message: *Merging bugfixes from 1_2-branch into head. === CMF/CMFDefault/Extensions/Upgrade.py 1.1 => 1.2 === +# +# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE +# +############################################################################## +""" + Utility functions for upgrading CMFDefault-based sites. +""" + +from Acquisition import aq_inner +import string + +def upgrade_decor_skins( self ): + """ + Upgrade old skin diretories loaded from 'CMFDecor' to load from + 'CMFDefault' (and zap the 'zpt_images' one). + """ + log = [] + + DELETED_SKINS = ( 'zpt_images' , ) + + MOVED_SKINS = ( 'zpt_content' + , 'zpt_control' + , 'zpt_generic' + ) + + skins_tool = aq_inner( self ).portal_skins # start from CMFSite! + + for deleted in DELETED_SKINS: + + try: + + skins_tool._delObject( deleted ) + + except AttributeError: + pass + + else: + log.append( 'Deleted CMFDecor skin directory: %s' % deleted ) + + for moved in MOVED_SKINS: + + skin_dir = getattr( skins_tool, moved, None ) + + if skin_dir is not None: + + skin_dir.manage_properties( + dirpath='Products/CMFDefault/skins/%s' % moved ) + log.append( 'Updated CMFDecor skin directory to CMFDefault: %s' + % moved ) + + return string.join( log, '\n' ) === CMF/CMFDefault/Extensions/update_catalogIndexes.py 1.1 => 1.2 === + +def update_catalogIndexes(self): + ''' + External method to drop, re-add, and rebuild catalog Indexes for migrated + CMF sites from Zope 2.3 to 2.4+. + ''' + rIndexes = {'allowedRolesAndUsers': 'KeywordIndex' + , 'effective': 'FieldIndex' + , 'expires': 'FieldIndex'} + ct = getToolByName(self, 'portal_catalog') + map(lambda x, ct=ct: ct.delIndex(x), rIndexes.keys()) + map(lambda x, ct=ct: ct.addIndex(x[0], x[1]), rIndexes.items()) + ct.manage_reindexIndex(ids=rIndexes.keys()) + return 'Catalog Indexes rebuilt.' From andrew@zope.com Thu Jan 10 16:26:23 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:26:23 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Link.py:1.4 Message-ID: <200201101626.g0AGQNF05096@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv5016/CMFDefault/tests Modified Files: test_Link.py Log Message: *Merging bugfixes from 1_2-branch into head. === CMF/CMFDefault/tests/test_Link.py 1.3 => 1.4 === self.assertEqual( d.Description(), '' ) self.assertEqual( d.getRemoteUrl(), '' ) + self.assertEqual( d.format, 'text/url' ) + self.assertEqual( d.URL_FORMAT, 'text/url') def test_StructuredText( self ): d = Link('foo') From andrew@zope.com Thu Jan 10 16:26:52 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:26:52 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.43 ISSUES.txt:1.3 Message-ID: <200201101626.g0AGQqv05564@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv5016 Modified Files: CHANGES.txt ISSUES.txt Log Message: *Merging bugfixes from 1_2-branch into head. === CMF/CHANGES.txt 1.42 => 1.43 === +New Features - New Features - - - (__future__) Exposed role management for manager users on + - (__future__) Exposed role management for manager users on the default roster: managers can now "promote" members without going to the ZMI. - - (__future__) Added "custom schema" mechanism for content - objetcts: site managers can now define additional - propertysheets for a content type, which will then be - reflected in instances created from the type. - - - (__future__) Added simple link extraction / checking for - textual content. Link checking also works for Link objects. - - - (__future__) Added "composite content" types (see the - "dogbowl proposal", - (http://cmf.zope.org/rqmts/proposals/compounds/compoundproposal.txt). + - (__future__) Added "custom schema" mechanism for content + objetcts: site managers can now define additional + propertysheets for a content type, which will then be + reflected in instances created from the type. + + - (__future__) Added simple link extraction / checking for + textual content. Link checking also works for Link objects. + + - (__future__) Added "composite content" types (see the + "dogbowl proposal", + (http://cmf.zope.org/rqmts/proposals/compounds/compoundproposal.txt). + + - Enabled querying actions from workflow tool in absence + of actions tool (Tracker #401). + + - Added 'operator' attribute to CMFTopic.ListCriterion, to permit + specifying an operator ('and', for the most part) for indexes which + support it, e.g., KeywordIndex (Tracker #442). + + - Added ZMI interface for editing Link URL (Tracker #364). - - (__future__) Added DCWorkflow (through-the-web configurable + - (__future__) Added DCWorkflow (through-the-web configurable workflows) to the core set of CMF products. - - Extended TypesTool to permit registration of new TypeInformation - implementations (Tracker #409, thanks to Jeffrey Shell for the - work!) +Bugs Fixed + + - Fixed a bug where the workflow notifyCreated method was called during + manage_afterAdd in PortalContent, making it possible for the + notification to occur on the wrong workflow. The notification has + moved to the contstructInstance method on the TypesTool after + the _setPortalTypeName method has been called on the object. + + - Extended TypesTool to permit registration of new TypeInformation + implementations (Tracker #409, thanks to Jeffrey Shell for the + work!) + + - Fixed a bug in Favorites.getObject to use restrictedTraverse on the portal + object. + + - Made all tool-generated actions configurable through-the-web, + via an "Actions" tab on each tool; made the list of ActionProviders + configurable TTW as well. + + - Fixed setting the Link.format to URL_FORMAT so the initially returned + metadata headers would return 'text/url' properly. Added unittests. + + - Enabled querying actions from workflow tool in absence + of actions tool (Tracker #401). - - Made all tool-generated actions configurable through-the-web, - via an "Actions" tab on each tool; made the list of ActionProviders - configurable TTW as well. + - Fixed CMFDefault.utils.parseHeadersBody to properly handle the headers + generated on a windows app (i.e. Dreamweaver) with /r/n; added the + compiled regular expression object to the method signature. - - Enabled querying actions from workflow tool in absence - of actions tool (Tracker #401). + - Added full webdav sipport code to Link.py. Changed _writeFromPUT to call + _editMetadata instead of editMetadata. - Bug Fixes + - Made links emitted by 'topic_view' play nice with virtual hosting + (Tracker #433). - - Made links emitted by 'topic_view' play nice with virtual hosting - (Tracker #433). + - Cleaned up emission of RFC822-style headers (Tracker #407), + terminating headers must be terminated with CRLF, and padding + continuation lines (for values with embedded newlines) with + leading whitespace). - - Ensure that package initialization files are non-empty, to prevent + - Ensure that package initialization files are non-empty, to prevent suspicion that they were corrupted in download (Tracker #426). + + - Added external method update_catalogIndexes.py to run as part of a + upgrade to CMFs migrating to Zope2.4+ from from CMF sites which were built + using Zope2.3 catalog + + - Use ID to label Favorite when target has an empty Title (Tracker #440). + + - Allowed sub-folders to have different syndication properties + than parents (Tracker #421). + + - Added 'CMFDefault.Upgrade.upgrade_decor_skins' external method to + convert existing sites which had installed skin directories from the + now-deprecated 'CMFDecor' product (Tracker #434). Added note + explaining the issue, and the workaround, to 'ISSUES.txt'. + + - Ensure that Favorites display the correct, absolute URL to their + target, without needing to have tag set (Tracker #419). + + - Worked around Opera's strange insistence on selecting an option, + even for multi-select lists (Tracker #332). + + - Hardened CMFCore to initialize correctly in the absence of + the PageTemplates product (Tracker #430). + + - Restored slot in of ZPT main template into which content + can insert the tag (Tracker #418). + + - Fixed 'CMFTopic.SimpleIntegerCriterion.edit' to require a pair + of values when 'direction' is 'min:max'; updated skins to use + new 'getValueString', which renders such values properly + (Tracker #439). + + - Ensured that Documents created with initial STX get cooked + (Tracker #435). + + - Made links emitted by 'topic_view' play nice with virtual hosting + (Tracker #433). + + - Made 'CMFCore/interfaces/__init__.py' non-empty, to remove suspicion + that the file was corrupted in the download (Tracker #426). === CMF/ISSUES.txt 1.2 => 1.3 === +CMF 1.2: Known Issues Overview - This document describes known issues with the beta release of - the Content Management Framework (CMF), version 1.0. + This document describes known issues with the release of + the Content Management Framework (CMF), version 1.2. For more information on the CMF, please see the "website", http://cmf.zope.org. - Package and Module Name Changes - In order to improve the future maintainability of the CMF, - and as part of the "rebranding" of the product from "Portal - Toolkit" to "Content Management Framework", we have moved the - code base to a new set of packages: - - 'CMF' -- the "top-level" package, replaces 'ZopePTK'. - - 'CMFCore' -- provides essential interfaces and services of - the framework. All CMF sites will use this package; - most will not replace the services it provides. Most - modules in this package came from the old 'PTKBase' - package. - - 'CMFDefault' -- provides a set of content objects and - services which allow construction of a useful CMF site - "out of the box"; while many sites will use these - objects directly, many will extend or replace them. Most - modules in this package came from the old 'PTKDemo' - package. - - 'CMFTopic' -- provides a new, "add-in" content object, the - Topic. Topics represent "canned" catalog queries, and - are useful for presenting "logical collections" of - content, based on common metadata. - - CVS Changes - - Until this release, the "canonical" CVS repository for the - PTK/CMF has been on the public CVS mirror, cvs.zope.org. - This placement has been somewhat problematic: because all - the *other* CVS code is replicated from Digital Creations' - internal CVS repository, tags and branches created on PTK/CMF - files tended to get "forgotten" during the synchronization. - Going forward, we will keep the CMF repository alongside the - main Zope repository, and replicate it to the public mirror. - - Migrating Existing PTK Sites - - The changes to package and module names (mentioned above) - have significant impacts for existing PTK sites: instances - created from classes defined in 'PTKBase' or 'PTKDemo' will - need to be "re-seated" as instances of their cognates in - 'CMFCore' or 'CMFDefault'. In the past, we have arranged to - do such reseating "in place"; for this change, however, we - plan not to require a "copying" migration, in order to remove - the need to maintain the backward-compatibility cruft - required by the in-place strategy. - - We will provide a script shortly to aid this migration (in - fact, it will be the script which we use to migrate the - "dogbowl" site this week. - - N.B.: This script is 'CMFDefault.migrate_ptk.py'. + Issue: 'CMFDecor'-based skins lost in upgrade to CMF 1.2 + + When migrating a site to 1.2 from CMF, where the skin directories + from the 'CMFDecor' product had been installed, such directories + are no longer present (the product has been merged into 'CMFDefault'). + + See the "tracker issue", + http://www.zope.org/Products/PTK/Tracker/434 + + Workaround + + 1. Create an ExternalMethod in the root of your CMF site + using the following properties: + + *Id* -- upgrade_decor_skins + + *Title* -- Upgrade CMFDecor Skin Directories + + *Module Name* -- CMFDefault.Upgrade + + *Function Name* -- upgrade_decor_skins + + 2. Run this method by clicking on its "Test" tab. + + Issue: Content constructor methods interface change + + The CMF used to require that factory methods for content instances + return the URL of the "initial view" of the new object; since + August 2001, the interface has changed to require that the method + return the new object directly, wrapped in its container. This + change will most likely affect ZClass-based content using the + default DTML factory methods. + + Fix + + - Update all such methods to return the new object. This + update may be done more simply by replacing the generated + DTMLMethod used for ZClass construction with an equivalent + PythonScript. From andrew@zope.com Thu Jan 10 16:26:53 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:26:53 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.21 DirectoryView.py:1.17 DiscussionTool.py:1.6 FSPropertiesObject.py:1.8 MemberDataTool.py:1.14 RegistrationTool.py:1.9 TypesTool.py:1.29 Message-ID: <200201101626.g0AGQrY05573@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv5016/CMFCore Modified Files: ActionsTool.py DirectoryView.py DiscussionTool.py FSPropertiesObject.py MemberDataTool.py RegistrationTool.py TypesTool.py Log Message: *Merging bugfixes from 1_2-branch into head. === CMF/CMFCore/ActionsTool.py 1.20 => 1.21 === $Id$ -""" __version__='$Revision$'[11:-2] === CMF/CMFCore/DirectoryView.py 1.16 => 1.17 === security.declareProtected(ManagePortal, 'manage_properties') - def manage_properties( self, dirpath, REQUEST ): + def manage_properties( self, dirpath, REQUEST=None ): """ Update the directory path of the DV. """ self.__dict__['_real']._dirpath = dirpath - REQUEST['RESPONSE'].redirect( '%s/manage_propertiesForm' - % self.absolute_url() ) + if REQUEST is not None: + REQUEST['RESPONSE'].redirect( '%s/manage_propertiesForm' + % self.absolute_url() ) security.declareProtected(AccessContentsInformation, 'getCustomizableObject') === CMF/CMFCore/DiscussionTool.py 1.5 => 1.6 === content = info.content if content is None or not self.isDiscussionAllowedFor(content): - return None + return [] discussion = self.getDiscussionFor(content) if discussion.aq_base == content.aq_base: === CMF/CMFCore/FSPropertiesObject.py 1.7 => 1.8 === }) except: - import pdb; pdb.set_trace() raise ValueError, ( 'Error processing line %s of %s:\n%s' % (lino,fp,line) ) self._properties = tuple(map) === CMF/CMFCore/MemberDataTool.py 1.13 => 1.14 === __version__='$Revision$'[11:-2] - import string from utils import UniqueObject, getToolByName, _dtmldir from OFS.SimpleItem import SimpleItem === CMF/CMFCore/RegistrationTool.py 1.8 => 1.9 === # 'portal_registration' interface methods # + security.declarePrivate('listActions') + def listActions(self, info): + return None security.declarePublic('isRegistrationAllowed') def isRegistrationAllowed(self, REQUEST): === CMF/CMFCore/TypesTool.py 1.28 => 1.29 === from utils import _dtmldir, _checkPermission, cookString import urllib +from utils import _dtmldir, _checkPermission, cookString, getToolByName import string from AccessControl import getSecurityManager, ClassSecurityInfo +try: + from AccessControl import Unauthorized +except: + Unauthorized = 'Unauthorized' from Acquisition import aq_base import Products, CMFCorePermissions from ActionProviderBase import ActionProviderBase @@ -432,7 +437,7 @@ m = self._getFactoryMethod(container, raise_exc=1) if m is None: - raise 'Unauthorized', ('Cannot create %s' % self.getId()) + raise Unauthorized, ('Cannot create %s' % self.getId()) id = str(id) @@ -447,7 +452,9 @@ if hasattr(ob, '_setPortalTypeName'): ob._setPortalTypeName(self.getId()) - + wf = getToolByName(ob, 'portal_workflow', None) + if wf is not None: + wf.notifyCreated(ob) return ob InitializeClass( FactoryTypeInformation ) @@ -492,7 +499,7 @@ of its "immediate" view (typically the metadata form). """ if not self.isConstructionAllowed(container): - raise 'Unauthorized' + raise Unauthorized constructor = self.restrictedTraverse( self.constructor_path ) # Rewrap to get into container's context. From andrew@zope.com Thu Jan 10 16:26:53 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:26:53 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_TypesTool.py:1.9 Message-ID: <200201101626.g0AGQrj05574@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv5016/CMFCore/tests Modified Files: test_TypesTool.py Log Message: *Merging bugfixes from 1_2-branch into head. === CMF/CMFCore/tests/test_TypesTool.py 1.8 => 1.9 === self.failIf( ti.isConstructionAllowed( folder ) ) - self.assertRaises( 'Unauthorized', ti.isConstructionAllowed + self.assertRaises( Unauthorized, ti.isConstructionAllowed , folder, raise_exc=1 ) class FTIConstructionTests_w_Roles( unittest.TestCase ): @@ -514,7 +514,7 @@ newSecurityManager( None , UserWithRoles( 'FooViewer' ).__of__( folder ) ) - self.assertRaises( 'Unauthorized', ti.isConstructionAllowed + self.assertRaises( Unauthorized, ti.isConstructionAllowed , folder, raise_exc=1 ) def test_constructInstance_wo_Roles( self ): @@ -524,7 +524,7 @@ newSecurityManager( None , UserWithRoles( 'FooViewer' ).__of__( folder ) ) - self.assertRaises( 'Unauthorized' + self.assertRaises( Unauthorized , ti.constructInstance, folder, 'foo' ) def test_constructInstance( self ): From andrew@zope.com Thu Jan 10 16:26:53 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:26:53 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Document.py:1.42 Link.py:1.16 utils.py:1.10 Message-ID: <200201101626.g0AGQrR05581@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv5016/CMFDefault Modified Files: Document.py Link.py utils.py Log Message: *Merging bugfixes from 1_2-branch into head. === CMF/CMFDefault/Document.py 1.41 => 1.42 === from Globals import DTMLFile, InitializeClass from AccessControl import ClassSecurityInfo, getSecurityManager + from Products.CMFCore.PortalContent import PortalContent -from DublinCore import DefaultDublinCoreImpl -from webdav.Lockable import ResourceLockedError +from Products.CMFCore.PortalContent import NoWL, ResourceLockedError from Products.CMFCore import CMFCorePermissions from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import _format_stx, keywordsplitter @@ -341,8 +341,9 @@ def PUT(self, REQUEST, RESPONSE): """ Handle HTTP (and presumably FTP?) PUT requests """ - self.dav__init(REQUEST, RESPONSE) - self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) + if not NoWL: + self.dav__init(REQUEST, RESPONSE) + self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) body = REQUEST.get('BODY', '') guessedformat = REQUEST.get_header('Content-Type', 'text/plain') ishtml = (guessedformat == 'text/html') or utils.html_headcheck(body) === CMF/CMFDefault/Link.py 1.15 => 1.16 === from Products.CMFCore import CMFCorePermissions -from Products.CMFCore.PortalContent import PortalContent +from Products.CMFCore.PortalContent import PortalContent, NoWL +from Products.CMFCore.PortalContent import ResourceLockedError from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import keywordsplitter @@ -105,6 +106,24 @@ self.title=title self.remote_url=remote_url self.description=description + self.format=self.URL_FORMAT + + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_edit' ) + manage_edit = DTMLFile( 'zmi_editLink', _dtmldir ) + + security.declareProtected( CMFCorePermissions.ModifyPortalContent + , 'manage_editLink' ) + def manage_editLink( self, remote_url, REQUEST=None ): + """ + Update the Link via the ZMI. + """ + self._edit( remote_url ) + if REQUEST is not None: + REQUEST['RESPONSE'].redirect( self.absolute_url() + + '/manage_edit' + + '?manage_tabs_message=Link+updated' + ) security.declareProtected( CMFCorePermissions.ModifyPortalContent , 'manage_edit' ) @@ -133,6 +152,8 @@ self.remote_url = urlparse.urlunparse( tokens ) else: self.remote_url = 'http://' + remote_url + if self.remote_url[-1] == '/': + self.remote_url = self.remote_url[:-1] security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'edit' ) edit = WorkflowAction( _edit ) @@ -153,12 +174,10 @@ security.declarePrivate( '_writeFromPUT' ) def _writeFromPUT( self, body ): - headers = {} headers, body = parseHeadersBody(body, headers) lines = string.split( body, '\n' ) self.edit( lines[0] ) - headers['Format'] = self.URL_FORMAT new_subject = keywordsplitter(headers) headers['Subject'] = new_subject or self.Subject() @@ -167,7 +186,7 @@ if key != 'Format' and not haveheader(key): headers[key] = value - self.editMetadata(title=headers['Title'], + self._editMetadata(title=headers['Title'], subject=headers['Subject'], description=headers['Description'], contributors=headers['Contributors'], @@ -184,11 +203,18 @@ """ Handle HTTP / WebDAV / FTP PUT requests. """ - self.dav__init(REQUEST, RESPONSE) + if not NoWL: + self.dav__init(REQUEST, RESPONSE) + self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) body = REQUEST.get('BODY', '') - self._writeFromPUT( body ) - RESPONSE.setStatus(204) - return RESPONSE + try: + self._writeFromPUT( body ) + RESPONSE.setStatus(204) + return RESPONSE + except ResourceLockedError, msg: + get_transaction().abort() + RESPONSE.setStatus(423) + return RESPONSE security.declareProtected( CMFCorePermissions.View, 'manage_FTPget' ) def manage_FTPget(self): === CMF/CMFDefault/utils.py 1.9 => 1.10 === -def parseHeadersBody( body, headers=None ): +def parseHeadersBody( body, headers=None, rc=re.compile(r'\n|\r\n')): """ Parse any leading 'RFC-822'-ish headers from an uploaded document, returning a dictionary containing the headers @@ -61,7 +61,7 @@ Allow passing initial dictionary as headers. """ # Split the lines apart, taking into account Mac|Unix|Windows endings - lines = re.split(r'[\n\r]+?', body) + lines = rc.split(body) i = 0 if headers is None: @@ -71,9 +71,7 @@ hdrlist = [] for line in lines: - if line and line[-1] == '\r': - line = line[:-1] - if not line: + if not strip(line): break tokens = split( line, ': ' ) if len( tokens ) > 1: From andrew@zope.com Thu Jan 10 16:57:09 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:57:09 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Document.py:1.43 Message-ID: <200201101657.g0AGv9m12762@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv12686/CMFDefault Modified Files: Document.py Log Message: *Fixed merge conflict typos === CMF/CMFDefault/Document.py 1.42 => 1.43 === from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import _format_stx, keywordsplitter +from DefaultDublinCoreImpl import DefaultDublinCoreImpl from utils import parseHeadersBody, formatRFC822Headers from utils import SimpleHTMLParser, bodyfinder, _dtmldir From andrew@zope.com Thu Jan 10 16:57:09 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 11:57:09 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.22 Message-ID: <200201101657.g0AGv9V12760@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv12686/CMFCore Modified Files: ActionsTool.py Log Message: *Fixed merge conflict typos === CMF/CMFCore/ActionsTool.py 1.21 => 1.22 === $Id$ +""" __version__='$Revision$'[11:-2] From andrew@zope.com Thu Jan 10 17:03:33 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 12:03:33 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - Document.py:1.44 Message-ID: <200201101703.g0AH3X814469@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv14448/CMFDefault Modified Files: Document.py Log Message: *Fixed import typo === CMF/CMFDefault/Document.py 1.43 => 1.44 === from Products.CMFCore.WorkflowCore import WorkflowAction from Products.CMFCore.utils import _format_stx, keywordsplitter -from DefaultDublinCoreImpl import DefaultDublinCoreImpl +from DublinCore import DefaultDublinCoreImpl from utils import parseHeadersBody, formatRFC822Headers from utils import SimpleHTMLParser, bodyfinder, _dtmldir From andrew@zope.com Thu Jan 10 17:07:17 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 12:07:17 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - version.txt:1.5 Message-ID: <200201101707.g0AH7Hj15584@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv15170/CMFDefault Modified Files: version.txt Log Message: *Updated version === CMF/CMFDefault/version.txt 1.4 => 1.5 === +1.2beta From andrew@zope.com Thu Jan 10 17:07:17 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 12:07:17 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic - version.txt:1.5 Message-ID: <200201101707.g0AH7H615586@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic In directory cvs.zope.org:/tmp/cvs-serv15170/CMFTopic Modified Files: version.txt Log Message: *Updated version === CMF/CMFTopic/version.txt 1.4 => 1.5 === +1.2beta From andrew@zope.com Thu Jan 10 17:07:46 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 12:07:46 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCalendar - VERSION.txt:1.3 Message-ID: <200201101707.g0AH7kh15646@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCalendar In directory cvs.zope.org:/tmp/cvs-serv15170/CMFCalendar Modified Files: VERSION.txt Log Message: *Updated version === CMF/CMFCalendar/VERSION.txt 1.2 => 1.3 === +CMFCalendar Product 0.2b From andrew@zope.com Thu Jan 10 17:07:46 2002 From: andrew@zope.com (Andrew Sawyers) Date: Thu, 10 Jan 2002 12:07:46 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - version.txt:1.5 Message-ID: <200201101707.g0AH7kh15649@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv15170/CMFCore Modified Files: version.txt Log Message: *Updated version === CMF/CMFCore/version.txt 1.4 => 1.5 === +1.2beta From seb@jamkit.com Fri Jan 11 14:38:09 2002 From: seb@jamkit.com (seb) Date: Fri, 11 Jan 2002 09:38:09 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCollector - INSTALL.txt:1.8 Message-ID: <200201111438.g0BEc9q15528@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCollector In directory cvs.zope.org:/tmp/cvs-serv15494 Modified Files: INSTALL.txt Log Message: Corrected name of 'Test' tab === CMF/CMFCollector/INSTALL.txt 1.7 => 1.8 === Go to the management screen for the newly added external method and - click the 'Try it' tab. + click the 'Test' tab. The install function will execute and give information about the steps it took to register and install the CMF Events into the CMF From seb@jamkit.com Fri Jan 11 17:15:16 2002 From: seb@jamkit.com (seb) Date: Fri, 11 Jan 2002 12:15:16 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCollector/skins/collector - collector_search.py:1.12 Message-ID: <200201111715.g0BHFGU29579@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCollector/skins/collector In directory cvs.zope.org:/tmp/cvs-serv29572 Modified Files: collector_search.py Log Message: corrected bug whereby 'importances' criterion was being omitted from searches === CMF/CMFCollector/skins/collector/collector_search.py 1.11 => 1.12 === supplement_query("resolution") supplement_query("version_info") +supplement_query("importances", "importance") sr = reqget("security_related", []) if sr: @@ -41,8 +42,8 @@ # and just do token processing according to their names. if i in ['Pending', 'Accepted']: rs.append("%s_confidential" % i) -if rs: - query['status'] = rs + if rs: + query['status'] = rs got = context.get_internal_catalog()(REQUEST=query) return got From shane@cvs.zope.org Fri Jan 11 17:26:37 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Fri, 11 Jan 2002 12:26:37 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/tests - test_Image.py:1.3 Message-ID: <200201111726.g0BHQbk32152@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/tests In directory cvs.zope.org:/tmp/cvs-serv32135 Modified Files: test_Image.py Log Message: Used a more robust way of finding the test image. === CMF/CMFDefault/tests/test_Image.py 1.2 => 1.3 === from Products.CMFDefault.Image import Image +from Products.CMFDefault import tests -TESTS_HOME = os.path.join(INSTANCE_HOME, 'Products/CMFDefault/tests') +TESTS_HOME = tests.__path__[0] TEST_JPG = os.path.join(TESTS_HOME, 'TestImage.jpg') From tseaver@zope.com Sat Jan 12 00:27:15 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 11 Jan 2002 19:27:15 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore/dtml - cpp_defaultWidget.dtml:1.1.2.1 cpp_nameSuffixWidget.dtml:1.1.2.1 cpp_registryPredList.dtml:1.1.2.1 Message-ID: <200201120027.g0C0RFQ02992@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv2968/dtml Added Files: Tag: tseaver-portal_caching-branch cpp_defaultWidget.dtml cpp_nameSuffixWidget.dtml cpp_registryPredList.dtml Log Message: - Initial checkin of caching tool. === Added File Products/CMFCore/dtml/cpp_defaultWidget.dtml === This snippet is designed to be included in a larger method; it provides input elements for a cache policy predicate which matches the suffix of a skin method name. Default predicates have no properties. === Added File Products/CMFCore/dtml/cpp_nameSuffixWidget.dtml === This snippet is designed to be included in a larger method; it provides input elements for a cache policy predicate which matches the suffix of a skin method name. Suffix: Tests suffix of skin method name (blank matches nothing). === Added File Products/CMFCore/dtml/cpp_registryPredList.dtml ===

Edit Predicate List

&dtml-sequence-key;
[&dtml-predType;]:


Add predicate:
From tseaver@zope.com Sat Jan 12 00:27:16 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 11 Jan 2002 19:27:16 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore/interfaces - portal_caching.py:1.1.2.1 Message-ID: <200201120027.g0C0RGZ02993@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore/interfaces In directory cvs.zope.org:/tmp/cvs-serv2968/interfaces Added Files: Tag: tseaver-portal_caching-branch portal_caching.py Log Message: - Initial checkin of caching tool. === Added File Products/CMFCore/interfaces/portal_caching.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (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 # ############################################################################## """ Declare interfaces for tool which maps skin methods to caching policies. """ from Interface import Attribute, Base class CachePolicyPredicate(Base): """ Rule matching a given content object / skin method to a caching policy. """ PREDICATE_TYPE = Attribute() def __call__( content, skin_method_name ): """ Return true if the content object / skin method matches the rule. """ def getTypeLabel(): """ Return a human-readable label for the kind of predicate. """ def edit( **kw ): """ Update the predicate. """ def predicateWidget(): """ Return a snippet of HTML suitable for editing the predicate; the snippet should arrange for values to be marshalled by ZPublisher as a ':record', with the ID of the predicate as the name of the record. The registry will call the predictate's 'edit' method, passing the fields of the record. """ class portal_caching( Base ): """ Registry for rules which map content skins to cache managers. """ def findCacheManager( content, skin_name ): """ Perform a lookup over a collection of rules, returning the the cache manager object corresponding to content / skin_name, or None if no match found. """ From tseaver@zope.com Sat Jan 12 00:27:16 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 11 Jan 2002 19:27:16 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore/tests - test_CachingTool.py:1.1.2.1 test_all.py:1.7.8.1 Message-ID: <200201120027.g0C0RGv02996@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv2968/tests Modified Files: Tag: tseaver-portal_caching-branch test_all.py Added Files: Tag: tseaver-portal_caching-branch test_CachingTool.py Log Message: - Initial checkin of caching tool. === Added File Products/CMFCore/tests/test_CachingTool.py === import unittest class DefaultPredicateTests( unittest.TestCase ): def test_empty( self ): from Products.CMFCore.CachingTool import DefaultPredicate pred = DefaultPredicate( 'empty' ) assert pred( None, 'any_name' ) class NameSuffixPredicateTests( unittest.TestCase ): def _makeOne( self, id ): from Products.CMFCore.CachingTool import NameSuffixPredicate return NameSuffixPredicate( id ) def test_empty( self ): pred = self._makeOne( 'empty' ) assert pred.getSkinNameSuffix() == '' assert not pred( None, 'any_name' ) def test_simple( self ): pred = self._makeOne( 'views' ) pred.edit( skin_name_suffix='_view' ) assert pred.getSkinNameSuffix() == '_view' assert not pred( None, 'foo_bar' ) assert pred( None, 'foo_view' ) class CachingToolRegistryTests( unittest.TestCase ): def _makeOne( self ): from Products.CMFCore.CachingTool import CachingTool return CachingTool() def test_empty( self ): reg = self._makeOne() assert reg.findCacheManagerID( None, 'some_name' ) is None assert not reg.listPredicates() self.assertRaises( KeyError, reg.removePredicate, 'xyzzy' ) def test_reorder( self ): reg = self._makeOne() predIDs = ( 'foo', 'bar', 'baz', 'qux' ) for predID in predIDs: reg.addPredicate( predID, 'name_suffix' ) ids = tuple( map( lambda x: x[0], reg.listPredicates() ) ) assert ids == predIDs reg.reorderPredicate( 'bar', 3 ) ids = tuple( map( lambda x: x[0], reg.listPredicates() ) ) assert ids == ( 'foo', 'baz', 'qux', 'bar' ) def test_lookup( self ): reg = self._makeOne() reg.addPredicate( 'view', 'name_suffix' ) reg.getPredicate( 'view' ).edit( skin_name_suffix='_view' ) reg.assignCacheManagerID( 'view', 'View' ) reg.addPredicate( 'form', 'name_suffix' ) reg.getPredicate( 'form' ).edit( skin_name_suffix='_form' ) reg.assignCacheManagerID( 'form', 'Form' ) self.assertEqual( reg.findCacheManagerID( None, 'foo_view' ), 'View' ) self.assertEqual( reg.findCacheManagerID( None, 'foo_form' ), 'Form' ) self.assertEqual( reg.findCacheManagerID( None, 'some_name' ), None ) reg.addPredicate( 'default', 'default' ) reg.assignCacheManagerID( 'default', 'Default' ) self.assertEqual( reg.findCacheManagerID( None, 'some_name' ) , 'Default' ) def test_suite(): suite = unittest.TestSuite() suite.addTest( unittest.makeSuite( DefaultPredicateTests ) ) suite.addTest( unittest.makeSuite( NameSuffixPredicateTests ) ) suite.addTest( unittest.makeSuite( CachingToolRegistryTests ) ) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() === Products/CMFCore/tests/test_all.py 1.7 => 1.7.8.1 === import unittest +from Products.CMFCore.tests import test_CachingTool from Products.CMFCore.tests import test_ContentTypeRegistry from Products.CMFCore.tests import test_PortalFolder from Products.CMFCore.tests import test_TypesTool @@ -10,6 +11,7 @@ def test_suite(): suite = unittest.TestSuite() + suite.addTest( test_CachingTool.test_suite() ) suite.addTest( test_ContentTypeRegistry.test_suite() ) suite.addTest( test_PortalFolder.test_suite() ) suite.addTest( test_TypesTool.test_suite() ) From tseaver@zope.com Sat Jan 12 00:27:45 2002 From: tseaver@zope.com (Tres Seaver) Date: Fri, 11 Jan 2002 19:27:45 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore - CachingTool.py:1.1.2.1 __init__.py:1.14.12.1 Message-ID: <200201120027.g0C0Rju03008@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore In directory cvs.zope.org:/tmp/cvs-serv2968 Modified Files: Tag: tseaver-portal_caching-branch __init__.py Added Files: Tag: tseaver-portal_caching-branch CachingTool.py Log Message: - Initial checkin of caching tool. === Added File Products/CMFCore/CachingTool.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (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 # ############################################################################## """ Define tool implementing 'portal_caching' interface, plus appropriate predicates and rules. $Id: CachingTool.py,v 1.1.2.1 2002/01/12 00:27:14 tseaver Exp $ """ __version__='$Revision: 1.1.2.1 $'[11:-2] from OFS.SimpleItem import SimpleItem, Item from AccessControl import ClassSecurityInfo from Globals import DTMLFile, InitializeClass, PersistentMapping from ZPublisher.mapply import mapply from CMFCorePermissions import ManagePortal from interfaces.portal_caching import CachePolicyPredicate, portal_caching from utils import _dtmldir import re, os, string, urllib # # Predicate type registry # _predicate_types = [] def registerPredicateType( typeID, klass ): """ Add a new predicate type. """ _predicate_types.append( ( typeID, klass ) ) class DefaultPredicate( SimpleItem ): """ Match anything (put this one last in the list). """ __implements__ = CachePolicyPredicate PREDICATE_TYPE = 'default' security = ClassSecurityInfo() security.declareObjectProtected( ManagePortal ) def __init__( self, id ): self.id = id def __call__( self, content, skin_method_name ): " " return 1 security.declareProtected( ManagePortal, 'getTypeLabel' ) def getTypeLabel( self ): " " return self.PREDICATE_TYPE security.declareProtected( ManagePortal, 'edit' ) def edit( self, **kw ): " " pass security.declareProtected( ManagePortal, 'predicateWidget' ) predicateWidget = DTMLFile( 'cpp_defaultWidget', _dtmldir ) InitializeClass( DefaultPredicate ) registerPredicateType( DefaultPredicate.PREDICATE_TYPE, DefaultPredicate ) class NameSuffixPredicate( SimpleItem ): """ Treat skin name suffix as "view type". """ __implements__ = CachePolicyPredicate PREDICATE_TYPE = 'name_suffix' _skin_name_suffix = None security = ClassSecurityInfo() security.declareObjectProtected( ManagePortal ) def __init__( self, id ): self.id = id def __call__( self, content, skin_method_name ): " " sfx = self._skin_name_suffix return sfx and skin_method_name.endswith( sfx ) security.declareProtected( ManagePortal, 'getTypeLabel' ) def getTypeLabel( self ): " " return self.PREDICATE_TYPE security.declareProtected( ManagePortal, 'edit' ) def edit( self, skin_name_suffix ): " " if skin_name_suffix!= self._skin_name_suffix: self._skin_name_suffix = skin_name_suffix security.declareProtected( ManagePortal, 'predicateWidget' ) predicateWidget = DTMLFile( 'cpp_nameSuffixWidget', _dtmldir ) def getSkinNameSuffix( self ): " " return self._skin_name_suffix or '' InitializeClass( NameSuffixPredicate ) registerPredicateType( NameSuffixPredicate.PREDICATE_TYPE, NameSuffixPredicate ) class CachingTool( SimpleItem ): """ Registry for rules which map content objects / skin names to cache managers. """ meta_type = 'Core Caching Tool' id = 'portal_caching' manage_options = ( { 'label' : 'Predicates' , 'action' : 'manage_predicates' } , #{ 'label' : 'Test' #, 'action' : 'manage_testRegistry' #} ) + SimpleItem.manage_options security = ClassSecurityInfo() def __init__( self ): self._predicate_ids = () self._predicates = PersistentMapping() # # ZMI # security.declarePublic( 'listPredicateTypes' ) def listPredicateTypes( self ): """ """ return map( lambda x: x[0], _predicate_types ) security.declareProtected( ManagePortal, 'manage_predicates' ) manage_predicates = DTMLFile( 'cpp_registryPredList', _dtmldir ) security.declareProtected( ManagePortal, 'doAddPredicate' ) def doAddPredicate( self, predicate_id, predicate_type, REQUEST ): """ """ self.addPredicate( predicate_id, predicate_type ) REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=Predicate+added.' ) security.declareProtected( ManagePortal, 'doUpdatePredicate' ) def doUpdatePredicate( self , predicate_id , predicate , cacheManagerID , REQUEST ): """ """ self.updatePredicate( predicate_id, predicate, cacheManagerID ) REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=Predicate+updated.' ) security.declareProtected( ManagePortal, 'doMovePredicateUp' ) def doMovePredicateUp( self, predicate_id, REQUEST ): """ """ predicate_ids = list( self._predicate_ids ) ndx = predicate_ids.index( predicate_id ) if ndx == 0: msg = "Predicate+already+first." else: self.reorderPredicate( predicate_id, ndx - 1 ) msg = "Predicate+moved." REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=%s' % msg ) security.declareProtected( ManagePortal, 'doMovePredicateDown' ) def doMovePredicateDown( self, predicate_id, REQUEST ): """ """ predicate_ids = list( self._predicate_ids ) ndx = predicate_ids.index( predicate_id ) if ndx == len( predicate_ids ) - 1: msg = "Predicate+already+last." else: self.reorderPredicate( predicate_id, ndx + 1 ) msg = "Predicate+moved." REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=%s' % msg ) security.declareProtected( ManagePortal, 'doRemovePredicate' ) def doRemovePredicate( self, predicate_id, REQUEST ): """ """ self.removePredicate( predicate_id ) REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() + '/manage_predicates' + '?manage_tabs_message=Predicate+removed.' ) # security.declareProtected( ManagePortal, 'manage_testRegistry' ) # manage_testRegistry = DTMLFile( 'registryTest', _dtmldir ) # # security.declareProtected( ManagePortal, 'doTestRegistry' ) # def doTestRegistry( self, name, content_type, body, REQUEST ): # """ # """ # typeName = self.findTypeName( name, content_type, body ) # if typeName is None: # typeName = '' # REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() # + '/manage_testRegistry' # + '?testResults=Type:%s' # % urllib.quote( typeName ) # ) # # Predicate manipulation # security.declarePublic( 'getPredicate' ) def getPredicate( self, predicate_id ): """ Find the predicate whose id is 'id'; return the predicate object, if found, or else None. """ return self._predicates.get( predicate_id, ( None, None ) )[0] security.declarePublic( 'listPredicates' ) def listPredicates( self ): """ Return a sequence of tuples, '( id, ( predicate, cacheManagerID ) )' for all predicates in the registry """ result = [] for predicate_id in self._predicate_ids: result.append( ( predicate_id, self._predicates[ predicate_id ] ) ) return tuple( result ) security.declarePublic( 'getTypeObjectName' ) def getCacheManagerID( self, predicate_id ): """ Find the predicate whose id is 'id'; return the id of the cache manager, if found, or else None. """ return self._predicates.get( predicate_id, ( None, None ) )[1] security.declareProtected( ManagePortal, 'addPredicate' ) def addPredicate( self, predicate_id, predicate_type ): """ Add a predicate to this element of type 'typ' to the registry. """ if predicate_id in self._predicate_ids: raise ValueError, "Existing predicate: %s" % predicate_id klass = None for key, value in _predicate_types: if key == predicate_type: klass = value if klass is None: raise ValueError, "Unknown predicate type: %s" % predicate_type self._predicates[ predicate_id ] = ( klass( predicate_id ), None ) self._predicate_ids = self._predicate_ids + ( predicate_id, ) security.declareProtected( ManagePortal, 'addPredicate' ) def updatePredicate( self, predicate_id, predicate, cacheManagerID ): """ Update a predicate in this element. """ if not predicate_id in self._predicate_ids: raise ValueError, "Unknown predicate: %s" % predicate_id predObj = self._predicates[ predicate_id ][0] mapply( predObj.edit, (), predicate.__dict__ ) self.assignCacheManagerID( predicate_id, cacheManagerID ) security.declareProtected( ManagePortal, 'removePredicate' ) def removePredicate( self, predicate_id ): """ Remove a predicate from the registry. """ del self._predicates[ predicate_id ] idlist = list( self._predicate_ids ) ndx = idlist.index( predicate_id ) idlist = idlist[ :ndx ] + idlist[ ndx+1: ] self._predicate_ids = tuple( idlist ) security.declareProtected( ManagePortal, 'reorderPredicate' ) def reorderPredicate( self, predicate_id, newIndex ): """ Move a given predicate to a new location in the list. """ idlist = list( self._predicate_ids ) ndx = idlist.index( predicate_id ) pred = idlist[ ndx ] idlist = idlist[ :ndx ] + idlist[ ndx+1: ] idlist.insert( newIndex, pred ) self._predicate_ids = tuple( idlist ) security.declareProtected( ManagePortal, 'CacheManagerID' ) def assignCacheManagerID( self, predicate_id, cacheManagerID ): """ Bind the given predicate to a particular cache manager. """ pred, replaced = self._predicates[ predicate_id ] self._predicates[ predicate_id ] = ( pred, cacheManagerID ) # # portal_caching interface # def findCacheManagerID( self, content, skin_name ): """ Perform a lookup over a collection of rules, returning the the cache manager object corresponding to content / skin_name, or None if no match found. """ for predicate_id in self._predicate_ids: pred, cacheManagerID = self._predicates[ predicate_id ] if pred( content, skin_name ): return cacheManagerID return None InitializeClass( CachingTool ) def manage_addCachingTool( self, REQUEST=None ): """ Add a CachingTool to self. """ id = CachingTool.id reg = CachingTool() self._setObject( id, reg ) if REQUEST is not None: REQUEST[ 'RESPONSE' ].redirect( self.absolute_url() + '/manage_main' + '?manage_tabs_message=Caching+tool+added.' ) === Products/CMFCore/__init__.py 1.14 => 1.14.12.1 === import FSZSQLMethod import CookieCrumbler +import CachingTool import ContentTypeRegistry import utils @@ -91,6 +92,12 @@ context.registerClass( ContentTypeRegistry.ContentTypeRegistry, constructors=( ContentTypeRegistry.manage_addRegistry, ), + icon = 'images/registry.gif' + ) + + context.registerClass( + CachingTool.CachingTool, + constructors=( CachingTool.manage_addCachingTool, ), icon = 'images/registry.gif' ) From tseaver@zope.com Mon Jan 14 16:00:49 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 14 Jan 2002 11:00:49 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore/tests - test_ActionProviderBase.py:1.1 test_all.py:1.8 Message-ID: <200201141600.g0EG0nr30174@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv29661/tests Modified Files: test_all.py Added Files: test_ActionProviderBase.py Log Message: - Fix non-persistence of actions borrowed from class. - Add unit tests for ActionProviderBase. === Added File Products/CMFCore/tests/test_ActionProviderBase.py === import unittest class DummyAction: def __init__( self, **kw ): self.__dict__.update( kw ) class ActionProviderBaseTests(unittest.TestCase): def _makeProvider( self ): from Products.CMFCore.ActionProviderBase import ActionProviderBase return ActionProviderBase() def test_addAction( self ): apb = self._makeProvider() self.failIf( apb._actions ) old_actions = apb._actions apb.addAction( id='foo' , name='foo_action' , action='' , condition='' , permission='' , category='' ) self.failUnless( apb._actions ) self.failIf( apb._actions is old_actions ) def test_changeActions( self ): from Products.CMFCore.ActionProviderBase import ActionProviderBase class DummyTool( ActionProviderBase ): _actions = [ DummyAction() , DummyAction() ] apb = DummyTool() old_actions = apb._actions keys = [ ( 'id_%d', None ) , ( 'name_%d', None ) , ( 'action_%d', '' ) , ( 'condition_%d', '' ) , ( 'permission_%d', None ) , ( 'category_%d', None ) , ( 'visible_%d', None ) ] properties = {} for i in range( len( old_actions ) ): for key, value in keys: token = key % i if value is None: value = token properties[ token ] = value apb.changeActions( properties=properties ) marker = [] for i in range( len( apb._actions ) ): for key, value in keys: attr = key[ : -3 ] if value is None: value = key % i if attr == 'name': # WAAAA attr = 'title' if attr == 'action': # WAAAA attr = '_action' if attr == 'permission': # WAAAA attr = 'permissions' value = ( value, ) attr_value = getattr( apb._actions[i], attr, marker ) self.assertEqual( attr_value , value , '%s, %s != %s, %s' % ( attr, attr_value, key, value ) ) self.failIf( apb._actions is old_actions ) def test_deleteActions( self ): apb = self._makeProvider() apb._actions = [ '0', '1', '2' ] # fake out for testing apb.deleteActions( selections=(0,2) ) self.assertEqual( apb._actions, ['1'] ) def test_suite(): suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ActionProviderBaseTests)) return suite def run(): unittest.TextTestRunner().run(test_suite()) if __name__ == '__main__': run() === Products/CMFCore/tests/test_all.py 1.7 => 1.8 === from Products.CMFCore.tests import test_ActionsTool from Products.CMFCore.tests import test_ActionInformation +from Products.CMFCore.tests import test_ActionProviderBase from Products.CMFCore.tests import test_Expression from Products.CMFCore.tests import test_CatalogTool @@ -15,6 +16,7 @@ suite.addTest( test_TypesTool.test_suite() ) suite.addTest( test_ActionsTool.test_suite() ) suite.addTest( test_ActionInformation.test_suite() ) + suite.addTest( test_ActionProviderBase.test_suite() ) suite.addTest( test_Expression.test_suite() ) suite.addTest( test_CatalogTool.test_suite() ) return suite From tseaver@zope.com Mon Jan 14 16:00:49 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 14 Jan 2002 11:00:49 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore - ActionProviderBase.py:1.3 Message-ID: <200201141600.g0EG0nA30173@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore In directory cvs.zope.org:/tmp/cvs-serv29661 Modified Files: ActionProviderBase.py Log Message: - Fix non-persistence of actions borrowed from class. - Add unit tests for ActionProviderBase. === Products/CMFCore/ActionProviderBase.py 1.2 => 1.3 === Adds an action to the list. """ - al = self._actions + al = self._actions[:] if not name: raise ValueError('A name is required.') if action: @@ -128,8 +128,8 @@ """ if properties is None: properties = REQUEST - actions = [] - for idx in range(len(self._actions)): + actions = self._actions[:] + for idx in range(len(actions)): s_idx = str(idx) action = { 'id': str(properties.get('id_' + s_idx, '')), @@ -143,7 +143,7 @@ } if not action['name']: raise ValueError('A name is required.') - a = self._actions[idx] + a = actions[idx] a.id = action['id'] a.title = action['name'] if action['action'] is not '': @@ -157,6 +157,7 @@ a.permissions = action['permissions'] a.category = action['category'] a.visible = action['visible'] + self._actions = actions if REQUEST is not None: return self.manage_editActionsForm(REQUEST, manage_tabs_message= 'Actions changed.') @@ -166,7 +167,7 @@ """ Deletes actions. """ - actions = list(self._actions) + actions = self._actions[:] sels = list(map(int, selections)) # Convert to a list of integers. sels.sort() sels.reverse() From tseaver@zope.com Mon Jan 14 19:21:27 2002 From: tseaver@zope.com (Tres Seaver) Date: Mon, 14 Jan 2002 14:21:27 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.23 Message-ID: <200201141921.g0EJLRH11454@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv11445 Modified Files: ActionsTool.py Log Message: - Pass 'info' to objects which may need it. === CMF/CMFCore/ActionsTool.py 1.22 => 1.23 === }) if hasattr(base, 'listActions'): - a = object.listActions() + a = object.listActions(info) if a: actions.extend(list(a)) From shane@cvs.zope.org Tue Jan 15 17:03:26 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Tue, 15 Jan 2002 12:03:26 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - TypesTool.py:1.29.4.1 Message-ID: <200201151703.g0FH3Qs28402@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv28204 Modified Files: Tag: cmf-pre-1_3-branch TypesTool.py Log Message: This is a different way of providing an extensible types tool. The "scriptable" argument became "add_meta_type" and the list of TypesTool-specific meta types was made into a global. === CMF/CMFCore/TypesTool.py 1.29 => 1.29.4.1 === from Globals import InitializeClass, DTMLFile from utils import UniqueObject, SimpleItemWithProperties, tuplize -from utils import _dtmldir, _checkPermission, cookString -import urllib from utils import _dtmldir, _checkPermission, cookString, getToolByName import string from AccessControl import getSecurityManager, ClassSecurityInfo @@ -39,22 +37,6 @@ _marker = [] # Create a new marker. - -_type_factories = {} -allowedTypes = ( 'Script (Python)' - , 'Python Method' - , 'DTML Method' - , 'External Method' - ) - -def addTypeFactory(factory, id=None): - # modeled after WorkflowTool.addWorkflowFactory() - global allowedTypes - if id is None: - id = getattr(factory, 'id', '') or getattr(factory, 'meta_type', '') - _type_factories[id] = factory - allowedTypes = allowedTypes + (factory.meta_type,) - class TypeInformation (SimpleItemWithProperties): """ Base class for information about a content type. @@ -458,7 +440,7 @@ return ob InitializeClass( FactoryTypeInformation ) -addTypeFactory(FactoryTypeInformation) + class ScriptableTypeInformation( TypeInformation ): """ @@ -513,13 +495,32 @@ return ob InitializeClass( ScriptableTypeInformation ) -addTypeFactory(ScriptableTypeInformation) + # Provide aliases for backward compatibility. ContentFactoryMetadata = FactoryTypeInformation ContentTypeInformation = ScriptableTypeInformation +typeClasses = [ + {'class':FactoryTypeInformation, + 'name':FactoryTypeInformation.meta_type, + 'action':'manage_addFactoryTIForm', + 'permission':'Manage portal'}, + {'class':ScriptableTypeInformation, + 'name':ScriptableTypeInformation.meta_type, + 'action':'manage_addScriptableTIForm', + 'permission':'Manage portal'}, + ] + + +allowedTypes = [ + 'Script (Python)', + 'Python Method', + 'DTML Method', + 'External Method', + ] + class TypesTool( UniqueObject, OFS.Folder.Folder, ActionProviderBase ): """ @@ -553,24 +554,22 @@ return self._actions def all_meta_types(self): + """Adds TypesTool-specific meta types.""" all = TypesTool.inheritedAttribute('all_meta_types')(self) - factypes = [] - for name, fac in _type_factories.items(): - query = urllib.urlencode({'type_type': name}) - factypes.append({ - 'name': fac.meta_type, - 'action': 'manage_addTypeInfoForm?%s' % query, - 'permission': CMFCorePermissions.ManagePortal, - }) - factypes.extend(all) - return factypes + return tuple(typeClasses) + tuple(all) def filtered_meta_types(self, user=None): # Filters the list of available meta types. + allowed = {} + for tc in typeClasses: + allowed[tc['name']] = 1 + for name in allowedTypes: + allowed[name] = 1 + all = TypesTool.inheritedAttribute('filtered_meta_types')(self) meta_types = [] for meta_type in self.all_meta_types(): - if meta_type['name'] in allowedTypes: + if allowed.get(meta_type['name']): meta_types.append(meta_type) return meta_types @@ -598,17 +597,28 @@ _addTIForm = DTMLFile( 'addTypeInfo', _dtmldir ) - security.declareProtected(ManagePortal, 'manage_addTypeInfoForm') - def manage_addTypeInfoForm(self, REQUEST={}, type_type=''): - """ Return the type info form while keeping the list of - prefab type information up to date """ - return self._addTIForm(self, REQUEST, type_type=type_type, - types=self.listDefaultTypeInformation()) + security.declareProtected(ManagePortal, 'manage_addFactoryTIForm') + def manage_addFactoryTIForm(self, REQUEST): + ' ' + return self._addTIForm( + self, REQUEST, + add_meta_type=FactoryTypeInformation.meta_type, + types=self.listDefaultTypeInformation()) + + security.declareProtected(ManagePortal, 'manage_addScriptableTIForm') + def manage_addScriptableTIForm(self, REQUEST): + ' ' + return self._addTIForm( + self, REQUEST, + add_meta_type=ScriptableTypeInformation.meta_type, + types=self.listDefaultTypeInformation()) security.declareProtected(ManagePortal, 'manage_addTypeInformation') - def manage_addTypeInformation(self, id=None, type_type=None, + def manage_addTypeInformation(self, add_meta_type, id=None, typeinfo_name=None, RESPONSE=None): - """ Create a TypeInformation in self. """ + """ + Create a TypeInformation in self. + """ fti = None if typeinfo_name: info = self.listDefaultTypeInformation() @@ -622,11 +632,13 @@ id = fti.get('id', None) if not id: raise 'Bad Request', 'An id is required.' - - if type_type in _type_factories.keys(): - klass = _type_factories[type_type] + for mt in typeClasses: + if mt['name'] == add_meta_type: + klass = mt['class'] + break else: - klass = FactoryTypeInformation + raise ValueError, ( + 'Meta type %s is not a type class.' % add_meta_type) id = str(id) if fti is not None: fti = fti.copy() From shane@cvs.zope.org Tue Jan 15 17:03:26 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Tue, 15 Jan 2002 12:03:26 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/dtml - addTypeInfo.dtml:1.2.10.1 Message-ID: <200201151703.g0FH3QX28403@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/dtml In directory cvs.zope.org:/tmp/cvs-serv28204/dtml Modified Files: Tag: cmf-pre-1_3-branch addTypeInfo.dtml Log Message: This is a different way of providing an extensible types tool. The "scriptable" argument became "add_meta_type" and the list of TypesTool-specific meta types was made into a global. === CMF/CMFCore/dtml/addTypeInfo.dtml 1.2 => 1.2.10.1 === + @@ -10,7 +10,7 @@
- + @@ -38,7 +38,7 @@ @@ -47,7 +47,7 @@ @@ -56,7 +56,7 @@ From andrew@zope.com Fri Jan 18 16:14:00 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 18 Jan 2002 11:14:00 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/zpt_control - synPropertiesForm.pt:1.2.2.1 Message-ID: <200201181614.g0IGE0P20891@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/zpt_control In directory cvs.zope.org:/tmp/cvs-serv20776/CMFDefault/skins/zpt_control Modified Files: Tag: CMF-1_2-branch synPropertiesForm.pt Log Message: *Fixed bugs in SyndicationTool module where methods weren't properly dealing with the case when obj was None. *Fixed zpt skin to properly select the update frequency. === CMF/CMFDefault/skins/zpt_control/synPropertiesForm.pt 1.2 => 1.2.2.1 === tal:define="upd python: portal_syndication.getUpdatePeriod(here)" tal:repeat="item portal_syndication/buildUpdatePeriods" - tal:attributes="value python: item[0]; selected python: item == upd" + tal:attributes="value python: item[0]; selected python: item[0] == upd" tal:content="python: item[1]">Hourly From andrew@zope.com Fri Jan 18 16:14:30 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 18 Jan 2002 11:14:30 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - SyndicationTool.py:1.9.2.2 Message-ID: <200201181614.g0IGEUZ20966@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv20776/CMFDefault Modified Files: Tag: CMF-1_2-branch SyndicationTool.py Log Message: *Fixed bugs in SyndicationTool module where methods weren't properly dealing with the case when obj was None. *Fixed zpt skin to properly select the update frequency. === CMF/CMFDefault/SyndicationTool.py 1.9.2.1 => 1.9.2.2 === if not self.isSiteSyndicationAllowed(): raise 'Syndication is Not Allowed' + if obj is None: + return self.syUpdatePeriod else: syInfo = getattr(obj, 'syndication_information', None) if syInfo is not None: return syInfo.syUpdatePeriod else: - #return self.syUpdatePeriod return 'Syndication is Not Allowed' security.declarePublic('getUpdateFrequency') @@ -252,6 +253,8 @@ """ if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + return self.syUpdateFrequency else: syInfo = getattr(obj, 'syndication_information', None) @@ -276,6 +279,9 @@ #import pdb; pdb.set_trace() if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + when = self.syUpdateBase + return when.ISO() else: syInfo = getattr(obj, 'syndication_information', None) @@ -292,6 +298,9 @@ """ if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + when = syUpdateBase + return when.HTML4() else: syInfo = getattr(obj, 'syndication_information', None) @@ -307,6 +316,8 @@ """ if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + return self.max_items else: syInfo = getattr(obj, 'syndication_information', None) From chrisw@nipltd.com Fri Jan 18 17:31:00 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 18 Jan 2002 12:31:00 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore - DirectoryView.py:1.16.22.1 Message-ID: <200201181731.g0IHV0c07098@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore In directory cvs.zope.org:/tmp/cvs-serv7091 Modified Files: Tag: ChrisW-DirectoryView_fix-branch DirectoryView.py Log Message: Working fix but no tests. === Products/CMFCore/DirectoryView.py 1.16 => 1.16.22.1 === # Ignore version control subdirectories # and special names. +def _filter(name): + return name not in ('CVS', 'SVN', '.', '..') + def _filtered_listdir(path): - n = filter(lambda name: name not in ('CVS', 'SVN', '.', '..'), + n = filter(_filter, listdir(path)) return n +def _walker (listdir, dirname, names): + names[:]=filter(_filter,names) + listdir.extend(names) + class DirectoryInformation: data = None _v_last_read = 0 + _v_last_filelist = [] # Only used on Win32 def __init__(self, expanded_fp, minimal_fp): self.filepath = minimal_fp @@ -86,19 +94,56 @@ types[strip(obname)] = strip(meta_type) return types - def getContents(self, registry): - changed = 0 - if Globals.DevelopmentMode: + if Globals.DevelopmentMode and os.name=='nt': + + def _changed(self): + mtime=0 + filelist=[] + try: + fp = expandpath(self.filepath) + mtime = stat(fp)[8] + # Windows directories don't change mtime when a file + # in them changes :-( + # So keep a list of files as well, and see if that + # changes + path.walk(fp,_walker,filelist) + filelist.sort() + except: + from zLOG import LOG, ERROR + import sys + LOG('DirectoryView', + ERROR, + 'Error checking for directory modification', + error=sys.exc_info()) + + if mtime != self._v_last_read or filelist != self._v_last_filelist: + self._v_last_read = mtime + self._v_last_filelist = filelist + return 1 + + return 0 + + elif Globals.DevelopmentMode: + + def _changed(self): try: mtime = stat(expandpath(self.filepath))[8] except: mtime = 0 if mtime != self._v_last_read: self._v_last_read = mtime - changed = 1 - + return 1 + return 0 + + else: + + def _changed(self): + return 0 + + def getContents(self, registry): + changed = self._changed() if self.data is None or changed: try: self.data, self.objects = self.prepareContents(registry, - register_subdirs=changed) + register_subdirs=fish) except: LOG('DirectoryView', ERROR, From andrew@zope.com Fri Jan 18 18:35:57 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 18 Jan 2002 13:35:57 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/dtml - synProps.dtml:1.3 Message-ID: <200201181835.g0IIZv924307@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/dtml In directory cvs.zope.org:/tmp/cvs-serv23938/CMFDefault/dtml Modified Files: synProps.dtml Log Message: *Merging bug fixes from the 1.2-branch to the head. === CMF/CMFDefault/dtml/synProps.dtml 1.2 => 1.3 === @@ -38,7 +38,7 @@ @@ -47,7 +47,7 @@ @@ -56,7 +56,7 @@ From andrew@zope.com Fri Jan 18 18:35:57 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 18 Jan 2002 13:35:57 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/skins/zpt_control - synPropertiesForm.pt:1.3 Message-ID: <200201181835.g0IIZv324309@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/skins/zpt_control In directory cvs.zope.org:/tmp/cvs-serv23938/CMFDefault/skins/zpt_control Modified Files: synPropertiesForm.pt Log Message: *Merging bug fixes from the 1.2-branch to the head. === CMF/CMFDefault/skins/zpt_control/synPropertiesForm.pt 1.2 => 1.3 === tal:define="upd python: portal_syndication.getUpdatePeriod(here)" tal:repeat="item portal_syndication/buildUpdatePeriods" - tal:attributes="value python: item[0]; selected python: item == upd" + tal:attributes="value python: item[0]; selected python: item[0] == upd" tal:content="python: item[1]">Hourly From andrew@zope.com Fri Jan 18 18:36:26 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 18 Jan 2002 13:36:26 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - SyndicationTool.py:1.12 Message-ID: <200201181836.g0IIaQ724408@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv23938/CMFDefault Modified Files: SyndicationTool.py Log Message: *Merging bug fixes from the 1.2-branch to the head. === CMF/CMFDefault/SyndicationTool.py 1.11 => 1.12 === if not self.isSiteSyndicationAllowed(): raise 'Syndication is Not Allowed' + if obj is None: + return self.syUpdatePeriod else: syInfo = getattr(obj, 'syndication_information', None) if syInfo is not None: return syInfo.syUpdatePeriod else: - #return self.syUpdatePeriod return 'Syndication is Not Allowed' security.declarePublic('getUpdateFrequency') @@ -274,6 +275,8 @@ """ if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + return self.syUpdateFrequency else: syInfo = getattr(obj, 'syndication_information', None) @@ -298,6 +301,9 @@ #import pdb; pdb.set_trace() if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + when = self.syUpdateBase + return when.ISO() else: syInfo = getattr(obj, 'syndication_information', None) @@ -314,6 +320,9 @@ """ if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + when = syUpdateBase + return when.HTML4() else: syInfo = getattr(obj, 'syndication_information', None) @@ -329,6 +338,8 @@ """ if not self.isSiteSyndicationAllowed(): raise 'Syndication is not Allowed' + if obj is None: + return self.max_items else: syInfo = getattr(obj, 'syndication_information', None) From andrew@zope.com Mon Jan 21 16:24:39 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 21 Jan 2002 11:24:39 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.23 HISTORY.txt:1.31.4.3 Message-ID: <200201211624.g0LGOdX04696@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv4634 Modified Files: Tag: CMF-1_2-branch CHANGES.txt HISTORY.txt Log Message: *Fixed the addition of changelog information into the HISTORY log and put the information in the proper files. === CMF/CHANGES.txt 1.36.2.22 => 1.36.2.23 === + + Somewhere over the rainbow + + - (__future__) Exposed role management for manager users on + the default roster: managers can now "promote" members + without going to the ZMI. + + - (__future__) Added "custom schema" mechanism for content + objetcts: site managers can now define additional + propertysheets for a content type, which will then be + reflected in instances created from the type. + + - (__future__) Added simple link extraction / checking for + textual content. Link checking also works for Link objects. + + - (__future__) Added "composite content" types (see the + "dogbowl proposal", + (http://cmf.zope.org/rqmts/proposals/compounds/compoundproposal.txt). + + - (__future__) Added DCWorkflow (through-the-web configurable + workflows) to the core set of CMF products. + + +CMF1.2 Features Added @@ -11,6 +34,92 @@ - Added ZMI interface for editing Link URL (Tracker #364). + - Added docs from the crack ZC docs guys; these docs live in + the top-level 'docs' directory. + + - Merged CMFDecor product's artifacts into CMFCore / + CMFDefault; theses aretifacts allow use of filesystem-based + Zope Page Templates as skins. + + Note that the CMFDecor skin is the one which will be + receiving all our development focus: we will fix bugs in the + DTML skins, but are not likely to invest significant effort + in upgrading it. + + - Hooked 'manage_addFolder' to allow creation of PortalFolders + from both WebDAV, FTP, and ZMI. + + - Improved tracebacks from broken FSDTMLMethods, which no longer + indicate that every problem is in RestrictedDTML. + + - Made it possible to add CookieCrumblers in nested folders. + You can just drop in a cookie crumbler anywhere to change the + login form for that area of the site. In fact, now you don't + have to create a user folder just to change the login + process. + + - Made Link objects editable via FTP / WebDAV. + + - Merged Chris Withers' FSSQLMethod into CMFCore. + + - Added documentation for installing from CVS. + + - Moved permission checking inside personalize_form to make + sure Anonymous cannot access it without logging in (CMF Tracker + Issue 349, thanks go to Bill Anderson). + + - Added initial CMF use cases as FSSTXMethods in CMFDefault/help. + + - Made validation methods of 'portal_metadata' available to + scripts. + + - Made skinned 'index_html' reflect generic view on folder + content, rather than simple title/description of the portal. + + - Added "Change and View" submit button to content editing + forms; added check for this button to POST handlers in CMFDefault, + and indirected redirect targets in those methods through + 'getActionByID'. + + - Added knob for skin cookie persistence to SkinsTool's + "properties" tab. The default policy (unchanged) is that + skin cookies expire at the end of the browser session. if + Skin Cookie Persistence is checked the cookie will last a + full yesr. + + - Added an API to the 'portal_actions' interface for querying, + adding, and removing action providers. + + - Added a "multi-review" form, enabling a reviewer to publish + or reject multiple items at once, using a common comment. + + - Added ZMI tab to DirectoryView to allow re-basing the + filesystem path. + + - Added "breadcrumbs" to CMFDecor skins. + + - Added initial support for WebDAV locaking to PortalContent. + + - Added SortCriterion to list of criterion types for Topics, + to permit sorting of results. + + - Added "Local Roles" action to folders to ease collaboration. + + - Add scarecrow assertions for the CMF-centric interfaces, and + made the actual interfaces compatible with the standard + Zope Interface package. + + - Made FSSTXMethod display skinnable, and added ZPT version. + + - Added 'visible' attribute to TypeInformation actions, to + permit indirection (via 'getActionById') without exposing the + action in the CMF UI. + + - Extended MetadataTool to allow adding / removing element specs + (i.e., it can now manage policies for "custom" schemas, as + well as Dublin Core). + + Bugs Fixed - Added .html and .htm files to be loaded from the filesystem as @@ -79,3 +188,204 @@ - Made 'CMFCore/interfaces/__init__.py' non-empty, to remove suspicion that the file was corrupted in the download (Tracker #426). + + - Refactored content and metadata editing methods of + DefaultDublinCoreImpl, Document, and NewsItem to disentangle + the excessive coupling. Each "path" for editing now has a + "presentation"-level method, which directs traffic and + reindexes the object; the underlying methods are much + simplified. + + - Fixed inner / named links in Document / News Item (thanks to + Kenichi Sato for the patch!) + + - Ensured that editing methods handle WebDAV locks correctly, + using a new 'failIfLocked' assertion. + + - Added cookString method to CMFCore.utils for taking a string + and making it id friendly, it also does a string.lower on the + resultant regex. Changed TypesTool to utilize cookString to + ensure that action ids are properly formated if the name is + being used as the id. + + - Added 'getReply' to CMFDefault.DiscussionItem.DiscussionItemContainer, + to permit access to an individual reply without needing to + do traversal. + + - Corrected pass-through of 'file' and 'seatbelt' arguments in + new 'CMFDefault.Document.edit' method; also sync'ed ZMI edit + method for documents with standard protocol (Tracker #417). + + - Added cookString method to CMFCore.utils for taking a string and + making it id friendly, it also does a string.lower on the resultant + regex. + + - Made examples in INSTALL.txt less terse, and added notes on + Windows-specific issues (thanks to Johan Mukhsein for the + suggestions). + + - Made error message generated by FSPropertiesObject capture the + offending line and line #; also, added logic to allow blank + lines and comment lines beginning with '#' (tracker #338). + + - Added fixup to Link objects for user-entered URLs which don't + supply scheme: for example, fix up 'www.zope.com' to + 'http://www.zope.com'. (tracker #361) + + - Updated CMFCore.CatalogTool to allow new, optional 'idxs' + argument to 'catalog_object' (tracker #405). + + - Added a workaround for the problem where the CookieCrumber + set cookies even though the user entered an incorrect password. + RESPONSE.unauthorized() now cancels the cookie response + header. The new login_form and logged_in form both try to + invoke unauthorized(), so make sure you install the new + forms. + + - Implement the notional 'search results item' interface for + SkinnedFolder. + + - Corrected solecism in Topic (use of 'criteria' for singular); + removed the need to know about the funky generated IDs for + criterion objects. Fixed derived bug in skins (tracker #408). + + - Modified error-logging code to avoid potential leaks of + traceback frame. + + - Made Document's 'manage_FTPget' use 'EditableBody', rather than + accessing 'text' attribute directly (improves reusability). + Likewise 'Document.SearchableText'. + + - Merged Seb Bacon's refactoring of 'getDefaultView' into + 'CMFCore.utils._getView'; clients can now specify a view by name, + as well. + + - Made the default content type for Image 'image/png', instead + of the unintuitive 'text/html' inherited from DefaultDublinCoreImpl + (tracker #384). + + - Corrected typo in ActionsTool which broke actions for the root + portal object (tracker #379). + + - Updated the MemberDataTool to use an OOBTree, instead of the + old-style BTree, to store member data wrappers (CMF Tracker 375). + + - Corrected 'personalize_form' to use 'getProperty' where feasible, + rather than relying on direct attribute access (tracker #372). + + - Removed an incompatibility with LoginManager in + CMFCore.MembershipTool (tracker #365). + + - Removed an infinite loop condition that arises when + MembershipTool.createMemberArea gets called inside wrapUser + (this could only happen if "Create Member Area" was checked + in the Membership tool.) + + - Added new 'TitleOrId' skin method, and updated skins to + depend on it rather than SimpleItem.title_or_id. + + - Made unit tests runnable without reliance on 'zctl test'. + + - Corrected initial column set in catalog to include + "ModificationDate" instead of "ModifiedDate". + + - Ensured that object is recatalogued (e.g., after setting + 'portal_type'; thanks to Florent Guillaume). + + - Removed silly dependency of 'CatalogTool.searchResults' on + REQUEST (catalog already does the Right Thing (tm) when no + REQUEST is available). Note that this requires updates to + the 'search' skins, as they didn't pass it in. + + - Changed redirect target after rejecting an item to the search + page for pending content items; this resolves the problem + that the non-Manager reviewr who rejects an item no longer + has View permission on it, and therefore gets an Unauthorized + when redirecting to the object's view action. + + - Moved generation of the "Add to Favorites" and "My Favorites" + links from the CMFCore/ActionsTool into the + CMFDefault/MembershipTool, which is a more logical location + for it because it relies on information from the membership tool. + + - Made Topic directly publishable (like PortalContent), using its + 'view' action (or the first action found, if view is not present). + + - Set title for newly-created member folders; fixes + breadcrumbs that expect a title on the object. + + - Allow URLs with query strings in StructuredText links. + + - Update 'news_box' to search based on 'Type', rather than + 'meta_type'. + + - Fix 'SkinnedFolder.Creator()' to call 'getOwner()' with + info argument. + + - Made CookieCrumbler check for 'WEBDAV_SOURCE_PORT' + environment variable (supplied by Zope 2.4.1+), and bail on + intercepting authentication if found. + + - Included 'Owner' in list of significant roles returned by + 'MembershipTool.getPortalRoles' (e.g., so that the + "Local Roles" interface can allow assignment of it). + + - Allow users with local role of "Reviewer" to see the "pending + review" action. + + - Made TypesTool, rather than individual type objects, + responsible for generating "immediate view" URL; type + objects now return the newly-created object, which makes + scripting them much simpler. + + - Remove fossilized reference to 'getSecurityManager' from + 'PortalContent._verifyActionPermissions'. + + - Modified the redirect after "Add to Favorites" to us the + view action, rather than 'view'. + + - Fixed 'Document.guessFormat()' to use + 'utils.html_headcheck()' instead of 'bodyfinder' regex to + detect structured text versus html; avoids recursion limit + for large HTML files. + + - Removed spurious 'afterCreate' protocol for content objects. + + - Added mapping of 'css' file extension to FSDTMLMethods. + + - Modifed PortalFolder.listFolderContents to handle + permission-based filtering; duplicates what skip_unauthorized + is doing in DocumentTemplate/DT_IN.py (but works for ZPT as + well). + + - Modified CMFDefault.RegistrationTool.addMember to avoid + flunking validation if properties are not passed (Tracker + #335). + + - Applied patch from Chris Withers to 'register' skin method; + the patch which avoids quoting problems for the error message + if a problem occurs (Tracker #339). + + - Added 'DiscussionItem.replyCount' (Tracker #328). + 'DiscussionItem.hasReplies' now returns only a boolean value. + Standard skins don't call 'replyCount' due to performance + concerns. + + - Factored content filtering logic into a Python Script. + + - Improved handling of multiple rename targets (thanks to Chris + Withers for the patch.) + + - Completed conversion of form targets from DTML Methods to + Python Scripts. + + - Improved compatibility with Zope 2.4: + + o support for new "restricted execution" mode; + + o support for new catalog initialization API. + + o updated 'test_all' unit test drivers to use standard + 'unittest' module from Python 2.1 (it no longer has + 'JUnitTestTextRunner' class). + === CMF/HISTORY.txt 1.31.4.2 => 1.31.4.3 === - - New features - - - Added docs from the crack ZC docs guys; these docs live in - the top-level 'docs' directory. - - - Merged CMFDecor product's artifacts into CMFCore / - CMFDefault; theses aretifacts allow use of filesystem-based - Zope Page Templates as skins. - - Note that the CMFDecor skin is the one which will be - receiving all our development focus: we will fix bugs in the - DTML skins, but are not likely to invest significant effort - in upgrading it. - - - Hooked 'manage_addFolder' to allow creation of PortalFolders - from both WebDAV, FTP, and ZMI. - - - Improved tracebacks from broken FSDTMLMethods, which no longer - indicate that every problem is in RestrictedDTML. - - - Made it possible to add CookieCrumblers in nested folders. - You can just drop in a cookie crumbler anywhere to change the - login form for that area of the site. In fact, now you don't - have to create a user folder just to change the login - process. - - - Made Link objects editable via FTP / WebDAV. - - - Merged Chris Withers' FSSQLMethod into CMFCore. - - - Added documentation for installing from CVS. - - - Moved permission checking inside personalize_form to make - sure Anonymous cannot access it without logging in (CMF Tracker - Issue 349, thanks go to Bill Anderson). - - - Added initial CMF use cases as FSSTXMethods in CMFDefault/help. - - - Made validation methods of 'portal_metadata' available to - scripts. - - - Made skinned 'index_html' reflect generic view on folder - content, rather than simple title/description of the portal. - - - Added "Change and View" submit button to content editing - forms; added check for this button to POST handlers in CMFDefault, - and indirected redirect targets in those methods through - 'getActionByID'. - - - Added knob for skin cookie persistence to SkinsTool's - "properties" tab. The default policy (unchanged) is that - skin cookies expire at the end of the browser session. if - Skin Cookie Persistence is checked the cookie will last a - full yesr. - - - Added an API to the 'portal_actions' interface for querying, - adding, and removing action providers. - - - Added a "multi-review" form, enabling a reviewer to publish - or reject multiple items at once, using a common comment. - - - Added ZMI tab to DirectoryView to allow re-basing the - filesystem path. - - - Added "breadcrumbs" to CMFDecor skins. - - - Added initial support for WebDAV locaking to PortalContent. - - - Added SortCriterion to list of criterion types for Topics, - to permit sorting of results. - - - Added "Local Roles" action to folders to ease collaboration. - - - Add scarecrow assertions for the CMF-centric interfaces, and - made the actual interfaces compatible with the standard - Zope Interface package. - - - Made FSSTXMethod display skinnable, and added ZPT version. - - - Added 'visible' attribute to TypeInformation actions, to - permit indirection (via 'getActionById') without exposing the - action in the CMF UI. - - - Extended MetadataTool to allow adding / removing element specs - (i.e., it can now manage policies for "custom" schemas, as - well as Dublin Core). - - Bug fixes - - - Refactored content and metadata editing methods of - DefaultDublinCoreImpl, Document, and NewsItem to disentangle - the excessive coupling. Each "path" for editing now has a - "presentation"-level method, which directs traffic and - reindexes the object; the underlying methods are much - simplified. - - - Fixed inner / named links in Document / News Item (thanks to - Kenichi Sato for the patch!) - - - Ensured that editing methods handle WebDAV locks correctly, - using a new 'failIfLocked' assertion. - - - Added cookString method to CMFCore.utils for taking a string - and making it id friendly, it also does a string.lower on the - resultant regex. Changed TypesTool to utilize cookString to - ensure that action ids are properly formated if the name is - being used as the id. - - - Added 'getReply' to CMFDefault.DiscussionItem.DiscussionItemContainer, - to permit access to an individual reply without needing to - do traversal. - - - Corrected pass-through of 'file' and 'seatbelt' arguments in - new 'CMFDefault.Document.edit' method; also sync'ed ZMI edit - method for documents with standard protocol (Tracker #417). - - - Added cookString method to CMFCore.utils for taking a string and - making it id friendly, it also does a string.lower on the resultant - regex. - - - Made examples in INSTALL.txt less terse, and added notes on - Windows-specific issues (thanks to Johan Mukhsein for the - suggestions). - - - Made error message generated by FSPropertiesObject capture the - offending line and line #; also, added logic to allow blank - lines and comment lines beginning with '#' (tracker #338). - - - Added fixup to Link objects for user-entered URLs which don't - supply scheme: for example, fix up 'www.zope.com' to - 'http://www.zope.com'. (tracker #361) - - - Updated CMFCore.CatalogTool to allow new, optional 'idxs' - argument to 'catalog_object' (tracker #405). - - - Added a workaround for the problem where the CookieCrumber - set cookies even though the user entered an incorrect password. - RESPONSE.unauthorized() now cancels the cookie response - header. The new login_form and logged_in form both try to - invoke unauthorized(), so make sure you install the new - forms. - - - Implement the notional 'search results item' interface for - SkinnedFolder. - - - Corrected solecism in Topic (use of 'criteria' for singular); - removed the need to know about the funky generated IDs for - criterion objects. Fixed derived bug in skins (tracker #408). - - - Modified error-logging code to avoid potential leaks of - traceback frame. - - - Made Document's 'manage_FTPget' use 'EditableBody', rather than - accessing 'text' attribute directly (improves reusability). - Likewise 'Document.SearchableText'. - - - Merged Seb Bacon's refactoring of 'getDefaultView' into - 'CMFCore.utils._getView'; clients can now specify a view by name, - as well. - - - Made the default content type for Image 'image/png', instead - of the unintuitive 'text/html' inherited from DefaultDublinCoreImpl - (tracker #384). - - - Corrected typo in ActionsTool which broke actions for the root - portal object (tracker #379). - - - Updated the MemberDataTool to use an OOBTree, instead of the - old-style BTree, to store member data wrappers (CMF Tracker 375). - - - Corrected 'personalize_form' to use 'getProperty' where feasible, - rather than relying on direct attribute access (tracker #372). - - - Removed an incompatibility with LoginManager in - CMFCore.MembershipTool (tracker #365). - - - Removed an infinite loop condition that arises when - MembershipTool.createMemberArea gets called inside wrapUser - (this could only happen if "Create Member Area" was checked - in the Membership tool.) - - - Added new 'TitleOrId' skin method, and updated skins to - depend on it rather than SimpleItem.title_or_id. - - - Made unit tests runnable without reliance on 'zctl test'. - - - Corrected initial column set in catalog to include - "ModificationDate" instead of "ModifiedDate". - - - Ensured that object is recatalogued (e.g., after setting - 'portal_type'; thanks to Florent Guillaume). - - - Removed silly dependency of 'CatalogTool.searchResults' on - REQUEST (catalog already does the Right Thing (tm) when no - REQUEST is available). Note that this requires updates to - the 'search' skins, as they didn't pass it in. - - - Changed redirect target after rejecting an item to the search - page for pending content items; this resolves the problem - that the non-Manager reviewr who rejects an item no longer - has View permission on it, and therefore gets an Unauthorized - when redirecting to the object's view action. - - - Moved generation of the "Add to Favorites" and "My Favorites" - links from the CMFCore/ActionsTool into the - CMFDefault/MembershipTool, which is a more logical location - for it because it relies on information from the membership tool. - - - Made Topic directly publishable (like PortalContent), using its - 'view' action (or the first action found, if view is not present). - - - Set title for newly-created member folders; fixes - breadcrumbs that expect a title on the object. - - - Allow URLs with query strings in StructuredText links. - - - Update 'news_box' to search based on 'Type', rather than - 'meta_type'. - - - Fix 'SkinnedFolder.Creator()' to call 'getOwner()' with - info argument. - - - Made CookieCrumbler check for 'WEBDAV_SOURCE_PORT' - environment variable (supplied by Zope 2.4.1+), and bail on - intercepting authentication if found. - - - Included 'Owner' in list of significant roles returned by - 'MembershipTool.getPortalRoles' (e.g., so that the - "Local Roles" interface can allow assignment of it). - - - Allow users with local role of "Reviewer" to see the "pending - review" action. - - - Made TypesTool, rather than individual type objects, - responsible for generating "immediate view" URL; type - objects now return the newly-created object, which makes - scripting them much simpler. - - - Remove fossilized reference to 'getSecurityManager' from - 'PortalContent._verifyActionPermissions'. - - - Modified the redirect after "Add to Favorites" to us the - view action, rather than 'view'. - - - Fixed 'Document.guessFormat()' to use - 'utils.html_headcheck()' instead of 'bodyfinder' regex to - detect structured text versus html; avoids recursion limit - for large HTML files. - - - Removed spurious 'afterCreate' protocol for content objects. - - - Added mapping of 'css' file extension to FSDTMLMethods. - - - Modifed PortalFolder.listFolderContents to handle - permission-based filtering; duplicates what skip_unauthorized - is doing in DocumentTemplate/DT_IN.py (but works for ZPT as - well). - - - Modified CMFDefault.RegistrationTool.addMember to avoid - flunking validation if properties are not passed (Tracker - #335). - - - Applied patch from Chris Withers to 'register' skin method; - the patch which avoids quoting problems for the error message - if a problem occurs (Tracker #339). - - - Added 'DiscussionItem.replyCount' (Tracker #328). - 'DiscussionItem.hasReplies' now returns only a boolean value. - Standard skins don't call 'replyCount' due to performance - concerns. - - - Factored content filtering logic into a Python Script. - - - Improved handling of multiple rename targets (thanks to Chris - Withers for the patch.) - - - Completed conversion of form targets from DTML Methods to - Python Scripts. - - - Improved compatibility with Zope 2.4: - - o support for new "restricted execution" mode; - - o support for new catalog initialization API. - - o updated 'test_all' unit test drivers to use standard - 'unittest' module from Python 2.1 (it no longer has - 'JUnitTestTextRunner' class). - 1.1 final (2001/06/20) New features From andrew@zope.com Mon Jan 21 20:23:20 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 21 Jan 2002 15:23:20 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.24 Message-ID: <200201212023.g0LKNKT30525@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv30461 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *Fixed regular expression bug in _ALLOWED_MEMBER_ID_PATTERN *Tracker #456 === CMF/CHANGES.txt 1.36.2.23 => 1.36.2.24 === Bugs Fixed + - Fixed a bug in the regular expression check for valid user id's in + CMFCore.RegistrationTool._ALLOWED_MEMBER_ID_PATTERN; user id's with + anything other then what matches the regular expression will be allowed. + (Tracker #456) + - Added .html and .htm files to be loaded from the filesystem as FSPageTemplates. From andrew@zope.com Mon Jan 21 20:23:20 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 21 Jan 2002 15:23:20 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - RegistrationTool.py:1.7.2.2 Message-ID: <200201212023.g0LKNK030526@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv30461/CMFCore Modified Files: Tag: CMF-1_2-branch RegistrationTool.py Log Message: *Fixed regular expression bug in _ALLOWED_MEMBER_ID_PATTERN *Tracker #456 === CMF/CMFCore/RegistrationTool.py 1.7.2.1 => 1.7.2.2 === import re - __ALLOWED_MEMBER_ID_PATTERN = re.compile( "[A-Za-z][A-Za-z0-9_]*" ) + __ALLOWED_MEMBER_ID_PATTERN = re.compile( "^[A-Za-z][A-Za-z0-9_]*$" ) security.declareProtected(AddPortalMember, 'isMemberIdAllowed') def isMemberIdAllowed(self, id): '''Returns 1 if the ID is not in use and is not reserved. From andrew@zope.com Mon Jan 21 20:24:49 2002 From: andrew@zope.com (Andrew Sawyers) Date: Mon, 21 Jan 2002 15:24:49 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - RegistrationTool.py:1.10 Message-ID: <200201212024.g0LKOnB31082@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv31067 Modified Files: RegistrationTool.py Log Message: *Merging fix from CMF-1_2-branch for Tracker #456 bug fix. === CMF/CMFCore/RegistrationTool.py 1.9 => 1.10 === import re - __ALLOWED_MEMBER_ID_PATTERN = re.compile( "[A-Za-z][A-Za-z0-9_]*" ) + __ALLOWED_MEMBER_ID_PATTERN = re.compile( "^[A-Za-z][A-Za-z0-9_]*$" ) security.declareProtected(AddPortalMember, 'isMemberIdAllowed') def isMemberIdAllowed(self, id): '''Returns 1 if the ID is not in use and is not reserved. From shane@cvs.zope.org Wed Jan 23 20:16:24 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Wed, 23 Jan 2002 15:16:24 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - WorkflowTool.py:1.20.10.1 Message-ID: <200201232016.g0NKGOL03854@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv3794 Modified Files: Tag: cmf-pre-1_3-branch WorkflowTool.py Log Message: We no longer need auto-migration of workflow tools. === CMF/CMFCore/WorkflowTool.py 1.20 => 1.20.10.1 === from string import join, split, replace, strip -AUTO_MIGRATE_WORKFLOW_TOOLS = 1 # This will later be set to 0. +AUTO_MIGRATE_WORKFLOW_TOOLS = 0 # Set to 1 to auto-migrate _marker = [] # Create a new marker object. From shane@cvs.zope.org Wed Jan 23 21:17:56 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Wed, 23 Jan 2002 16:17:56 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - CMFCatalogAware.py:1.1.2.1 PortalContent.py:1.33.20.1 Message-ID: <200201232117.g0NLHu622668@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv22054 Modified Files: Tag: cmf-pre-1_3-branch PortalContent.py Added Files: Tag: cmf-pre-1_3-branch CMFCatalogAware.py Log Message: Split the catalog aware functionality of PortalContent into a separate base class so SkinnedFolder can make use of it. === Added File CMF/CMFCore/CMFCatalogAware.py === ############################################################################## # # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## import Globals from Acquisition import aq_base from AccessControl import ClassSecurityInfo from CMFCorePermissions import ModifyPortalContent from utils import getToolByName class CMFCatalogAware: """Mix-in for notifying portal_catalog and portal_workflow """ security = ClassSecurityInfo() # Cataloging methods # ------------------ security.declareProtected(ModifyPortalContent, 'indexObject') def indexObject(self): catalog = getToolByName(self, 'portal_catalog', None) if catalog is not None: catalog.indexObject(self) security.declareProtected(ModifyPortalContent, 'unindexObject') def unindexObject(self): catalog = getToolByName(self, 'portal_catalog', None) if catalog is not None: catalog.unindexObject(self) security.declareProtected(ModifyPortalContent, 'reindexObject') def reindexObject(self): catalog = getToolByName(self, 'portal_catalog', None) if catalog is not None: catalog.reindexObject(self) def manage_afterAdd(self, item, container): """ Add self to the workflow and catalog. """ # # Are we being added (or moved)? # if aq_base(container) is not aq_base(self): wf = getToolByName(self, 'portal_workflow', None) if wf is not None: wf.notifyCreated(self) self.indexObject() def manage_beforeDelete(self, item, container): """ Remove self from the catalog. """ # # Are we going away? # if aq_base(container) is not aq_base(self): self.unindexObject() # # Now let our "aspects" know we are going away. # for item_id, subitem in self.objectItems(): m = getattr(subitem, 'manage_beforeDelete', None) if m is not None: m(item, container) Globals.InitializeClass(CMFCatalogAware) === CMF/CMFCore/PortalContent.py 1.33 => 1.33.20.1 === from CMFCorePermissions import AccessContentsInformation, View, FTPAccess -from CMFCorePermissions import ReviewPortalContent, ModifyPortalContent from interfaces.Contentish import Contentish from DynamicType import DynamicType -from utils import getToolByName, _checkPermission, _getViewFor +from utils import _checkPermission, _getViewFor + +from CMFCatalogAware import CMFCatalogAware try: from webdav.Lockable import ResourceLockedError @@ -43,7 +44,7 @@ NoWL = 1 -class PortalContent(DynamicType, SimpleItem): +class PortalContent(DynamicType, CMFCatalogAware, SimpleItem): """ Base class for portal objects. @@ -100,58 +101,6 @@ "Returns a concatination of all searchable text" # Should be overriden by portal objects return "%s %s" % (self.Title(), self.Description()) - - # Cataloging methods - # ------------------ - - security.declareProtected(ModifyPortalContent, 'indexObject') - def indexObject(self): - catalog = getToolByName(self, 'portal_catalog', None) - if catalog is not None: - catalog.indexObject(self) - - security.declareProtected(ModifyPortalContent, 'unindexObject') - def unindexObject(self): - catalog = getToolByName(self, 'portal_catalog', None) - if catalog is not None: - catalog.unindexObject(self) - - security.declareProtected(ModifyPortalContent, 'reindexObject') - def reindexObject(self): - catalog = getToolByName(self, 'portal_catalog', None) - if catalog is not None: - catalog.reindexObject(self) - - def manage_afterAdd(self, item, container): - """ - Add self to the workflow and catalog. - """ - # - # Are we being added (or moved)? - # - if aq_base(container) is not aq_base(self): - wf = getToolByName(self, 'portal_workflow', None) - if wf is not None: - wf.notifyCreated(self) - self.indexObject() - - def manage_beforeDelete(self, item, container): - """ - Remove self from the catalog. - """ - # - # Are we going away? - # - if aq_base(container) is not aq_base(self): - self.unindexObject() - # - # Now let our "aspects" know we are going away. - # - for it, subitem in self.objectItems(): - si_m_bD = getattr( subitem, 'manage_beforeDelete', None ) - if si_m_bD is not None: - si_m_bD( item, container ) - # Contentish interface methods # ---------------------------- From shane@cvs.zope.org Wed Jan 23 21:19:15 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Wed, 23 Jan 2002 16:19:15 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - SkinnedFolder.py:1.6.26.1 Message-ID: <200201232119.g0NLJFU25183@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv24081 Modified Files: Tag: cmf-pre-1_3-branch SkinnedFolder.py Log Message: SkinnedFolder is now catalog aware, which allows it to be used as searchable content. === CMF/CMFDefault/SkinnedFolder.py 1.6 => 1.6.26.1 === from ComputedAttribute import ComputedAttribute from Products.CMFCore.utils import _getViewFor +from Products.CMFCore.CMFCatalogAware import CMFCatalogAware from Acquisition import aq_base factory_type_information = ( { 'id' : 'Skinned Folder' @@ -66,7 +67,8 @@ , ) -class SkinnedFolder( PortalFolder ): + +class SkinnedFolder(CMFCatalogAware, PortalFolder): """ """ meta_type = 'Skinned Folder' From shane@cvs.zope.org Wed Jan 23 23:31:42 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Wed, 23 Jan 2002 18:31:42 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - FSObject.py:1.8.24.1 FSPageTemplate.py:1.3.20.1 FSPythonScript.py:1.14.24.1 Message-ID: <200201232331.g0NNVgJ07166@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv6554 Modified Files: Tag: cmf-pre-1_3-branch FSObject.py FSPageTemplate.py FSPythonScript.py Log Message: Delay parsing of FSPythonScripts and FSPageTemplates. It's very hard to get this right because of the lack of interfaces, but it seems to work for now. This makes CMF-enabled Zope sites start quickly again. === CMF/CMFCore/FSObject.py 1.8 => 1.8.24.1 === _file_mod_time = 0 + _parsed = 0 def __init__(self, id, filepath, fullname=None, properties=None): if properties: @@ -92,11 +93,13 @@ # Refresh our contents from the filesystem if that is newer and we are # running in debug mode. def _updateFromFS(self): - if Globals.DevelopmentMode: + parsed = self._parsed + if not parsed or Globals.DevelopmentMode: fp = expandpath(self._filepath) try: mtime=stat(fp)[8] except: mtime=0 - if mtime != self._file_mod_time: + if not parsed or mtime != self._file_mod_time: + self._parsed = 1 self._file_mod_time = mtime self._readFile(1) === CMF/CMFCore/FSPageTemplate.py 1.3 => 1.3.20.1 === try: data = file.read() finally: file.close() - self.write(data) + if reparse: + self.write(data) security.declarePrivate('read') def read(self): === CMF/CMFCore/FSPythonScript.py 1.14 => 1.14.24.1 === try: data = file.read() finally: file.close() - self._write(data, reparse) + if reparse: + self._write(data, reparse) def _validateProxy(self, roles=None): pass + def __render_with_namespace__(self, namespace): + '''Calls the script.''' + self._updateFromFS() + return Script.__render_with_namespace__(self, namespace) + + def __call__(self, *args, **kw): + '''Calls the script.''' + self._updateFromFS() + return Script.__call__(self, args, kw) + #### The following is mainly taken from PythonScript.py ### def _exec(self, bound_names, args, kw): @@ -83,15 +94,11 @@ Calling a Python Script is an actual function invocation. """ - self._updateFromFS() # Prepare the function. f = self._v_f if f is None: - # Not yet compiled. - self._write(self._source, 1) - f = self._v_f - if f is None: - raise RuntimeError, '%s has errors.' % self._filepath + # The script has errors. + raise RuntimeError, '%s has errors.' % self._filepath __traceback_info__ = bound_names, args, kw, self.func_defaults @@ -185,15 +192,21 @@ # This ensures func_code and func_defaults are # set when the code hasn't been compiled yet, # just in time for mapply(). Truly odd, but so is mapply(). :P - self._write(self._source, 1) + self._updateFromFS() return self.__dict__.get('func_defaults', None) func_defaults = ComputedAttribute(func_defaults, 1) def func_code(self): # See func_defaults. - self._write(self._source, 1) + self._updateFromFS() return self.__dict__.get('func_code', None) func_code = ComputedAttribute(func_code, 1) + + def title(self): + # See func_defaults. + self._updateFromFS() + return self.__dict__.get('title', None) + title = ComputedAttribute(title, 1) Globals.InitializeClass(FSPythonScript) From shane@cvs.zope.org Thu Jan 24 03:42:55 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Wed, 23 Jan 2002 22:42:55 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - FSPythonScript.py:1.14.24.2 Message-ID: <200201240342.g0O3gtt06912@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv6895 Modified Files: Tag: cmf-pre-1_3-branch FSPythonScript.py Log Message: Have to pass varargs and kwargs as varargs and kwargs. Oops. === CMF/CMFCore/FSPythonScript.py 1.14.24.1 => 1.14.24.2 === '''Calls the script.''' self._updateFromFS() - return Script.__call__(self, args, kw) + return Script.__call__(self, *args, **kw) #### The following is mainly taken from PythonScript.py ### From tseaver@zope.com Thu Jan 24 15:32:19 2002 From: tseaver@zope.com (Tres Seaver) Date: Thu, 24 Jan 2002 10:32:19 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore - MembershipTool.py:1.18 Message-ID: <200201241532.g0OFWJ107707@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore In directory cvs.zope.org:/tmp/cvs-serv7700 Modified Files: MembershipTool.py Log Message: - Accept (but ignore) info. === Products/CMFCore/MembershipTool.py 1.17 => 1.18 === security.declarePrivate('listActions') - def listActions(self): + def listActions(self, info=None): return None security.declarePublic('getHomeFolder') From tseaver@zope.com Thu Jan 24 15:35:06 2002 From: tseaver@zope.com (Tres Seaver) Date: Thu, 24 Jan 2002 10:35:06 -0500 Subject: [CMF-checkins] CVS: Products/CMFCore - ActionProviderBase.py:1.4 Message-ID: <200201241535.g0OFZ6C08403@cvs.baymountain.com> Update of /cvs-repository/Products/CMFCore In directory cvs.zope.org:/tmp/cvs-serv8396 Modified Files: ActionProviderBase.py Log Message: - Accept (but ignore) info. === Products/CMFCore/ActionProviderBase.py 1.3 => 1.4 === security.declarePrivate('listActions') - def listActions(self): + def listActions(self, info=None): """ Return all the actions defined by a tool """ From chrisw@nipltd.com Fri Jan 25 11:16:51 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 06:16:51 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests/fake_skins CMF/CMFCore/tests/fake_skins - New directory Message-ID: <200201251116.g0PBGpM10527@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests/fake_skins In directory cvs.zope.org:/tmp/cvs-serv10521/fake_skins Log Message: Directory /cvs-repository/CMF/CMFCore/tests/fake_skins added to the repository === Added directory CMF/CMFCore/tests/fake_skins === From chrisw@nipltd.com Fri Jan 25 11:16:57 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 06:16:57 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests/fake_skins/fake_skin CMF/CMFCore/tests/fake_skins/fake_skin - New directory Message-ID: <200201251116.g0PBGvf10542@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests/fake_skins/fake_skin In directory cvs.zope.org:/tmp/cvs-serv10536/fake_skin Log Message: Directory /cvs-repository/CMF/CMFCore/tests/fake_skins/fake_skin added to the repository === Added directory CMF/CMFCore/tests/fake_skins/fake_skin === From chrisw@nipltd.com Fri Jan 25 11:17:36 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 06:17:36 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_DirectoryView.py:1.1 test_all.py:1.9 Message-ID: <200201251117.g0PBHaP10629@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv10617 Modified Files: test_all.py Added Files: test_DirectoryView.py Log Message: Basic DirectoryView tests. === Added File CMF/CMFCore/tests/test_DirectoryView.py === import Zope from unittest import TestCase, TestSuite, makeSuite, main from Products.CMFCore.DirectoryView import registerDirectory,addDirectoryViews,DirectoryViewSurrogate from Globals import package_home from Acquisition import Implicit from os import remove from os.path import join from shutil import copy2 # the path of our fake skin skin_path_name = join(package_home(globals()),'fake_skins','fake_skin') class DirectoryViewTests1( TestCase ): def setUp( self ): get_transaction().begin() def tearDown( self ): get_transaction().abort() def test_registerDirectory( self ): """ Test registerDirectory """ registerDirectory('fake_skins', globals()) class Dummy(Implicit): """ A Dummy object to use in place of the skins tool """ def _setObject(self,id,object): """ Dummy _setObject method """ setattr(self,id,object) class DirectoryViewTests2( TestCase ): def setUp( self ): get_transaction().begin() registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() addDirectoryViews(ob, 'fake_skins', globals()) def tearDown( self ): get_transaction().abort() def test_addDirectoryViews( self ): """ Test addDirectoryViews """ pass def test_DirectoryViewExists( self ): """ Check DirectoryView added by addDirectoryViews appears as a DirectoryViewSurrogate due to Acquisition hackery. """ assert isinstance(self.ob.fake_skin,DirectoryViewSurrogate) def test_DirectoryViewMethod( self ): """ Check if DirectoryView method works """ assert self.ob.fake_skin.test1()=='test1' import Globals import Products.CMFCore.DirectoryView test1path = join(skin_path_name,'test1.py') test2path = join(skin_path_name,'test2.py') class DebugModeTests( TestCase ): def setUp( self ): get_transaction().begin() # put us in debug mode, preserve the DirectoryRegistry Globals.DevelopmentMode=1 _dirreg = Products.CMFCore.DirectoryView._dirreg reload(Products.CMFCore.DirectoryView) Products.CMFCore.DirectoryView._dirreg = _dirreg # initialise skins Products.CMFCore.DirectoryView.registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() Products.CMFCore.DirectoryView.addDirectoryViews(ob, 'fake_skins', globals()) # add a method to the fake skin folder f = open(test2path,'w') f.write("return 'test2'") f.close() # edit the test1 method copy2(test1path,test1path+'.bak') f = open(test1path,'w') f.write("return 'new test1'") f.close() def tearDown( self ): # undo FS changes remove(test1path) copy2(test1path+'.bak',test1path) remove(test1path+'.bak') remove(test2path) # take us out of debug mode, preserve the DirectoryRegistry Globals.DevelopmentMode=None _dirreg = Products.CMFCore.DirectoryView._dirreg reload(Products.CMFCore.DirectoryView) Products.CMFCore.DirectoryView._dirreg = _dirreg get_transaction().abort() def test_AddNewMethod( self ): """ See if a method added to the skin folder can be found """ assert self.ob.fake_skin.test2()=='test2' def test_EditMethod( self ): """ See if an edited method exhibits its new behaviour """ assert self.ob.fake_skin.test1()=='new test1' def test_suite(): return TestSuite(( makeSuite(DirectoryViewTests1), makeSuite(DirectoryViewTests2), makeSuite(DebugModeTests), )) def run(): main(defaultTest='test_suite') if __name__ == '__main__': run() === CMF/CMFCore/tests/test_all.py 1.8 => 1.9 === from Products.CMFCore.tests import test_Expression from Products.CMFCore.tests import test_CatalogTool +from Products.CMFCore.tests import test_DirectoryView def test_suite(): suite = unittest.TestSuite() @@ -19,6 +20,7 @@ suite.addTest( test_ActionProviderBase.test_suite() ) suite.addTest( test_Expression.test_suite() ) suite.addTest( test_CatalogTool.test_suite() ) + suite.addTest( test_DirectoryView.test_suite() ) return suite def run(): From chrisw@nipltd.com Fri Jan 25 11:17:36 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 06:17:36 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests/fake_skins/fake_skin - test1.py:1.1 Message-ID: <200201251117.g0PBHat10631@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests/fake_skins/fake_skin In directory cvs.zope.org:/tmp/cvs-serv10617/fake_skins/fake_skin Added Files: test1.py Log Message: Basic DirectoryView tests. === Added File CMF/CMFCore/tests/fake_skins/fake_skin/test1.py === return 'test1' From chrisw@nipltd.com Fri Jan 25 12:20:10 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 07:20:10 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_DirectoryView.py:1.1.2.1 Message-ID: <200201251220.g0PCKAL26651@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv26644 Modified Files: Tag: ChrisW-DirectoryView_fix-branch test_DirectoryView.py Log Message: These tests don't work since I can' t simulate switching into debug mode properly :-( === CMF/CMFCore/tests/test_DirectoryView.py 1.1 => 1.1.2.1 === from unittest import TestCase, TestSuite, makeSuite, main from Products.CMFCore.DirectoryView import registerDirectory,addDirectoryViews,DirectoryViewSurrogate -from Globals import package_home +from Globals import package_home, DevelopmentMode from Acquisition import Implicit -from os import remove +from os import remove, mkdir, rmdir from os.path import join from shutil import copy2 @@ -63,6 +63,8 @@ test1path = join(skin_path_name,'test1.py') test2path = join(skin_path_name,'test2.py') +test3path = join(skin_path_name,'test3') + class DebugModeTests( TestCase ): @@ -80,6 +82,8 @@ Products.CMFCore.DirectoryView.registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() Products.CMFCore.DirectoryView.addDirectoryViews(ob, 'fake_skins', globals()) + + print "in test" # add a method to the fake skin folder f = open(test2path,'w') @@ -92,13 +96,26 @@ f.write("return 'new test1'") f.close() + # add a new folder + mkdir(test3path) + + def tearDown( self ): # undo FS changes remove(test1path) copy2(test1path+'.bak',test1path) remove(test1path+'.bak') - remove(test2path) + try: + remove(test2path) + except (IOError,OSError): + # it might be gone already + pass + try: + rmdir(test3path) + except (IOError,OSError): + # it might be gone already + pass # take us out of debug mode, preserve the DirectoryRegistry Globals.DevelopmentMode=None @@ -120,6 +137,37 @@ """ assert self.ob.fake_skin.test1()=='new test1' + def test_NewFolder( self ): + """ + See if a new folder shows up + """ + # This fails for some bizarre reason :-( - CW + # assert isinstance(self.ob.fake_skin.test3,DirectoryViewSurrogate) + self.ob.fake_skin.test3.objectIds() + + def test_DeleteMethod( self ): + """ + Make sure a deleted method goes away + """ + remove(test2path) + try: + self.ob.fake_skin.test2 + except AttributeError: + pass + else: + self.fail('test2 still exists') + + def test_DeleteFolder( self ): + """ + Make sure a deleted folder goes away + """ + rmdir(test3path) + try: + self.ob.fake_skin.test3 + except AttributeError: + pass + else: + self.fail('test3 still exists') def test_suite(): return TestSuite(( From chrisw@nipltd.com Fri Jan 25 12:23:46 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 07:23:46 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - DirectoryView.py:1.16.22.2 Message-ID: <200201251223.g0PCNkg27697@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv27686 Modified Files: Tag: ChrisW-DirectoryView_fix-branch DirectoryView.py Log Message: Tests now pass both normally andwhen Z_DEBUG_MODE=1 === CMF/CMFCore/DirectoryView.py 1.16.22.1 => 1.16.22.2 === if Globals.DevelopmentMode and os.name=='nt': - + def _changed(self): mtime=0 filelist=[] try: fp = expandpath(self.filepath) mtime = stat(fp)[8] - # Windows directories don't change mtime when a file - # in them changes :-( + # some Windows directories don't change mtime + # when a file in them changes :-( # So keep a list of files as well, and see if that # changes path.walk(fp,_walker,filelist) @@ -119,8 +119,9 @@ if mtime != self._v_last_read or filelist != self._v_last_filelist: self._v_last_read = mtime self._v_last_filelist = filelist + return 1 - + return 0 elif Globals.DevelopmentMode: @@ -134,7 +135,7 @@ return 0 else: - + def _changed(self): return 0 @@ -143,7 +144,7 @@ if self.data is None or changed: try: self.data, self.objects = self.prepareContents(registry, - register_subdirs=fish) + register_subdirs=changed) except: LOG('DirectoryView', ERROR, @@ -207,6 +208,7 @@ t = registry.getTypeByMetaType(mt) if t is None: t = registry.getTypeByExtension(ext) + if t is not None: try: ob = t(name, e_filepath, fullname=entry) From chrisw@nipltd.com Fri Jan 25 12:23:46 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 07:23:46 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_DirectoryView.py:1.1.2.2 Message-ID: <200201251223.g0PCNkx27699@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv27686/tests Modified Files: Tag: ChrisW-DirectoryView_fix-branch test_DirectoryView.py Log Message: Tests now pass both normally andwhen Z_DEBUG_MODE=1 === CMF/CMFCore/tests/test_DirectoryView.py 1.1.2.1 => 1.1.2.2 === assert self.ob.fake_skin.test1()=='test1' -import Globals -import Products.CMFCore.DirectoryView - test1path = join(skin_path_name,'test1.py') test2path = join(skin_path_name,'test2.py') test3path = join(skin_path_name,'test3') +if DevelopmentMode: -class DebugModeTests( TestCase ): + class DebugModeTests( TestCase ): def setUp( self ): get_transaction().begin() - # put us in debug mode, preserve the DirectoryRegistry - Globals.DevelopmentMode=1 - _dirreg = Products.CMFCore.DirectoryView._dirreg - reload(Products.CMFCore.DirectoryView) - Products.CMFCore.DirectoryView._dirreg = _dirreg - - # initialise skins - Products.CMFCore.DirectoryView.registerDirectory('fake_skins', globals()) + registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() - Products.CMFCore.DirectoryView.addDirectoryViews(ob, 'fake_skins', globals()) + addDirectoryViews(ob, 'fake_skins', globals()) - print "in test" - # add a method to the fake skin folder f = open(test2path,'w') f.write("return 'test2'") @@ -99,7 +88,6 @@ # add a new folder mkdir(test3path) - def tearDown( self ): # undo FS changes @@ -117,12 +105,6 @@ # it might be gone already pass - # take us out of debug mode, preserve the DirectoryRegistry - Globals.DevelopmentMode=None - _dirreg = Products.CMFCore.DirectoryView._dirreg - reload(Products.CMFCore.DirectoryView) - Products.CMFCore.DirectoryView._dirreg = _dirreg - get_transaction().abort() def test_AddNewMethod( self ): @@ -168,6 +150,11 @@ pass else: self.fail('test3 still exists') + +else: + + class DebugModeTests( TestCase ): + pass def test_suite(): return TestSuite(( From chrisw@nipltd.com Fri Jan 25 14:31:42 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 09:31:42 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_DirectoryView.py:1.1.2.3 Message-ID: <200201251431.g0PEVgq27376@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv27369 Modified Files: Tag: ChrisW-DirectoryView_fix-branch test_DirectoryView.py Log Message: changes suggested by Tres :-) === CMF/CMFCore/tests/test_DirectoryView.py 1.1.2.2 => 1.1.2.3 === from unittest import TestCase, TestSuite, makeSuite, main -from Products.CMFCore.DirectoryView import registerDirectory,addDirectoryViews,DirectoryViewSurrogate +from Products.CMFCore.DirectoryView import \ + registerDirectory,addDirectoryViews,DirectoryViewSurrogate from Globals import package_home, DevelopmentMode from Acquisition import Implicit from os import remove, mkdir, rmdir @@ -12,12 +13,6 @@ class DirectoryViewTests1( TestCase ): - def setUp( self ): - get_transaction().begin() - - def tearDown( self ): - get_transaction().abort() - def test_registerDirectory( self ): """ Test registerDirectory """ registerDirectory('fake_skins', globals()) @@ -34,14 +29,10 @@ class DirectoryViewTests2( TestCase ): def setUp( self ): - get_transaction().begin() registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() addDirectoryViews(ob, 'fake_skins', globals()) - def tearDown( self ): - get_transaction().abort() - def test_addDirectoryViews( self ): """ Test addDirectoryViews """ pass @@ -52,11 +43,11 @@ appears as a DirectoryViewSurrogate due to Acquisition hackery. """ - assert isinstance(self.ob.fake_skin,DirectoryViewSurrogate) + self.failUnless(isinstance(self.ob.fake_skin,DirectoryViewSurrogate)) def test_DirectoryViewMethod( self ): """ Check if DirectoryView method works """ - assert self.ob.fake_skin.test1()=='test1' + self.assertEqual(self.ob.fake_skin.test1(),'test1') test1path = join(skin_path_name,'test1.py') test2path = join(skin_path_name,'test2.py') @@ -67,7 +58,6 @@ class DebugModeTests( TestCase ): def setUp( self ): - get_transaction().begin() # initialise skins registerDirectory('fake_skins', globals()) @@ -105,26 +95,24 @@ # it might be gone already pass - get_transaction().abort() - def test_AddNewMethod( self ): """ See if a method added to the skin folder can be found """ - assert self.ob.fake_skin.test2()=='test2' + self.assertEqual(self.ob.fake_skin.test2(),'test2') def test_EditMethod( self ): """ See if an edited method exhibits its new behaviour """ - assert self.ob.fake_skin.test1()=='new test1' + self.assertEqual(self.ob.fake_skin.test1(),'new test1') def test_NewFolder( self ): """ See if a new folder shows up """ # This fails for some bizarre reason :-( - CW - # assert isinstance(self.ob.fake_skin.test3,DirectoryViewSurrogate) + self.failUnless(isinstance(self.ob.fake_skin.test3,DirectoryViewSurrogate)) self.ob.fake_skin.test3.objectIds() def test_DeleteMethod( self ): From chrisw@nipltd.com Fri Jan 25 14:50:52 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 09:50:52 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore/tests - test_DirectoryView.py:1.2 Message-ID: <200201251450.g0PEoqP32342@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore/tests In directory cvs.zope.org:/tmp/cvs-serv32278/CMFCore/tests Modified Files: test_DirectoryView.py Log Message: Merge fixes for #305 === CMF/CMFCore/tests/test_DirectoryView.py 1.1 => 1.2 === from unittest import TestCase, TestSuite, makeSuite, main -from Products.CMFCore.DirectoryView import registerDirectory,addDirectoryViews,DirectoryViewSurrogate -from Globals import package_home +from Products.CMFCore.DirectoryView import \ + registerDirectory,addDirectoryViews,DirectoryViewSurrogate +from Globals import package_home, DevelopmentMode from Acquisition import Implicit -from os import remove +from os import remove, mkdir, rmdir from os.path import join from shutil import copy2 @@ -12,12 +13,6 @@ class DirectoryViewTests1( TestCase ): - def setUp( self ): - get_transaction().begin() - - def tearDown( self ): - get_transaction().abort() - def test_registerDirectory( self ): """ Test registerDirectory """ registerDirectory('fake_skins', globals()) @@ -34,14 +29,10 @@ class DirectoryViewTests2( TestCase ): def setUp( self ): - get_transaction().begin() registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() addDirectoryViews(ob, 'fake_skins', globals()) - def tearDown( self ): - get_transaction().abort() - def test_addDirectoryViews( self ): """ Test addDirectoryViews """ pass @@ -52,35 +43,27 @@ appears as a DirectoryViewSurrogate due to Acquisition hackery. """ - assert isinstance(self.ob.fake_skin,DirectoryViewSurrogate) + self.failUnless(isinstance(self.ob.fake_skin,DirectoryViewSurrogate)) def test_DirectoryViewMethod( self ): """ Check if DirectoryView method works """ - assert self.ob.fake_skin.test1()=='test1' - -import Globals -import Products.CMFCore.DirectoryView + self.assertEqual(self.ob.fake_skin.test1(),'test1') test1path = join(skin_path_name,'test1.py') test2path = join(skin_path_name,'test2.py') +test3path = join(skin_path_name,'test3') + +if DevelopmentMode: -class DebugModeTests( TestCase ): + class DebugModeTests( TestCase ): def setUp( self ): - get_transaction().begin() - # put us in debug mode, preserve the DirectoryRegistry - Globals.DevelopmentMode=1 - _dirreg = Products.CMFCore.DirectoryView._dirreg - reload(Products.CMFCore.DirectoryView) - Products.CMFCore.DirectoryView._dirreg = _dirreg - - # initialise skins - Products.CMFCore.DirectoryView.registerDirectory('fake_skins', globals()) + registerDirectory('fake_skins', globals()) ob = self.ob = Dummy() - Products.CMFCore.DirectoryView.addDirectoryViews(ob, 'fake_skins', globals()) - + addDirectoryViews(ob, 'fake_skins', globals()) + # add a method to the fake skin folder f = open(test2path,'w') f.write("return 'test2'") @@ -92,34 +75,74 @@ f.write("return 'new test1'") f.close() + # add a new folder + mkdir(test3path) + def tearDown( self ): # undo FS changes remove(test1path) copy2(test1path+'.bak',test1path) remove(test1path+'.bak') - remove(test2path) + try: + remove(test2path) + except (IOError,OSError): + # it might be gone already + pass + try: + rmdir(test3path) + except (IOError,OSError): + # it might be gone already + pass - # take us out of debug mode, preserve the DirectoryRegistry - Globals.DevelopmentMode=None - _dirreg = Products.CMFCore.DirectoryView._dirreg - reload(Products.CMFCore.DirectoryView) - Products.CMFCore.DirectoryView._dirreg = _dirreg - - get_transaction().abort() - def test_AddNewMethod( self ): """ See if a method added to the skin folder can be found """ - assert self.ob.fake_skin.test2()=='test2' + self.assertEqual(self.ob.fake_skin.test2(),'test2') def test_EditMethod( self ): """ See if an edited method exhibits its new behaviour """ - assert self.ob.fake_skin.test1()=='new test1' + self.assertEqual(self.ob.fake_skin.test1(),'new test1') + + def test_NewFolder( self ): + """ + See if a new folder shows up + """ + # This fails for some bizarre reason :-( - CW + self.failUnless(isinstance(self.ob.fake_skin.test3,DirectoryViewSurrogate)) + self.ob.fake_skin.test3.objectIds() + def test_DeleteMethod( self ): + """ + Make sure a deleted method goes away + """ + remove(test2path) + try: + self.ob.fake_skin.test2 + except AttributeError: + pass + else: + self.fail('test2 still exists') + + def test_DeleteFolder( self ): + """ + Make sure a deleted folder goes away + """ + rmdir(test3path) + try: + self.ob.fake_skin.test3 + except AttributeError: + pass + else: + self.fail('test3 still exists') + +else: + + class DebugModeTests( TestCase ): + pass def test_suite(): return TestSuite(( From chrisw@nipltd.com Fri Jan 25 14:50:52 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 09:50:52 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - DirectoryView.py:1.18 Message-ID: <200201251450.g0PEoqM32340@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv32278/CMFCore Modified Files: DirectoryView.py Log Message: Merge fixes for #305 === CMF/CMFCore/DirectoryView.py 1.17 => 1.18 === ############################################################################## """Views of filesystem directories as folders.""" -__version__='$Revision$'[11:-2] +__version__='$Revision$'[11:-2] import Globals from Globals import HTMLFile, Persistent, package_home, DTMLFile @@ -37,14 +37,22 @@ # Ignore version control subdirectories # and special names. +def _filter(name): + return name not in ('CVS', 'SVN', '.', '..') + def _filtered_listdir(path): - n = filter(lambda name: name not in ('CVS', 'SVN', '.', '..'), + n = filter(_filter, listdir(path)) return n +def _walker (listdir, dirname, names): + names[:]=filter(_filter,names) + listdir.extend(names) + class DirectoryInformation: data = None _v_last_read = 0 + _v_last_filelist = [] # Only used on Win32 def __init__(self, expanded_fp, minimal_fp): self.filepath = minimal_fp @@ -86,15 +94,53 @@ types[strip(obname)] = strip(meta_type) return types - def getContents(self, registry): - changed = 0 - if Globals.DevelopmentMode: + if Globals.DevelopmentMode and os.name=='nt': + + def _changed(self): + mtime=0 + filelist=[] + try: + fp = expandpath(self.filepath) + mtime = stat(fp)[8] + # some Windows directories don't change mtime + # when a file in them changes :-( + # So keep a list of files as well, and see if that + # changes + path.walk(fp,_walker,filelist) + filelist.sort() + except: + from zLOG import LOG, ERROR + import sys + LOG('DirectoryView', + ERROR, + 'Error checking for directory modification', + error=sys.exc_info()) + + if mtime != self._v_last_read or filelist != self._v_last_filelist: + self._v_last_read = mtime + self._v_last_filelist = filelist + + return 1 + + return 0 + + elif Globals.DevelopmentMode: + + def _changed(self): try: mtime = stat(expandpath(self.filepath))[8] except: mtime = 0 if mtime != self._v_last_read: self._v_last_read = mtime - changed = 1 + return 1 + return 0 + + else: + def _changed(self): + return 0 + + def getContents(self, registry): + changed = self._changed() if self.data is None or changed: try: self.data, self.objects = self.prepareContents(registry, @@ -162,6 +208,7 @@ t = registry.getTypeByMetaType(mt) if t is None: t = registry.getTypeByExtension(ext) + if t is not None: try: ob = t(name, e_filepath, fullname=entry) From chrisw@nipltd.com Fri Jan 25 14:51:22 2002 From: chrisw@nipltd.com (Chris Withers) Date: Fri, 25 Jan 2002 09:51:22 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.44 Message-ID: <200201251451.g0PEpMP32408@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv32278 Modified Files: CHANGES.txt Log Message: Merge fixes for #305 === CMF/CHANGES.txt 1.43 => 1.44 === Bugs Fixed + - Fixed bug whereby DirectoryView instances were not noticing + some of the changes they should when Zope was running in debug mode + on Windows (Tracker #305) + - Fixed a bug where the workflow notifyCreated method was called during manage_afterAdd in PortalContent, making it possible for the notification to occur on the wrong workflow. The notification has From klm@zope.com Sat Jan 26 22:19:07 2002 From: klm@zope.com (Ken Manheimer) Date: Sat, 26 Jan 2002 17:19:07 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCollector - CollectorIssue.py:1.39 Message-ID: <200201262219.g0QMJ7e31654@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCollector In directory cvs.zope.org:/tmp/cvs-serv31647 Modified Files: CollectorIssue.py Log Message: _send_update_notice(): Fix state-sensitive email dispatching - had been computing the recipients according to a version of the state with '_confidential' chopped - bad, bad! This rectifies that, so state_email will reflect the actual state. === CMF/CMFCollector/CollectorIssue.py 1.38 => 1.39 === def _send_update_notice(self, action, actor, orig_status=None, additions=None, removals=None, - file=None, fileid=None): + file=None, fileid=None, lower=string.lower): """Send email notification about issue event to relevant parties.""" action = string.capitalize(string.split(action, '_')[0]) - new_status = string.split(self.status(), '_')[0] + new_status = self.status() recipients = [] @@ -406,16 +406,24 @@ # - Person taking action always # - Supporters assigned to the issue always # - Managers or managers + all supporters (according to dispatching): - # - When an issue is any state besides accepted - # - When an issue is being accepted - # - When an issue is accepted and moving to another state + # - When in any state besides accepted (or accepted_confidential) + # - When being accepted (or accepted_confidential) + # - When is accepted (or accepted_confidential) and moving to + # another state # - Any supporters being removed from the issue by the current action + # - In addition, any destinations for the resulting state registered + # in state_email are included. # - # We're liberal about duplicates - they'll be filtered before send. + # We're liberal about allowing duplicates in the collection phase - + # all duplicate addresses will be filtered out before actual send. candidates = [self.submitter_id, actor] + list(self.assigned_to()) - if orig_status and not ('accepted' == string.lower(new_status) == - string.lower(orig_status)): + continuing_accepted = (lower(orig_status) in ['accepted', + 'accepted_confidential'] + and + lower(new_status) in ['accepted', + 'accepted_confidential']) + if orig_status and not continuing_accepted: candidates.extend(self.aq_parent.managers) if not self.aq_parent.dispatching: candidates.extend(self.aq_parent.supporters) From klm@zope.com Sat Jan 26 22:20:46 2002 From: klm@zope.com (Ken Manheimer) Date: Sat, 26 Jan 2002 17:20:46 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCollector/skins/collector - collector_macros.pt:1.29 Message-ID: <200201262220.g0QMKkS32209@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCollector/skins/collector In directory cvs.zope.org:/tmp/cvs-serv32202 Modified Files: collector_macros.pt Log Message: Parenthesize the state when it had '_confidential'. XXX A subtle cue, to be documented. === CMF/CMFCollector/skins/collector/collector_macros.pt 1.28 => 1.29 === -
From shane@cvs.zope.org Tue Jan 15 17:46:49 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Tue, 15 Jan 2002 12:46:49 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - ActionsTool.py:1.23.2.1 Message-ID: <200201151746.g0FHknm06504@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv6465 Modified Files: Tag: cmf-pre-1_3-branch ActionsTool.py Log Message: Propagate the action element of action structures. It is useful for building custom action boxes. === CMF/CMFCore/ActionsTool.py 1.23 => 1.23.2.1 === 'id': d.get('id', None), 'name': d['name'], + 'action': d['action'], 'url': url, 'permissions': d['permissions'], 'category': d.get('category', 'object'), From andrew@zope.com Tue Jan 15 20:14:06 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 15 Jan 2002 15:14:06 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault - version.txt:1.4.14.1 Message-ID: <200201152014.g0FKE6O08015@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault In directory cvs.zope.org:/tmp/cvs-serv7946/CMFDefault Modified Files: Tag: CMF-1_2-branch version.txt Log Message: *Fixed version numbers for the CMF1.2 release. *Tracker #446 === CMF/CMFDefault/version.txt 1.4 => 1.4.14.1 === +1.2 From andrew@zope.com Tue Jan 15 20:14:06 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 15 Jan 2002 15:14:06 -0500 Subject: [CMF-checkins] CVS: CMF/CMFTopic - version.txt:1.4.12.1 Message-ID: <200201152014.g0FKE6n08017@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFTopic In directory cvs.zope.org:/tmp/cvs-serv7946/CMFTopic Modified Files: Tag: CMF-1_2-branch version.txt Log Message: *Fixed version numbers for the CMF1.2 release. *Tracker #446 === CMF/CMFTopic/version.txt 1.4 => 1.4.12.1 === +1.2 From andrew@zope.com Tue Jan 15 20:14:36 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 15 Jan 2002 15:14:36 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCalendar - VERSION.txt:1.2.8.1 Message-ID: <200201152014.g0FKEam08023@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCalendar In directory cvs.zope.org:/tmp/cvs-serv7946/CMFCalendar Modified Files: Tag: CMF-1_2-branch VERSION.txt Log Message: *Fixed version numbers for the CMF1.2 release. *Tracker #446 === CMF/CMFCalendar/VERSION.txt 1.2 => 1.2.8.1 === +0.2b From andrew@zope.com Tue Jan 15 20:14:36 2002 From: andrew@zope.com (Andrew Sawyers) Date: Tue, 15 Jan 2002 15:14:36 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - version.txt:1.4.14.1 Message-ID: <200201152014.g0FKEaF08026@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv7946/CMFCore Modified Files: Tag: CMF-1_2-branch version.txt Log Message: *Fixed version numbers for the CMF1.2 release. *Tracker #446 === CMF/CMFCore/version.txt 1.4 => 1.4.14.1 === +1.2 From andrew@zope.com Wed Jan 16 14:48:53 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 09:48:53 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCore - FSPageTemplate.py:1.2.2.1 Message-ID: <200201161448.g0GEmrl21320@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCore In directory cvs.zope.org:/tmp/cvs-serv21307 Modified Files: Tag: CMF-1_2-branch FSPageTemplate.py Log Message: *Added recognition of filesystem .html and .htm to be loaded as FSPageTemplates === CMF/CMFCore/FSPageTemplate.py 1.2 => 1.2.2.1 === registerFileExtension('pt', FSPageTemplate) +registerFileExtension('html', FSPageTemplate) +registerFileExtension('htm', FSPageTemplate) registerMetaType('Page Template', FSPageTemplate) From andrew@zope.com Wed Jan 16 15:15:57 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 10:15:57 -0500 Subject: [CMF-checkins] CVS: CMF - CHANGES.txt:1.36.2.22 Message-ID: <200201161515.g0GFFvb28447@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv28092 Modified Files: Tag: CMF-1_2-branch CHANGES.txt Log Message: *Updated changes for my last check-in. === CMF/CHANGES.txt 1.36.2.21 => 1.36.2.22 === Bugs Fixed + - Added .html and .htm files to be loaded from the filesystem as + FSPageTemplates. + - Fixed a bug where the workflow notifyCreated method was called during manage_afterAdd in PortalContent, making it possible for the notification to occur on the wrong workflow. The notification has From andrew@zope.com Wed Jan 16 20:05:21 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 15:05:21 -0500 Subject: [CMF-checkins] CVS: CMF - ISSUES.txt:1.2.14.4 Message-ID: <200201162005.g0GK5LD30234@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv29739 Modified Files: Tag: CMF-1_2-branch ISSUES.txt Log Message: *Noted possible issue with additions to the ActionsTool default actions_provider list. === CMF/ISSUES.txt 1.2.14.3 => 1.2.14.4 === + Issue: Modified Action Providers not showing in upgrade to CMF 1.2 + + When migrating a site to CMF 1.2, the new modifications to the ActionsTool + action_providers is not showing up in the migrated instance. If you have + registered or removed action providers with the actions tool, you will not + get the new changes made to the action_providers list in a CMF 1.2 + upgrade. + + Workaround + + 1. Delete portal_actions in the root of your CMF instance. + + 2. Add CMF Core Tool - Actions Tool + + 3. Reapply your actions provider changes. + Issue: 'CMFDecor'-based skins lost in upgrade to CMF 1.2 When migrating a site to 1.2 from CMF, where the skin directories From andrew@zope.com Wed Jan 16 20:43:53 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 15:43:53 -0500 Subject: [CMF-checkins] CVS: CMF - HISTORY.txt:1.31.4.2 Message-ID: <200201162043.g0GKhrx06414@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv6407 Modified Files: Tag: CMF-1_2-branch HISTORY.txt Log Message: *Finalizing Docs for 1.2 final release === CMF/HISTORY.txt 1.31.4.1 => 1.31.4.2 === +1.2 Final (2002/01/16) New features From andrew@zope.com Wed Jan 16 21:35:30 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 16:35:30 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCalendar - DEPENDENCIES.txt:1.3.4.1 INSTALL.txt:1.3.4.1 VERSION.txt:1.2.8.2 __init__.py:1.2.2.1 Message-ID: <200201162135.g0GLZUu18427@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCalendar In directory cvs.zope.org:/tmp/cvs-serv17910/CMFCalendar Modified Files: Tag: CMF-1_2-branch DEPENDENCIES.txt INSTALL.txt VERSION.txt __init__.py Log Message: *Updated more documentation... *Updated CMFCalendar to 0.3b for use with CMF 1.2 === CMF/CMFCalendar/DEPENDENCIES.txt 1.3 => 1.3.4.1 === === CMF/CMFCalendar/INSTALL.txt 1.3 => 1.3.4.1 === steps it took to register and install the CMF Events into the CMF Site instance. - - If you wish to use the zpt_calendar skins, you will need to - have PageTemplates installed, and the CMFDecor product - installed. === CMF/CMFCalendar/VERSION.txt 1.2.8.1 => 1.2.8.2 === +0.3b === CMF/CMFCalendar/__init__.py 1.2 => 1.2.2.1 === registerDirectory('skins', globals()) registerDirectory('skins/calendar', globals()) +registerDirectory('skins/zpt_calendar', globals()) def initialize( context ): utils.initializeBasesPhase2( z_bases, context ) From andrew@zope.com Wed Jan 16 21:35:30 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 16:35:30 -0500 Subject: [CMF-checkins] CVS: CMF/CMFCalendar/Extensions - Install.py:1.6.2.1 Message-ID: <200201162135.g0GLZUh18428@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFCalendar/Extensions In directory cvs.zope.org:/tmp/cvs-serv17910/CMFCalendar/Extensions Modified Files: Tag: CMF-1_2-branch Install.py Log Message: *Updated more documentation... *Updated CMFCalendar to 0.3b for use with CMF 1.2 === CMF/CMFCalendar/Extensions/Install.py 1.6 => 1.6.2.1 === # Setup the skins - # This is borrowed from CMFDefault/scripts/addImagesToSkinPaths.pys - if 'calendar' not in skinstool.objectIds(): + if 'calendar' not in skinstool.objectIds() or 'zpt_calendar' not in skinstool.objectIds(): # We need to add Filesystem Directory Views for any directories # in our skins/ directory. These directories should already be # configured. addDirectoryViews(skinstool, 'skins', event_globals) - out.write("Added 'calendar' directory view to portal_skins\n") + out.write("Added 'calendar' directory views to portal_skins\n") # Now we need to go through the skin configurations and insert # 'calendar' into the configurations. Preferably, this should be @@ -120,17 +119,20 @@ for skin in skins: path = skinstool.getSkinPath(skin) path = map(string.strip, string.split(path,',')) - if 'calendar' not in path: + if 'calendar' not in path and 'zpt_calendar' not in path: try: path.insert(path.index('content'), 'calendar') except ValueError: path.append('calendar') - + if skin == 'Basic': + try: path.insert(path.index('zpt_content'), 'zpt_calendar') + except ValueError: + path.append('zpt_calendar') path = string.join(path, ', ') # addSkinSelection will replace exissting skins as well. skinstool.addSkinSelection(skin, path) - out.write("Added 'calendar' to %s skin\n" % skin) + out.write("Added 'calendar' to %s skins\n" % skin) else: - out.write("Skipping %s skin, 'calendar' is already set up\n" % ( + out.write("Skipping %s skin, 'calendar skins' are already set up\n" % ( skin)) return out.getvalue() From andrew@zope.com Wed Jan 16 21:35:59 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 16 Jan 2002 16:35:59 -0500 Subject: [CMF-checkins] CVS: CMF - DEPENDENCIES.txt:1.1.12.1 INSTALL.txt:1.4.2.2 Message-ID: <200201162135.g0GLZxv18782@cvs.baymountain.com> Update of /cvs-repository/CMF In directory cvs.zope.org:/tmp/cvs-serv17910 Modified Files: Tag: CMF-1_2-branch DEPENDENCIES.txt INSTALL.txt Log Message: *Updated more documentation... *Updated CMFCalendar to 0.3b for use with CMF 1.2 === CMF/DEPENDENCIES.txt 1.1 => 1.1.12.1 === +requires "ZPT" "1.4.1" === CMF/INSTALL.txt 1.4.2.1 => 1.4.2.2 === +Installing CMF 1.2 Requirements - Zope v. 2.3.3, 2.4.3 or 2.5 and later (*not* 2.4.2 or 2.4.1) + - Zope PageTemplates + Assumptions - New installation @@ -13,7 +15,7 @@ Procedure - 1. Unpack the CMF-1.1beta.tar.gz tarball into a working + 1. Unpack the CMF-1.2.tar.gz tarball or CMF-1.2.zip file into a working directory. For instance:: $ cd /usr/local/zope @@ -49,17 +51,17 @@ Enjoy! *Note:* -- - the optional 'CMFDecor' product **requires** that you have + CMFDecor has been merged into CMFDefault, it is not longer and + optional product. It requires that you have "Zope Page Templates", http://www.zope.org/Members/4am/ZPT - installed; if you don't have it, then don't link/copy in - 'CMFDecor'. + installed. -Upgrading from CMF 1.0 +Upgrading from CMF 1.1 Install the New Software. - 0. "Download":CMF-1.1beta.tar.gz the tarball. + 0. "Download":CMF-1.2.tar.gz the tarball. 1. Copy your "working" products off to one side (in case you need / choose to revert). @@ -69,6 +71,20 @@ your INSTANCE_HOME. 3. Restart Zope. + + 4. **Note** + + If your CMF instance was setup to use CMFDecor you will need to Create + and run the external method Upgrade.py (upgrade_decor_skins) + from the root of your CMF instance to upgrade your skin paths. + This fixes breakage due to the merging of CMFDecor into CMFDefault. + +Upgrading from CMF 1.0 + + + Install the New Software. + + 1. Follow the procudures for upgrading from CMF 1.1 Create and Configure New tools From shane@cvs.zope.org Wed Jan 16 22:43:31 2002 From: shane@cvs.zope.org (Shane Hathaway) Date: Wed, 16 Jan 2002 17:43:31 -0500 Subject: [CMF-checkins] CVS: CMF/DCWorkflow - CHANGES.txt:1.8 Message-ID: <200201162243.g0GMhVN01879@cvs.baymountain.com> Update of /cvs-repository/CMF/DCWorkflow In directory cvs.zope.org:/tmp/cvs-serv1872 Modified Files: CHANGES.txt Log Message: Updated CHANGES for DCWorkflow-0.4.2 === CMF/DCWorkflow/CHANGES.txt 1.7 => 1.8 === - Fixed getInfoFor() using patch from Sebastien.Bigaret@inqual.com. Thanks! -- executeTransition(): Preserve (by copying) any existing, unchanged -state vars, rather than setting them to default value or just omitting -them, if there's no default. +- executeTransition(): Optionally preserve (by copying) unchanged +status variables. + +- Updated to ZPL 2.0. + +- Added scripts that get executed after a transition. Version 0.4.1 From andrew@zope.com Fri Jan 18 16:14:00 2002 From: andrew@zope.com (Andrew Sawyers) Date: Fri, 18 Jan 2002 11:14:00 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/dtml - synProps.dtml:1.2.14.1 Message-ID: <200201181614.g0IGE0920889@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/dtml In directory cvs.zope.org:/tmp/cvs-serv20776/CMFDefault/dtml Modified Files: Tag: CMF-1_2-branch synProps.dtml Log Message: *Fixed bugs in SyndicationTool module where methods weren't properly dealing with the case when obj was None. *Fixed zpt skin to properly select the update frequency. === CMF/CMFDefault/dtml/synProps.dtml 1.2 => 1.2.14.1 === - +
+ value="&dtml-getUpdateFrequency;" size="3">
+ value="&dtml-getUpdateBase;" size="70">
+ value="&dtml-getMaxItems;" size="3">
- +
+ value="&dtml-getUpdateFrequency;" size="3">
+ value="&dtml-getUpdateBase;" size="70">
+ value="&dtml-getMaxItems;" size="3">
Status: + Update of /cvs-repository/CMF/CMFDefault/Extensions In directory cvs.zope.org:/tmp/cvs-serv27575 Modified Files: update_catalogIndexes.py Log Message: *Added REQUEST to arguments for update_catalogIndexes; overlooked from last fix === CMF/CMFDefault/Extensions/update_catalogIndexes.py 1.3 => 1.4 === -def update_catalogIndexes(self): +def update_catalogIndexes(self, REQUEST): ''' External method to drop, re-add, and rebuild catalog Indexes for migrated CMF sites from Zope 2.3 to 2.4+. From andrew@zope.com Wed Jan 30 19:59:02 2002 From: andrew@zope.com (Andrew Sawyers) Date: Wed, 30 Jan 2002 14:59:02 -0500 Subject: [CMF-checkins] CVS: CMF/CMFDefault/Extensions - update_catalogIndexes.py:1.1.4.3 Message-ID: <200201301959.g0UJx2F08092@cvs.baymountain.com> Update of /cvs-repository/CMF/CMFDefault/Extensions In directory cvs.zope.org:/tmp/cvs-serv7895 Modified Files: Tag: CMF-1_2-branch update_catalogIndexes.py Log Message: *Added REQUEST to the update_catalogIndexes arguements missed in the first change. === CMF/CMFDefault/Extensions/update_catalogIndexes.py 1.1.4.2 => 1.1.4.3 === -def update_catalogIndexes(self): +def update_catalogIndexes(self, REQUEST): ''' External method to drop, re-add, and rebuild catalog Indexes for migrated CMF sites from Zope 2.3 to 2.4+.