Commit 3ab70b57 authored by Jean-Paul Smets's avatar Jean-Paul Smets

Refactored version of Interactor with compoenent sysytem

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@29545 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 06755c34
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
......@@ -35,6 +36,10 @@ from zLOG import LOG
global product_document_registry
product_document_registry = []
global product_interactor_registry
product_interactor_registry = []
global interactor_class_id_registry
interactor_class_id_registry = {}
def getProductDocumentPathList():
result = product_document_registry
......@@ -45,7 +50,16 @@ def InitializeDocument(document_class, document_path=None):
global product_document_registry
# Register class in ERP5Type.Document
product_document_registry.append(((document_class, document_path)))
#LOG('InitializeDocument', 0, document_class.__name__)
def getProductInteractorPathList():
result = product_interactor_registry
result.sort()
return result
def InitializeInteractor(interactor_class, interactor_path=None):
global product_interactor_registry
# Register class in ERP5Type.Interactor
product_interactor_registry.append(((interactor_class, interactor_path)))
def initializeProductDocumentRegistry():
from Utils import importLocalDocument
......@@ -56,3 +70,17 @@ def initializeProductDocumentRegistry():
#LOG('Added product document to ERP5Type repository: %s (%s)' % (class_id, document_path), 0, '')
#print 'Added product document to ERP5Type repository: %s (%s)' % (class_id, document_path)
def initializeProductInteractorRegistry():
from Utils import importLocalInteractor
for (class_id, interactor_path) in product_interactor_registry:
if class_id != 'Interactor': # Base class can not be global and placeless
importLocalInteractor(class_id, path=interactor_path)
def registerInteractorClass(class_id, klass):
global interactor_class_id_registry
interactor_class_id_registry[class_id] = klass
def installInteractorClassRegistry():
global interactor_class_id_registry
for class_id, klass in interactor_class_id_registry.items():
klass().install()
from MethodObject import Method
from Products.ERP5Type.Base import _aq_reset
"""
Current implementation uses callable objects.
Using decorator would be more modern and consistent with
recent evolution of python. But we have 2.3 ERP5 users
so we should, at least, provide both implementations.
Code structure should be changed so that Interactors
because a new "type" of ERP5 class such Document
with a modular plugin structure.
TODO: multiple instances of interactors could
use different parameters. This way, interactions
can be defined on "instances" or can be
made generic.
"""
class InteractorMethodCall:
"""
Method's wrapper used to keep arguments passed, in order to retrieve them
from before and after scripts if needed.
"""
def __init__(self, method, instance, *args, **kw):
self.instance = instance
self.args = args
self.kw = kw
self.method = method
def __call__(self):
# We use self.__dict__['instance'] to prevent inserting the
# InteractorMethodCall instance in the acquisition chain
# which has some side effects
return self.method(self.__dict__['instance'], *self.args, **self.kw)
class InteractorMethod(Method):
def __init__(self, method):
self.after_action_list = []
self.before_action_list = []
self.method = method
self.func_code = method.func_code
self.func_defaults = method.func_defaults
self.__name__ = method.__name__
def registerBeforeAction(self, action, args, kw):
self.before_action_list.append((action, args, kw))
def registerAfterAction(self, action, args, kw):
self.after_action_list.append((action, args, kw))
def __call__(self, instance, *args, **kw):
method_call_object = InteractorMethodCall(self.method, instance, *args, **kw)
for action, args, kw in self.before_action_list:
action(method_call_object, *args, **kw)
result = method_call_object()
for action, args, kw in self.after_action_list:
action(method_call_object, *args, **kw)
return result
class InteractorSource:
def __init__(self, method):
"""
Register method
"""
self.method = method
def doAfter(self, action, *args, **kw):
"""
"""
im_class = self.method.im_class
if not isinstance(self.method, InteractorMethod):
# Turn this into an InteractorMethod
interactor_method = InteractorMethod(self.method)
setattr(im_class, self.method.__name__, interactor_method)
self.method = interactor_method
# Register the action
self.method.registerAfterAction(action, args, kw)
class Interactor:
def install(self):
raise NotImplementedError
def uninstall(self):
raise NotImplementedError
# Interaction implementation
def on(self, method):
"""
Parameters may hold predicates ?
no need - use InteractorMethodCall and decide on action
"""
return InteractorSource(method)
class AqDynamicInteractor(Interactor):
"""This base interactor will reset _aq_dynamic method cache, to regenerate
new accessors.
"""
def resetAqDynamic(self, *args, **kw):
"""
Reset _aq_dynamic
"""
_aq_reset()
class WorkflowToolInteractor(AqDynamicInteractor):
"""This interactor reset aq_dynamic method cache whenever workflow
associations are changed.
"""
def install(self):
from Products.CMFCore.WorkflowTool import WorkflowTool
self.on(WorkflowTool.manage_changeWorkflows).doAfter(self.resetAqDynamic)
class DCWorkflowInteractor(AqDynamicInteractor):
"""This interactor reset aq_dynamic method cache whenever a workflow
definition changes
"""
def install(self):
from Products.DCWorkflow.Transitions import Transitions
self.on(Transitions.addTransition).doAfter(self.resetAqDynamic)
self.on(Transitions.deleteTransitions).doAfter(self.resetAqDynamic)
from Products.DCWorkflow.Transitions import TransitionDefinition
self.on(TransitionDefinition.setProperties).doAfter(self.resetAqDynamic)
from Products.DCWorkflow.Variables import Variables
self.on(Variables.setStateVar).doAfter(self.resetAqDynamic)
## #
## # Experimental part
## #
## class InteractionWorkflowAqDynamicInteractor(AqDynamicInteractor):
## def install(self):
## """
## Installs interactions
## """
## from Products.ERP5.Interaction import InteractionDefinition
## self.on(InteractionDefinition.setProperties).doAfter(self.resetAqDynamic, 1, 2, toto="foo")
## self.on(InteractionDefinition.addVariable).doAfter(self.resetAqDynamic, 1, 2, toto="foo")
## def uninstall(self):
## """
## Uninstall interactions
## """
## # Interaction example
## class TypeInteractorExample(Interactor):
## def __init__(self, portal_type):
## self.portal_type = portal_type
## def install(self):
## from Products.CMFCore.TypesTool import TypesTool
## self.on(TypesTool.manage_edit).doAfter(self.doSomething)
## def doSomething(self, method_call_object):
## if self.portal_type == method_call_object.instance.portal_type:
## pass
## # do whatever
## class InteractorOfInteractor(Interactor):
## def __init__(self, interactor):
## self.interactor = interactor
## def install(self):
## self.on(interactor.doSomething).doAfter(self.doSomething)
## def doSomething(self, method_call_object):
## pass
## test = AqDynamicInteractor()
## test.install()
class FieldValueInteractor(Interactor):
def install(self):
"""
Installs interactions
"""
from Products.Formulator.Field import ZMIField
from Products.ERP5Form.ProxyField import ProxyField
from Products.Formulator.Form import ZMIForm
self.on(ZMIField.manage_edit).doAfter(self.purgeFieldValueCache)
self.on(ZMIField.manage_edit_xmlrpc).doAfter(self.purgeFieldValueCache)
self.on(ZMIField.manage_tales).doAfter(self.purgeFieldValueCache)
self.on(ZMIField.manage_tales_xmlrpc).doAfter(self.purgeFieldValueCache)
self.on(ProxyField.manage_edit).doAfter(self.purgeFieldValueCache)
self.on(ProxyField.manage_edit_target).doAfter(self.purgeFieldValueCache)
self.on(ProxyField.manage_tales).doAfter(self.purgeFieldValueCache)
self.on(ZMIForm.manage_renameObject).doAfter(self.purgeFieldValueCache)
def uninstall(self):
"""
Uninstall interactions
"""
def purgeFieldValueCache(self, method_call_object):
"""
"""
from Products.ERP5Form import Form, ProxyField
Form.purgeFieldValueCache()
ProxyField.purgeFieldValueCache()
class TypeInteractorExample(Interactor):
def __init__(self, portal_type):
self.portal_type = portal_type
def install(self):
from Products.CMFCore.TypesTool import TypesTool
self.on(TypesTool.manage_edit).doAfter(self.doSomething)
def doSomething(self, method_call_object):
if self.portal_type == method_call_object.instance.portal_type:
pass
# do whatever
class InteractorOfInteractor(Interactor):
def __init__(self, interactor):
self.interactor = interactor
def install(self):
self.on(Interactor.doSomething).doAfter(self.doSomething)
def doSomething(self, method_call_object):
pass
#interactor_of_interactor = InteractorOfInteractor(test)
#interactor_of_interactor.install()
# Install some interactors:
WorkflowToolInteractor().install()
DCWorkflowInteractor().install()
# This is used in ERP5Form and install method is called in ERP5Form
# Don't install this interactor here.
field_value_interactor = FieldValueInteractor()
......@@ -319,7 +319,7 @@ def getTranslationStringWithContext(self, msg_id, context, context_id):
# Globals initialization
#####################################################
from InitGenerator import InitializeDocument
from InitGenerator import InitializeDocument, InitializeInteractor, registerInteractorClass
# List Regexp
python_file_expr = re.compile("py$")
......@@ -355,7 +355,7 @@ def updateGlobals(this_module, global_hook,
this_module._dtmldir = os.path.join( product_path, 'dtml' )
# Update PropertySheet Registry
for module_id in ('PropertySheet', 'interfaces', 'Constraint', ):
for module_id in ('PropertySheet', 'interfaces', 'Constraint'):
path, module_id_list = getModuleIdList(product_path, module_id)
if module_id == 'PropertySheet':
import_method = importLocalPropertySheet
......@@ -383,6 +383,12 @@ def updateGlobals(this_module, global_hook,
path, module_id_list = getModuleIdList(product_path, 'Document')
for document in module_id_list:
InitializeDocument(document, document_path=path)
# Return interactor_class list
path, interactor_id_list = getModuleIdList(product_path, 'Interactor')
for interactor in interactor_id_list:
InitializeInteractor(interactor, interactor_path=path)
return module_id_list + core_module_id_list
#####################################################
......@@ -567,6 +573,20 @@ def importLocalConstraint(class_id, path = None):
finally:
f.close()
def importLocalInteractor(class_id, path=None):
import Products.ERP5Type.Interactor
if path is None:
instance_home = getConfiguration().instancehome
path = os.path.join(instance_home, "Interactor")
path = os.path.join(path, "%s.py" % class_id)
f = open(path)
try:
module = imp.load_source(class_id, path, f)
setattr(Products.ERP5Type.Interactor, class_id, getattr(module, class_id))
registerInteractorClass(class_id, getattr(Products.ERP5Type.Interactor, class_id))
finally:
f.close()
def getLocalExtensionList():
if not getConfiguration:
return []
......@@ -801,6 +821,7 @@ def importLocalDocument(class_id, document_path = None):
import Products.ERP5Type.Document
import Permissions
import Products
if document_path is None:
instance_home = getConfiguration().instancehome
path = os.path.join(instance_home, "Document")
......@@ -934,6 +955,10 @@ def initializeLocalPropertySheetRegistry():
def initializeLocalConstraintRegistry():
initializeLocalRegistry("Constraint", importLocalConstraint)
def initializeLocalInteractorRegistry():
initializeLocalRegistry("Interactor", importLocalInteractor)
#####################################################
# Product initialization
#####################################################
......@@ -942,7 +967,7 @@ def initializeProduct( context,
this_module,
global_hook,
document_module=None,
document_classes=None,
document_classes=None, # XXX - Never used - must be likely removed
object_classes=None,
portal_tools=None,
content_constructors=None,
......
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved.
......@@ -29,9 +30,9 @@
ERP5Type is provides a RAD environment for Zope / CMF
All ERP5 classes derive from ERP5Type
"""
# Switch(es) for ongoing development which require single code base
USE_BASE_TYPE = False
USE_INTERACTOR = False
# Update ERP5 Globals
import sys, Permissions, os
......@@ -99,9 +100,15 @@ def initialize( context ):
# We should register local classes at some point
from Products.ERP5Type.Utils import initializeLocalDocumentRegistry
initializeLocalDocumentRegistry()
# Experimental Interactor
if USE_INTERACTOR:
import Interactor
# We can now setup global interactors
from Products.ERP5Type.InitGenerator import initializeProductInteractorRegistry
initializeProductInteractorRegistry()
# And local interactors
from Products.ERP5Type.Utils import initializeLocalInteractorRegistry
initializeLocalInteractorRegistry()
# We can now install all interactors
from Products.ERP5Type.InitGenerator import installInteractorClassRegistry
installInteractorClassRegistry()
from AccessControl.SecurityInfo import allow_module
from AccessControl.SecurityInfo import ModuleSecurityInfo
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment