diff --git a/product/CMFActivity/ActiveObject.py b/product/CMFActivity/ActiveObject.py index 5fbe5ed87c1cd041eaf30c196125f6f631477a2b..7c56deb15f4f68c2542c81f696d4839214f74855 100755 --- a/product/CMFActivity/ActiveObject.py +++ b/product/CMFActivity/ActiveObject.py @@ -29,6 +29,7 @@ import ExtensionClass from AccessControl import ClassSecurityInfo from Products.CMFCore import CMFCorePermissions +from Acquisition import aq_base from zLOG import LOG @@ -37,27 +38,17 @@ DEFAULT_ACTIVITY = 'SQLDict' #DEFAULT_ACTIVITY = 'RAMDict' -def flushActivity(object, invoke=0, **kw): - # flush all activities related to this object - try: - object.portal_activities.flush(self, invoke=invoke, **kw) - except: - # If the portal_activities were not created - # nothing to do - pass - - class ActiveObject(ExtensionClass.Base): security = ClassSecurityInfo() - def activate(self, activity=DEFAULT_ACTIVITY, **kw): + def activate(self, activity=DEFAULT_ACTIVITY, active_process=None, **kw): # activate returns an ActiveWrapper # a queue can be provided as well as extra parameters # which can be used for example to define deferred tasks try: #if 1: - return self.portal_activities.activate(self, activity, **kw) + return self.portal_activities.activate(self, activity, active_process, **kw) #else: except: LOG("WARNING CMFActivity:",0, 'could not create activity for %s' % self.getRelativeUrl()) @@ -65,6 +56,7 @@ class ActiveObject(ExtensionClass.Base): # return a passive object return self + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'hasActivity' ) def flushActivity(self, invoke=0, **kw): # flush all activities related to this object #try: @@ -75,10 +67,14 @@ class ActiveObject(ExtensionClass.Base): # # nothing to do # pass - def recursiveFlushActivity(self, **kw): + security.declareProtected( CMFCorePermissions.ModifyPortalContent, 'hasActivity' ) + def recursiveFlushActivity(self, invoke=0, **kw): # flush all activities related to this object - # updateAll is defined in ERP5Type - self.recursiveApply(method=flushActivity, **kw) + self.flushActivity(invoke=invoke, **kw) + if hasattr(aq_base(self), 'objectValues'): + for o in self.objectValues(): + if hasattr(aq_base(self), 'recursiveFlushActivity'): + o.recursiveFlushActivity(invoke=invoke, **kw) security.declareProtected( CMFCorePermissions.View, 'hasActivity' ) def hasActivity(self, **kw): @@ -91,3 +87,7 @@ class ActiveObject(ExtensionClass.Base): # If the portal_activities were not created # there can not be any activity return 0 + + security.declareProtected( CMFCorePermissions.View, 'hasActivity' ) + def getActiveProcess(self): + return self.portal_activities.getActiveProcess() diff --git a/product/CMFActivity/ActiveProcess.py b/product/CMFActivity/ActiveProcess.py new file mode 100755 index 0000000000000000000000000000000000000000..aa22c322c77b211a2c2b581cb7202da9cbc6bda8 --- /dev/null +++ b/product/CMFActivity/ActiveProcess.py @@ -0,0 +1,106 @@ +############################################################################## +# +# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Jean-Paul Smets-Solane <jp@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +from Acquisition import aq_base +from Globals import InitializeClass, DTMLFile +from AccessControl import ClassSecurityInfo +from Products.CMFCore import CMFCorePermissions +from Products.ERP5Type.Base import Base +from Products.ERP5Type import PropertySheet + +from zLOG import LOG + +manage_addActiveProcessForm=DTMLFile('dtml/ActiveProcess_add', globals()) + +def addActiveProcess( self, id, title='', REQUEST=None ): + """ + Add a new Category and generate UID by calling the + ZSQLCatalog + """ + sf = ActiveProcess( id ) + sf._setTitle(title) + self._setObject( id, sf ) + sf = self._getOb( id ) + sf.reindexObject() + if REQUEST is not None: + return self.manage_main(self, REQUEST, update_menu=1) + +class ActiveProcess(Base): + """ + ActiveProcess is used to centralise interaction between multiple ActiveObject + """ + + meta_type='CMF Active Process' + portal_type=None # may be useful in the future... + isPortalContent = 0 + isRADContent = 1 + icon = None + + # Declarative security + security = ClassSecurityInfo() + security.declareProtected(CMFCorePermissions.ManagePortal, + 'manage_editProperties', + 'manage_changeProperties', + 'manage_propertiesForm', + ) + + # Declarative properties + property_sheets = ( PropertySheet.Base + , PropertySheet.SimpleItem ) + + # Declarative constructors + constructors = (manage_addActiveProcessForm, addActiveProcess) + + # Base methods + security.declareProtected(CMFCorePermissions.ManagePortal, 'postError') + def postError(self, error): + if not hasattr(aq_base(self), 'error_list'): + self.error_list = [] + self.error_list.append(error) + + security.declareProtected(CMFCorePermissions.ManagePortal, 'getErrorList') + def getErrorList(self): + """ + Returns the list of errors + """ + return self.error_list + + security.declareProtected(CMFCorePermissions.ManagePortal, 'getErrorListText') + def getErrorListText(self): + """ + Returns the list of errors as text + """ + return '\n'.join(self.error_list) + + security.declareProtected(CMFCorePermissions.ManagePortal, 'activateResult') + def activateResult(self, result): + if result not in (None, 0, '', (), []): + self.activate().postError(result) + + +InitializeClass( ActiveProcess ) diff --git a/product/CMFActivity/Activity/Queue.py b/product/CMFActivity/Activity/Queue.py index 889593df25d5bdf7defb89aa9fd19c2d29c4e81b..e2918bff47bd688571045460706fbd3f53725c27 100755 --- a/product/CMFActivity/Activity/Queue.py +++ b/product/CMFActivity/Activity/Queue.py @@ -27,6 +27,7 @@ ############################################################################## import pickle +from Acquisition import aq_base from Products.CMFActivity.ActivityTool import Message from zLOG import LOG diff --git a/product/CMFActivity/ActivityTool.py b/product/CMFActivity/ActivityTool.py index d8e59ff168b6f7d5e99e803907abd9fec7f5cf92..ce16a5dc1a0a715f4ca99373cc6cfa6478825816 100755 --- a/product/CMFActivity/ActivityTool.py +++ b/product/CMFActivity/ActivityTool.py @@ -27,10 +27,10 @@ ############################################################################## from Products.CMFCore import CMFCorePermissions -from Products.CMFCore.PortalFolder import PortalFolder +from Products.ERP5Type.Document.Folder import Folder from AccessControl import ClassSecurityInfo from Products.CMFCore.utils import UniqueObject, _checkPermission, _getAuthenticatedUser -from Globals import InitializeClass, DTMLFile +from Globals import InitializeClass, DTMLFile, get_request from Acquisition import aq_base from DateTime.DateTime import DateTime import threading @@ -56,11 +56,17 @@ def registerActivity(activity): activity_dict[activity.__name__] = activity_instance class Message: - def __init__(self, object, activity_kw, method_id, args, kw): + def __init__(self, object, active_process, activity_kw, method_id, args, kw): if type(object) is type('a'): self.object_path = object.split('/') else: self.object_path = object.getPhysicalPath() + if type(active_process) is type('a'): + self.active_process = active_process.split('/') + elif active_process is None: + self.active_process = None + else: + self.active_process = active_process.getPhysicalPath() self.activity_kw = activity_kw self.method_id = method_id self.args = args @@ -73,7 +79,12 @@ class Message: LOG('WARNING ActivityTool', 0, 'Trying to call method %s on object %s' % (self.method_id, self.object_path)) object = activity_tool.unrestrictedTraverse(self.object_path) - getattr(object, self.method_id)(*self.args, **self.kw) + REQUEST = get_request() + REQUEST.active_process = self.active_process + result = getattr(object, self.method_id)(*self.args, **self.kw) + if REQUEST.active_process is not None: + active_process = activity_tool.getActiveProcess() + active_process.activateResult(result) # XXX Allow other method_id in future self.__is_executed = 1 except: LOG('WARNING ActivityTool', 0, @@ -85,49 +96,69 @@ class Message: class Method: - def __init__(self, passive_self, activity, kw, method_id): + def __init__(self, passive_self, activity, active_process, kw, method_id): self.__passive_self = passive_self self.__activity = activity + self.__active_process = active_process self.__kw = kw self.__method_id = method_id def __call__(self, *args, **kw): - m = Message(self.__passive_self, self.__kw, self.__method_id, args, kw) + m = Message(self.__passive_self, self.__active_process, self.__kw, self.__method_id, args, kw) activity_dict[self.__activity].queueMessage(self.__passive_self.portal_activities, m) class ActiveWrapper: - def __init__(self, passive_self, activity, **kw): + def __init__(self, passive_self, activity, active_process, **kw): self.__dict__['__passive_self'] = passive_self self.__dict__['__activity'] = activity + self.__dict__['__active_process'] = active_process self.__dict__['__kw'] = kw def __getattr__(self, id): return Method(self.__dict__['__passive_self'], self.__dict__['__activity'], + self.__dict__['__active_process'], self.__dict__['__kw'], id) -class ActivityTool (UniqueObject, PortalFolder): +class ActivityTool (Folder, UniqueObject): """ This is a ZSQLCatalog that filters catalog queries. It is based on ZSQLCatalog """ id = 'portal_activities' meta_type = 'CMF Activity Tool' + allowed_types = ( 'CMF Active Process', ) security = ClassSecurityInfo() tic_lock = threading.Lock() - manage_options = ( { 'label' : 'Overview', 'action' : 'manage_overview' } + manage_options = tuple( + [ { 'label' : 'Overview', 'action' : 'manage_overview' } , { 'label' : 'Activities', 'action' : 'manageActivities' } , - ) - + ] + list(Folder.manage_options)) security.declareProtected( CMFCorePermissions.ManagePortal , 'manageActivities' ) manageActivities = DTMLFile( 'dtml/manageActivities', globals() ) + security.declareProtected( CMFCorePermissions.ManagePortal , 'manage_overview' ) + manage_overview = DTMLFile( 'dtml/explainActivityTool', globals() ) + + def __init__(self): + return Folder.__init__(self, ActivityTool.id) + + # Filter content (ZMI)) + def filtered_meta_types(self, user=None): + # Filters the list of available meta types. + all = ActivityTool.inheritedAttribute('filtered_meta_types')(self) + meta_types = [] + for meta_type in self.all_meta_types(): + if meta_type['name'] in self.allowed_types: + meta_types.append(meta_type) + return meta_types + def initialize(self): global is_initialized - from Activity import RAMQueue, RAMDict, SQLDict, ZODBDict + from Activity import RAMQueue, RAMDict, SQLDict # Initialize each queue for activity in activity_list: activity.initialize(self) @@ -187,10 +218,10 @@ class ActivityTool (UniqueObject, PortalFolder): return 1 return 0 - def activate(self, object, activity, **kw): + def activate(self, object, activity, active_process, **kw): global is_initialized if not is_initialized: self.initialize() - return ActiveWrapper(object, activity, **kw) + return ActiveWrapper(object, activity, active_process, **kw) def flush(self, object, invoke=0, **kw): global is_initialized @@ -203,10 +234,11 @@ class ActivityTool (UniqueObject, PortalFolder): def invoke(self, message): message(self) - def newMessage(self, activity, path, activity_kw, method_id, *args, **kw): + def newMessage(self, activity, path, active_process, activity_kw, method_id, *args, **kw): + # Some Security Cheking should be made here XXX global is_initialized if not is_initialized: self.initialize() - activity_dict[activity].queueMessage(self, Message(path, activity_kw, method_id, args, kw)) + activity_dict[activity].queueMessage(self, Message(path, active_process, activity_kw, method_id, args, kw)) def manageInvoke(self, object_path, method_id, REQUEST=None): """ @@ -240,4 +272,21 @@ class ActivityTool (UniqueObject, PortalFolder): message_list += activity.getMessageList(self) return message_list + security.declareProtected( CMFCorePermissions.ManagePortal , 'newActiveProcess' ) + def newActiveProcess(self): + from ActiveProcess import addActiveProcess + new_id = str(self.generateNewId()) + addActiveProcess(self, new_id) + return self._getOb(new_id) + + def reindexObject(self): + self.immediateReindexObject() + + def getActiveProcess(self): + REQUEST = get_request() + if REQUEST.active_process: + return self.unrestrictedTraverse(REQUEST.active_process) + return None + + InitializeClass(ActivityTool) diff --git a/product/CMFActivity/__init__.py b/product/CMFActivity/__init__.py index e2f9280eebe6abc41109ebfac4b73a37cc2c9ad2..7ccc9d0c5bda69a9ffff58c69b0766930c3775f8 100755 --- a/product/CMFActivity/__init__.py +++ b/product/CMFActivity/__init__.py @@ -42,8 +42,8 @@ document_classes = generateInitFiles(this_module, globals()) from Products.ERP5Type.Utils import initializeProduct, updateGlobals # Define object classes and tools -import ActivityTool -object_classes = () +import ActivityTool, ActiveProcess +object_classes = (ActiveProcess.ActiveProcess, ) portal_tools = (ActivityTool.ActivityTool,) content_classes = () content_constructors = () diff --git a/product/CMFActivity/dtml/ActiveProcess_add.dtml b/product/CMFActivity/dtml/ActiveProcess_add.dtml new file mode 100755 index 0000000000000000000000000000000000000000..65505d698903d01e74dca5d12f32b202c5b9afd4 --- /dev/null +++ b/product/CMFActivity/dtml/ActiveProcess_add.dtml @@ -0,0 +1,84 @@ +<dtml-var manage_page_header> + +<dtml-var "manage_form_title(this(), _, + form_title='Add Category', + help_product='ERP5', + help_topic='Category_Add.stx' + )"> +<p class="form-help"> +A Folder contains other objects. Use Folders to organize your +web objects in to logical groups. The <em>create public interface</em> +option creates an index document inside the Folder to give the +Folder a default HTML representation. The <em>create user folder</em> +option creates a User Folder inside the Folder to hold authorization +information for the Folder. +</p> + +<FORM ACTION="addActiveProcess" METHOD="POST"> + +<table cellspacing="0" cellpadding="2" border="0"> + <tr> + <td align="left" valign="top"> + <div class="form-label"> + Id + </div> + </td> + <td align="left" valign="top"> + <input type="text" name="id" size="40" /> + </td> + </tr> + + <tr> + <td align="left" valign="top"> + <div class="form-optional"> + Title + </div> + </td> + <td align="left" valign="top"> + <input type="text" name="title" size="40" /> + </td> + </tr> +<dtml-if + "_.SecurityCheckPermission('Add Documents, Images, and Files',this())"> + <tr> + <td align="left" valign="top"> + </td> + <td align="left" valign="top"> + <div class="form-text"> + <input type="checkbox" name="createPublic:int" value="1" + id="cbCreatePublic"> + <label for="cbCreatePublic">Create public interface</label> + </div> + </td> + </tr> +</dtml-if> + +<dtml-if + "_.SecurityCheckPermission('Add User Folders',this())"> + <tr> + <td align="left" valign="top"> + </td> + <td align="left" valign="top"> + <div class="form-text"> + <input type="checkbox" name="createUserF:int" value="1" + id="cbCreateUserF"> + <label for="cbCreateUserF">Create user folder</label> + </div> + </td> + </tr> +</dtml-if> + + <tr> + <td align="left" valign="top"> + </td> + <td align="left" valign="top"> + <div class="form-element"> + <input class="form-element" type="submit" name="submit" + value="Add" /> + </div> + </td> + </tr> +</table> +</form> + +<dtml-var manage_page_footer> diff --git a/product/CMFActivity/dtml/explainActivityTool.dtml b/product/CMFActivity/dtml/explainActivityTool.dtml new file mode 100755 index 0000000000000000000000000000000000000000..9e59bafc3d31bb3c0d667a80fb7a8247e4657daa --- /dev/null +++ b/product/CMFActivity/dtml/explainActivityTool.dtml @@ -0,0 +1,6 @@ +<dtml-var manage_page_header> +<dtml-var manage_tabs> + +<p>Explain CategoryTool</p> + +<dtml-var manage_page_footer>