Commit 2f54ba04 authored by Yoshinori Okuji's avatar Yoshinori Okuji

Fix some dynamic patches. They should not create new classes.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3565 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent cccbd8cd
...@@ -49,127 +49,116 @@ from Products.Formulator.Field import Field ...@@ -49,127 +49,116 @@ from Products.Formulator.Field import Field
from zLOG import LOG from zLOG import LOG
class ERP5Field(Field): def get_value(self, id, **kw):
""" """Get value for id."""
The ERP5Field provides here, request, # FIXME: backwards compat hack to make sure tales dict exists
container etc. names to TALES expressions. It is used to dynamically if not hasattr(self, 'tales'):
patch the standard Formulator self.tales = {}
"""
security = ClassSecurityInfo() tales_expr = self.tales.get(id, "")
if tales_expr:
# this is a field form = self.aq_parent
is_field = 1 object = getattr(form, 'aq_parent', None)
if object:
security.declareProtected('Access contents information', 'get_value') # NEEDS TO BE CHECKED
def get_value(self, id, **kw): # container = object.aq_inner.aq_parent ORIGINAL VERSION - not so good ?
"""Get value for id.""" container = object.aq_parent
# FIXME: backwards compat hack to make sure tales dict exists #container = object.getParentNode()
if not hasattr(self, 'tales'):
self.tales = {}
tales_expr = self.tales.get(id, "")
if tales_expr:
form = self.aq_parent
object = getattr(form, 'aq_parent', None)
if object:
# NEEDS TO BE CHECKED
# container = object.aq_inner.aq_parent ORIGINAL VERSION - not so good ?
container = object.aq_parent
#container = object.getParentNode()
else:
container = None
kw['field'] = self
kw['form'] = form
kw['here'] = object
kw['container'] = container
# This allows to pass some pointer to the local object
# through the REQUEST parameter. Not very clean.
# Used by ListBox to render different items in a list
if kw.has_key('REQUEST') and not kw.get('cell'): kw['cell'] = kw['REQUEST']
try:
value = tales_expr.__of__(self)(**kw)
except:
# We add this safety exception to make sure we always get
# something reasonable rather than generate plenty of errors
LOG('ERP5Form.get_value ( %s/%s [%s]), exception on tales_expr: '%(
self.aq_parent.getId(), self.getId(), id) ,0,'', error=sys.exc_info())
value = self.get_orig_value(id)
else: else:
# FIXME: backwards compat hack to make sure overrides dict exists container = None
if not hasattr(self, 'overrides'): kw['field'] = self
self.overrides = {} kw['form'] = form
kw['here'] = object
override = self.overrides.get(id, "") kw['container'] = container
if override: # This allows to pass some pointer to the local object
# call wrapped method to get answer # through the REQUEST parameter. Not very clean.
value = override.__of__(self)() # Used by ListBox to render different items in a list
else: if kw.has_key('REQUEST') and not kw.get('cell'): kw['cell'] = kw['REQUEST']
# get normal value
value = self.get_orig_value(id)
# Only for the default value
if id == 'default':
if (value is None or value == '' or value == [] or value == ()) \
and self.meta_type != 'MethodField' :
# If nothing was provided then try to
# find a default method to get the value
# for that field
# NEEDS TO BE CLEANED UP
try:
form = self.aq_parent
object = getattr(form, 'aq_parent', None)
key = self.id
key = key[3:]
value = object.getProperty(key, d=value)
except:
value = None
# if normal value is a callable itself, wrap it
if callable(value):
value = value.__of__(self)
#value=value() # Mising call ??? XXX Make sure compatible with listbox methods
if id == 'default':
if self.meta_type != 'DateTimeField':
# We make sure we convert values to empty strings
# for most fields (so that we do not get a 'value'
# message on screeen)
# This can be overriden by useing TALES in the field
if value is None: value = ''
return value
def om_icons(self):
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': self.icon,
'alt': self.meta_type, 'title': self.meta_type},)
return icons
psyco.bind(get_value)
def _get_default(self, key, value, REQUEST):
if value is not None:
return value
try: try:
value = REQUEST.form[key] value = tales_expr.__of__(self)(**kw)
except (KeyError, AttributeError): except:
# fall back on default # We add this safety exception to make sure we always get
return self.get_value('default',REQUEST=REQUEST) # It was missing on Formulator # something reasonable rather than generate plenty of errors
LOG('ERP5Form.get_value ( %s/%s [%s]), exception on tales_expr: '%(
# if we enter a string value while the field expects unicode, self.aq_parent.getId(), self.getId(), id) ,0,'', error=sys.exc_info())
# convert to unicode first value = self.get_orig_value(id)
# this solves a problem when re-rendering a sticky form with else:
# values from request # FIXME: backwards compat hack to make sure overrides dict exists
if (self.has_value('unicode') and self.get_value('unicode') and if not hasattr(self, 'overrides'):
type(value) == type('')): self.overrides = {}
return unicode(value, self.get_form_encoding())
override = self.overrides.get(id, "")
if override:
# call wrapped method to get answer
value = override.__of__(self)()
else: else:
return value # get normal value
value = self.get_orig_value(id)
# Only for the default value
if id == 'default':
if (value is None or value == '' or value == [] or value == ()) \
and self.meta_type != 'MethodField' :
# If nothing was provided then try to
# find a default method to get the value
# for that field
# NEEDS TO BE CLEANED UP
try:
form = self.aq_parent
object = getattr(form, 'aq_parent', None)
key = self.id
key = key[3:]
value = object.getProperty(key, d=value)
except:
value = None
# if normal value is a callable itself, wrap it
if callable(value):
value = value.__of__(self)
#value=value() # Mising call ??? XXX Make sure compatible with listbox methods
if id == 'default':
if self.meta_type != 'DateTimeField':
# We make sure we convert values to empty strings
# for most fields (so that we do not get a 'value'
# message on screeen)
# This can be overriden by useing TALES in the field
if value is None: value = ''
return value
psyco.bind(get_value)
def om_icons(self):
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': self.icon,
'alt': self.meta_type, 'title': self.meta_type},)
return icons
def _get_default(self, key, value, REQUEST):
if value is not None:
return value
try:
value = REQUEST.form[key]
except (KeyError, AttributeError):
# fall back on default
return self.get_value('default',REQUEST=REQUEST) # It was missing on Formulator
# if we enter a string value while the field expects unicode,
# convert to unicode first
# this solves a problem when re-rendering a sticky form with
# values from request
if (self.has_value('unicode') and self.get_value('unicode') and
type(value) == type('')):
return unicode(value, self.get_form_encoding())
else:
return value
# Dynamic Patch # Dynamic Patch
Field.get_value = ERP5Field.get_value Field.get_value = get_value
Field._get_default = ERP5Field._get_default Field._get_default = _get_default
Field.om_icons = ERP5Field.om_icons Field.om_icons = om_icons
# Constructors # Constructors
......
...@@ -26,70 +26,63 @@ from Products.Formulator.Widget import Widget ...@@ -26,70 +26,63 @@ from Products.Formulator.Widget import Widget
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from zLOG import LOG from zLOG import LOG
class PatchedField(Field): def Field_generate_field_key(self, validation=0, key=None):
"""Generate the key Silva uses to render the field in the form.
security = ClassSecurityInfo() """
security.declareProtected('Access contents information', # Patched by JPS for ERP5 in order to
'generate_field_key') # dynamically change the name
def generate_field_key(self, validation=0, key=None): if key is not None:
"""Generate the key Silva uses to render the field in the form. return 'field_%s' % key
""" if self.field_record is None:
# Patched by JPS for ERP5 in order to return 'field_%s' % self.id
# dynamically change the name elif validation:
if key is not None: return self.id
return 'field_%s' % key elif isinstance(self.widget, MultiItemsWidget):
if self.field_record is None: return "%s.%s:record:list" % (self.field_record, self.id)
return 'field_%s' % self.id else:
elif validation: return '%s.%s:record' % (self.field_record, self.id)
return self.id
elif isinstance(self.widget, MultiItemsWidget): def Field_render(self, value=None, REQUEST=None, key=None):
return "%s.%s:record:list" % (self.field_record, self.id) """Render the field widget.
else: value -- the value the field should have (for instance
return '%s.%s:record' % (self.field_record, self.id) from validation).
REQUEST -- REQUEST can contain raw (unvalidated) field
security.declareProtected('View', 'render') information. If value is None, REQUEST is searched
def render(self, value=None, REQUEST=None, key=None): for this value.
"""Render the field widget. if value and REQUEST are both None, the 'default' property of
value -- the value the field should have (for instance the field will be used for the value.
from validation). """
REQUEST -- REQUEST can contain raw (unvalidated) field return self._render_helper(self.generate_field_key(key=key), value, REQUEST)
information. If value is None, REQUEST is searched
for this value.
if value and REQUEST are both None, the 'default' property of
the field will be used for the value.
"""
return self._render_helper(self.generate_field_key(key=key), value, REQUEST)
security.declareProtected('View', 'render_sub_field') def Field_render_sub_field(self, id, value=None, REQUEST=None, key=None):
def render_sub_field(self, id, value=None, REQUEST=None, key=None): """Render a sub field, as part of complete rendering of widget in
"""Render a sub field, as part of complete rendering of widget in a form. Works like render() but for sub field.
a form. Works like render() but for sub field. Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox """
""" return self.sub_form.get_field(id)._render_helper(
return self.sub_form.get_field(id)._render_helper( self.generate_subfield_key(id, key=key), value, REQUEST)
self.generate_subfield_key(id, key=key), value, REQUEST)
def generate_subfield_key(self, id, validation=0, key=None): def Field_generate_subfield_key(self, id, validation=0, key=None):
"""Generate the key Silva uses to render a sub field. """Generate the key Silva uses to render a sub field.
Added key parameter for ERP5 Added key parameter for ERP5
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
""" """
if key is None: key = self.id if key is None: key = self.id
if self.field_record is None or validation: if self.field_record is None or validation:
return 'subfield_%s_%s'%(key, id) return 'subfield_%s_%s'%(key, id)
return '%s.subfield_%s_%s:record' % (self.field_record, key, id) return '%s.subfield_%s_%s:record' % (self.field_record, key, id)
def validate_sub_field(self, id, REQUEST, key=None): def Field_validate_sub_field(self, id, REQUEST, key=None):
"""Validates a subfield (as part of field validation). """Validates a subfield (as part of field validation).
""" """
return self.sub_form.get_field(id)._validate_helper( return self.sub_form.get_field(id)._validate_helper(
self.generate_subfield_key(id, validation=1, key=key), REQUEST) self.generate_subfield_key(id, validation=1, key=key), REQUEST)
Field.generate_field_key = PatchedField.generate_field_key Field.generate_field_key = Field_generate_field_key
Field.render = PatchedField.render Field.render = Field_render
Field.render_sub_field = PatchedField.render_sub_field Field.render_sub_field = Field_render_sub_field
Field.generate_subfield_key = PatchedField.generate_subfield_key Field.generate_subfield_key = Field_generate_subfield_key
Field.validate_sub_field = PatchedField.validate_sub_field Field.validate_sub_field = Field_validate_sub_field
from Products.Formulator.Validator import SelectionValidator from Products.Formulator.Validator import SelectionValidator
from Products.Formulator.Validator import StringBaseValidator from Products.Formulator.Validator import StringBaseValidator
...@@ -564,25 +557,18 @@ MultiItemsWidget.render_items = MultiItemsWidget_render_items ...@@ -564,25 +557,18 @@ MultiItemsWidget.render_items = MultiItemsWidget_render_items
# JPS - Subfield handling with listbox requires extension # JPS - Subfield handling with listbox requires extension
from Products.Formulator.StandardFields import DateTimeField from Products.Formulator.StandardFields import DateTimeField
class PatchedDateTimeField(DateTimeField): def DateTimeField_get_default(self, key, value, REQUEST):
""" if value is not None:
Make sur we test if this REQUEST parameter has a form return value
attribute. In ERP5, we sometimes use the REQUEST to pass # if there is something in the request then return None
subobjects to forms. # sub fields should pick up defaults themselves
""" if REQUEST is not None and hasattr(REQUEST, 'form') and \
REQUEST.form.has_key('subfield_%s_%s' % (self.id, 'year')):
def _get_default(self, key, value, REQUEST): return None
if value is not None: else:
return value return self.get_value('default')
# if there is something in the request then return None
# sub fields should pick up defaults themselves
if REQUEST is not None and hasattr(REQUEST, 'form') and \
REQUEST.form.has_key('subfield_%s_%s' % (self.id, 'year')):
return None
else:
return self.get_value('default')
DateTimeField._get_default = PatchedDateTimeField._get_default DateTimeField._get_default = DateTimeField_get_default
from Products.Formulator.Widget import DateTimeWidget from Products.Formulator.Widget import DateTimeWidget
......
...@@ -61,7 +61,7 @@ import FormulatorPatch ...@@ -61,7 +61,7 @@ import FormulatorPatch
import psyco import psyco
psyco.bind(ListBox.ListBoxWidget.render) psyco.bind(ListBox.ListBoxWidget.render)
psyco.bind(ListBox.ListBoxValidator.validate) psyco.bind(ListBox.ListBoxValidator.validate)
psyco.bind(Form.ERP5Field.get_value) #psyco.bind(Form.ERP5Field.get_value)
#psyco.bind(Form.ERP5Form.__call__) #psyco.bind(Form.ERP5Form.__call__)
#psyco.bind(Form.ERP5Form._exec) #psyco.bind(Form.ERP5Form._exec)
......
...@@ -24,39 +24,37 @@ from Products.CMFCore.FSZSQLMethod import FSZSQLMethod ...@@ -24,39 +24,37 @@ from Products.CMFCore.FSZSQLMethod import FSZSQLMethod
from Products.CMFCore.DirectoryView import expandpath from Products.CMFCore.DirectoryView import expandpath
from Products.ZSQLMethods.SQL import SQL from Products.ZSQLMethods.SQL import SQL
class PatchedFSZSQLMethod(FSZSQLMethod): def FSZSQLMethod_readFile(self, reparse):
fp = expandpath(self._filepath)
def _readFile(self, reparse): file = open(fp, 'r') # not 'rb', as this is a text file!
fp = expandpath(self._filepath) try:
file = open(fp, 'r') # not 'rb', as this is a text file! data = file.read()
try: finally: file.close()
data = file.read()
finally: file.close() RESPONSE = {}
RESPONSE['BODY'] = data
RESPONSE = {}
RESPONSE['BODY'] = data self.PUT(RESPONSE,None)
self.PUT(RESPONSE,None)
def FSZSQLMethod_createZODBClone(self):
"""Create a ZODB (editable) equivalent of this object."""
def _createZODBClone(self): # I guess it's bad to 'reach inside' ourselves like this,
"""Create a ZODB (editable) equivalent of this object.""" # but Z SQL Methods don't have accessor methdods ;-)
# I guess it's bad to 'reach inside' ourselves like this, s = SQL(self.id,
# but Z SQL Methods don't have accessor methdods ;-) self.title,
s = SQL(self.id, self.connection_id,
self.title, self.arguments_src,
self.connection_id, self.src)
self.arguments_src, s.manage_advanced(self.max_rows_,
self.src) self.max_cache_,
s.manage_advanced(self.max_rows_, self.cache_time_,
self.max_cache_, self.class_name_,
self.cache_time_, self.class_file_)
self.class_name_, return s
self.class_file_)
return s FSZSQLMethod._readFile = FSZSQLMethod_readFile
FSZSQLMethod._createZODBClone = FSZSQLMethod_createZODBClone
FSZSQLMethod._readFile = PatchedFSZSQLMethod._readFile
FSZSQLMethod._createZODBClone = PatchedFSZSQLMethod._createZODBClone
from Products.CMFCore import ActionInformation from Products.CMFCore import ActionInformation
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -188,146 +186,144 @@ ActionInformation.ActionInformation = PatchedActionInformation ...@@ -188,146 +186,144 @@ ActionInformation.ActionInformation = PatchedActionInformation
from Products.CMFCore.ActionProviderBase import ActionProviderBase from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.ActionInformation import ActionInformation from Products.CMFCore.ActionInformation import ActionInformation
class PatchedActionProviderBase(ActionProviderBase): def ActionProviderBase_manage_editActionsForm( self, REQUEST, manage_tabs_message=None ):
def manage_editActionsForm( self, REQUEST, manage_tabs_message=None ): """ Show the 'Actions' management tab.
"""
""" Show the 'Actions' management tab. actions = []
"""
actions = [] for a in self.listActions():
for a in self.listActions(): a1 = {}
a1['id'] = a.getId()
a1 = {} a1['name'] = a.Title()
a1['id'] = a.getId() p = a.getPermissions()
a1['name'] = a.Title() if p:
p = a.getPermissions() a1['permission'] = p[0]
if p: else:
a1['permission'] = p[0] a1['permission'] = ''
else: a1['category'] = a.getCategory() or 'object'
a1['permission'] = '' a1['visible'] = a.getVisibility()
a1['category'] = a.getCategory() or 'object' a1['action'] = a.getActionExpression()
a1['visible'] = a.getVisibility() a1['condition'] = a.getCondition()
a1['action'] = a.getActionExpression() if hasattr(a, 'getIconExpression') :
a1['condition'] = a.getCondition() a1['icon'] = a.getIconExpression()
if hasattr(a, 'getIconExpression') : if hasattr(a, 'getOption') :
a1['icon'] = a.getIconExpression() a1['optional'] = a.getOption()
if hasattr(a, 'getOption') : actions.append(a1)
a1['optional'] = a.getOption()
actions.append(a1) # possible_permissions is in AccessControl.Role.RoleManager.
pp = self.possible_permissions()
# possible_permissions is in AccessControl.Role.RoleManager. return self._actions_form( self
pp = self.possible_permissions() , REQUEST
return self._actions_form( self , actions=actions
, REQUEST , possible_permissions=pp
, actions=actions , management_view='Actions'
, possible_permissions=pp , manage_tabs_message=manage_tabs_message
, management_view='Actions' )
, manage_tabs_message=manage_tabs_message
)
def ActionProviderBase_addAction( self
, id
def addAction( self , name
, id , action
, name , condition
, action , permission
, condition , category
, permission , icon=None
, category , visible=1
, icon=None , optional=0
, visible=1 , REQUEST=None
, optional=0 ):
, REQUEST=None """ Add an action to our list.
): """
""" Add an action to our list. if not name:
""" raise ValueError('A name is required.')
if not name:
raise ValueError('A name is required.') a_expr = action and Expression(text=str(action)) or ''
i_expr = icon and Expression(text=str(icon)) or ''
a_expr = action and Expression(text=str(action)) or '' c_expr = condition and Expression(text=str(condition)) or ''
i_expr = icon and Expression(text=str(icon)) or ''
c_expr = condition and Expression(text=str(condition)) or '' if type( permission ) != type( () ):
permission = permission and (str(permission),) or ()
if type( permission ) != type( () ):
permission = permission and (str(permission),) or () new_actions = self._cloneActions()
new_actions = self._cloneActions() new_action = ActionInformation( id=str(id)
, title=str(name)
new_action = ActionInformation( id=str(id) , action=a_expr
, title=str(name) , icon=i_expr
, action=a_expr , condition=c_expr
, icon=i_expr , permissions=permission
, condition=c_expr , category=str(category)
, permissions=permission , visible=int(visible)
, category=str(category) , optional=int(optional)
, visible=int(visible) )
, optional=int(optional)
) new_actions.append( new_action )
self._actions = tuple( new_actions )
new_actions.append( new_action )
self._actions = tuple( new_actions ) if REQUEST is not None:
return self.manage_editActionsForm(
if REQUEST is not None: REQUEST, manage_tabs_message='Added.')
return self.manage_editActionsForm(
REQUEST, manage_tabs_message='Added.')
def ActionProviderBase_extractAction( self, properties, index ):
def _extractAction( self, properties, index ): """ Extract an ActionInformation from the funky form properties.
"""
id = str( properties.get( 'id_%d' % index, '' ) )
name = str( properties.get( 'name_%d' % index, '' ) )
action = str( properties.get( 'action_%d' % index, '' ) )
icon = str( properties.get( 'icon_%d' % index, '' ) )
condition = str( properties.get( 'condition_%d' % index, '' ) )
category = str( properties.get( 'category_%d' % index, '' ))
visible = properties.get( 'visible_%d' % index, 0 )
optional = properties.get( 'optional_%d' % index, 0 )
permissions = properties.get( 'permission_%d' % index, () )
if not name:
raise ValueError('A name is required.')
if action is not '':
action = Expression( text=action )
if icon is not '':
icon = Expression( text=icon )
if condition is not '':
condition = Expression( text=condition )
if category == '':
category = 'object'
if type( visible ) is not type( 0 ):
try:
visible = int( visible )
except:
visible = 0
""" Extract an ActionInformation from the funky form properties. if type( optional ) is not type( 0 ):
""" try:
id = str( properties.get( 'id_%d' % index, '' ) ) optional = int( optional )
name = str( properties.get( 'name_%d' % index, '' ) ) except:
action = str( properties.get( 'action_%d' % index, '' ) ) optional = 0
icon = str( properties.get( 'icon_%d' % index, '' ) )
condition = str( properties.get( 'condition_%d' % index, '' ) ) if type( permissions ) is type( '' ):
category = str( properties.get( 'category_%d' % index, '' )) permissions = ( permissions, )
visible = properties.get( 'visible_%d' % index, 0 )
optional = properties.get( 'optional_%d' % index, 0 ) return ActionInformation( id=id
permissions = properties.get( 'permission_%d' % index, () ) , title=name
, action=action
if not name: , icon=icon
raise ValueError('A name is required.') , condition=condition
, permissions=permissions
if action is not '': , category=category
action = Expression( text=action ) , visible=visible
, optional=optional
if icon is not '': )
icon = Expression( text=icon )
ActionProviderBase.manage_editActionsForm = ActionProviderBase_manage_editActionsForm
if condition is not '': ActionProviderBase.addAction = ActionProviderBase_addAction
condition = Expression( text=condition ) ActionProviderBase._extractAction = ActionProviderBase_extractAction
if category == '':
category = 'object'
if type( visible ) is not type( 0 ):
try:
visible = int( visible )
except:
visible = 0
if type( optional ) is not type( 0 ):
try:
optional = int( optional )
except:
optional = 0
if type( permissions ) is type( '' ):
permissions = ( permissions, )
return ActionInformation( id=id
, title=name
, action=action
, icon=icon
, condition=condition
, permissions=permissions
, category=category
, visible=visible
, optional=optional
)
ActionProviderBase.manage_editActionsForm = PatchedActionProviderBase.manage_editActionsForm
ActionProviderBase.addAction = PatchedActionProviderBase.addAction
ActionProviderBase._extractAction = PatchedActionProviderBase._extractAction
...@@ -36,30 +36,30 @@ MembershipTool.membersfolder_id = 'member' ...@@ -36,30 +36,30 @@ MembershipTool.membersfolder_id = 'member'
############################################################################## ##############################################################################
# Import: add rename feature # Import: add rename feature
from OFS.ObjectManager import ObjectManager, customImporters from OFS.ObjectManager import ObjectManager, customImporters
class PatchedObjectManager(ObjectManager):
def _importObjectFromFile(self, filepath, verify=1, set_owner=1, id=None): def ObjectManager_importObjectFromFile(self, filepath, verify=1, set_owner=1, id=None):
#LOG('_importObjectFromFile, filepath',0,filepath) #LOG('_importObjectFromFile, filepath',0,filepath)
# locate a valid connection # locate a valid connection
connection=self._p_jar connection=self._p_jar
obj=self obj=self
while connection is None: while connection is None:
obj=obj.aq_parent obj=obj.aq_parent
connection=obj._p_jar connection=obj._p_jar
ob=connection.importFile( ob=connection.importFile(
filepath, customImporters=customImporters) filepath, customImporters=customImporters)
if verify: self._verifyObjectPaste(ob, validate_src=0) if verify: self._verifyObjectPaste(ob, validate_src=0)
if id is None: if id is None:
id=ob.id id=ob.id
if hasattr(id, 'im_func'): id=id() if hasattr(id, 'im_func'): id=id()
self._setObject(id, ob, set_owner=set_owner) self._setObject(id, ob, set_owner=set_owner)
# try to make ownership implicit if possible in the context # try to make ownership implicit if possible in the context
# that the object was imported into. # that the object was imported into.
ob=self._getOb(id) ob=self._getOb(id)
ob.manage_changeOwnershipType(explicit=0) ob.manage_changeOwnershipType(explicit=0)
ObjectManager._importObjectFromFile=PatchedObjectManager._importObjectFromFile ObjectManager._importObjectFromFile=ObjectManager_importObjectFromFile
############################################################################## ##############################################################################
# Properties # Properties
...@@ -71,148 +71,152 @@ from Products.ERP5Type.ERP5Type import ERP5TypeInformation ...@@ -71,148 +71,152 @@ from Products.ERP5Type.ERP5Type import ERP5TypeInformation
from Products.CMFCore.Expression import Expression from Products.CMFCore.Expression import Expression
class ERP5PropertyManager(PropertyManager): class ERP5PropertyManager(PropertyManager):
"""
This class is only for backward compatibility.
"""
pass
manage_propertiesForm=DTMLFile('dtml/properties', globals(), PropertyManager_manage_propertiesForm=DTMLFile('dtml/properties', globals(),
property_extensible_schema__=1) property_extensible_schema__=1)
def _updateProperty(self, id, value):
# Update the value of an existing property. If value
# is a string, an attempt will be made to convert
# the value to the type of the existing property.
self._wrapperCheck(value)
if not hasattr(self, 'isRADContent'):
if not self.hasProperty(id):
raise 'Bad Request', 'The property %s does not exist' % escape(id)
if type(value)==type(''):
proptype=self.getPropertyType(id) or 'string'
if type_converters.has_key(proptype):
value=type_converters[proptype](value)
#LOG('_updateProperty', 0, 'self = %r, id = %r, value = %r' % (self, id, value))
self._setPropValue(id, value)
def hasProperty(self, id):
"""Return true if object has a property 'id'"""
for p in self.propertyIds():
if id==p:
return 1
return 0
def getProperty(self, id, d=None, evaluate=1):
"""Get the property 'id', returning the optional second
argument or None if no such property is found."""
type = self.getPropertyType(id)
if evaluate and type == 'tales':
value = getattr(self, id)
expression = Expression(value)
econtext = createExpressionContext(self)
return expression(econtext)
elif type:
return getattr(self, id)
return d
def getPropertyType(self, id):
"""Get the type of property 'id', returning None if no
such property exists"""
for md in self._propertyMap():
if md['id']==id:
return md.get('type', 'string')
return None
def _setProperty(self, id, value, type=None):
# for selection and multiple selection properties
# the value argument indicates the select variable
# of the property
if type is None:
# Generate a default type
value_type = type(value)
if value_type in (type([]), type(())):
type = 'lines'
elif value_type is type(1):
type = 'int'
elif value_type is type(1L):
type = 'long'
elif value_type is type(1.0):
type = 'float'
elif value_type is type('a'):
if len(value_type.split('\n')) > 1:
type = 'text'
else:
type = 'string'
else:
type = 'string'
self._wrapperCheck(value)
if not self.valid_property_id(id):
raise 'Bad Request', 'Invalid or duplicate property id'
if type in ('selection', 'multiple selection'):
if not hasattr(self, value):
raise 'Bad Request', 'No select variable %s' % value
self._local_properties=getattr(self, '_local_properties', ()) + (
{'id':id, 'type':type, 'select_variable':value},)
if type=='selection':
self._setPropValue(id, '')
else:
self._setPropValue(id, [])
else:
self._local_properties=getattr(self, '_local_properties', ())+({'id':id,'type':type},)
self._setPropValue(id, value)
def _delProperty(self, id): def PropertyManager_updateProperty(self, id, value):
# Update the value of an existing property. If value
# is a string, an attempt will be made to convert
# the value to the type of the existing property.
self._wrapperCheck(value)
if not hasattr(self, 'isRADContent'):
if not self.hasProperty(id): if not self.hasProperty(id):
raise ValueError, 'The property %s does not exist' % escape(id) raise 'Bad Request', 'The property %s does not exist' % escape(id)
self._delPropValue(id) if type(value)==type(''):
self._local_properties=tuple(filter(lambda i, n=id: i['id'] != n, proptype=self.getPropertyType(id) or 'string'
getattr(self, '_local_properties', ()))) if type_converters.has_key(proptype):
value=type_converters[proptype](value)
def propertyIds(self): #LOG('_updateProperty', 0, 'self = %r, id = %r, value = %r' % (self, id, value))
"""Return a list of property ids """ self._setPropValue(id, value)
return map(lambda i: i['id'], self._propertyMap())
def PropertyManager_hasProperty(self, id):
def propertyValues(self): """Return true if object has a property 'id'"""
"""Return a list of actual property objects """ for p in self.propertyIds():
return map(lambda i,s=self: getattr(s,i['id']), self._propertyMap()) if id==p:
return 1
def propertyItems(self): return 0
"""Return a list of (id,property) tuples """
return map(lambda i,s=self: (i['id'],getattr(s,i['id'])), self._propertyMap()) def PropertyManager_getProperty(self, id, d=None, evaluate=1):
"""Get the property 'id', returning the optional second
def _propertyMap(self): argument or None if no such property is found."""
"""Return a tuple of mappings, giving meta-data for properties """ type = self.getPropertyType(id)
return tuple(list(self._properties) + list(getattr(self, '_local_properties', ()))) if evaluate and type == 'tales':
value = getattr(self, id)
def propdict(self): expression = Expression(value)
dict={} econtext = createExpressionContext(self)
for p in self._propertyMap(): return expression(econtext)
dict[p['id']]=p elif type:
return dict return getattr(self, id)
return d
def manage_addProperty(self, id, value, type, REQUEST=None):
"""Add a new property via the web. Sets a new property with def PropertyManager_getPropertyType(self, id):
the given id, type, and value.""" """Get the type of property 'id', returning None if no
if type_converters.has_key(type): such property exists"""
value=type_converters[type](value) for md in self._propertyMap():
#LOG('manage_addProperty', 0, 'id = %r, value = %r, type = %r, REQUEST = %r' % (id, value, type, REQUEST)) if md['id']==id:
self._setProperty(id.strip(), value, type) return md.get('type', 'string')
if REQUEST is not None: return None
return self.manage_propertiesForm(self, REQUEST)
def PropertyManager_setProperty(self, id, value, type=None):
PropertyManager.manage_addProperty = ERP5PropertyManager.manage_addProperty # for selection and multiple selection properties
PropertyManager.manage_propertiesForm = ERP5PropertyManager.manage_propertiesForm # the value argument indicates the select variable
PropertyManager._updateProperty = ERP5PropertyManager._updateProperty # of the property
PropertyManager.getPropertyType = ERP5PropertyManager.getPropertyType
PropertyManager._setProperty = ERP5PropertyManager._setProperty if type is None:
PropertyManager._delProperty = ERP5PropertyManager._delProperty # Generate a default type
PropertyManager.propertyIds = ERP5PropertyManager.propertyIds value_type = type(value)
PropertyManager.propertyValues = ERP5PropertyManager.propertyValues if value_type in (type([]), type(())):
PropertyManager.propertyItems = ERP5PropertyManager.propertyItems type = 'lines'
PropertyManager._propertyMap = ERP5PropertyManager._propertyMap elif value_type is type(1):
PropertyManager.propdict = ERP5PropertyManager.propdict type = 'int'
PropertyManager.hasProperty = ERP5PropertyManager.hasProperty elif value_type is type(1L):
PropertyManager.getProperty = ERP5PropertyManager.getProperty type = 'long'
ERP5TypeInformation.manage_propertiesForm = ERP5PropertyManager.manage_propertiesForm elif value_type is type(1.0):
type = 'float'
elif value_type is type('a'):
if len(value_type.split('\n')) > 1:
type = 'text'
else:
type = 'string'
else:
type = 'string'
self._wrapperCheck(value)
if not self.valid_property_id(id):
raise 'Bad Request', 'Invalid or duplicate property id'
if type in ('selection', 'multiple selection'):
if not hasattr(self, value):
raise 'Bad Request', 'No select variable %s' % value
self._local_properties=getattr(self, '_local_properties', ()) + (
{'id':id, 'type':type, 'select_variable':value},)
if type=='selection':
self._setPropValue(id, '')
else:
self._setPropValue(id, [])
else:
self._local_properties=getattr(self, '_local_properties', ())+({'id':id,'type':type},)
self._setPropValue(id, value)
def PropertyManager_delProperty(self, id):
if not self.hasProperty(id):
raise ValueError, 'The property %s does not exist' % escape(id)
self._delPropValue(id)
self._local_properties=tuple(filter(lambda i, n=id: i['id'] != n,
getattr(self, '_local_properties', ())))
def PropertyManager_propertyIds(self):
"""Return a list of property ids """
return map(lambda i: i['id'], self._propertyMap())
def PropertyManager_propertyValues(self):
"""Return a list of actual property objects """
return map(lambda i,s=self: getattr(s,i['id']), self._propertyMap())
def PropertyManager_propertyItems(self):
"""Return a list of (id,property) tuples """
return map(lambda i,s=self: (i['id'],getattr(s,i['id'])), self._propertyMap())
def PropertyManager_propertyMap(self):
"""Return a tuple of mappings, giving meta-data for properties """
return tuple(list(self._properties) + list(getattr(self, '_local_properties', ())))
def PropertyManager_propdict(self):
dict={}
for p in self._propertyMap():
dict[p['id']]=p
return dict
def PropertyManager_manage_addProperty(self, id, value, type, REQUEST=None):
"""Add a new property via the web. Sets a new property with
the given id, type, and value."""
if type_converters.has_key(type):
value=type_converters[type](value)
#LOG('manage_addProperty', 0, 'id = %r, value = %r, type = %r, REQUEST = %r' % (id, value, type, REQUEST))
self._setProperty(id.strip(), value, type)
if REQUEST is not None:
return self.manage_propertiesForm(self, REQUEST)
PropertyManager.manage_addProperty = PropertyManager_manage_addProperty
PropertyManager.manage_propertiesForm = PropertyManager_manage_propertiesForm
PropertyManager._updateProperty = PropertyManager_updateProperty
PropertyManager.getPropertyType = PropertyManager_getPropertyType
PropertyManager._setProperty = PropertyManager_setProperty
PropertyManager._delProperty = PropertyManager_delProperty
PropertyManager.propertyIds = PropertyManager_propertyIds
PropertyManager.propertyValues = PropertyManager_propertyValues
PropertyManager.propertyItems = PropertyManager_propertyItems
PropertyManager._propertyMap = PropertyManager_propertyMap
PropertyManager.propdict = PropertyManager_propdict
PropertyManager.hasProperty = PropertyManager_hasProperty
PropertyManager.getProperty = PropertyManager_getProperty
ERP5TypeInformation.manage_propertiesForm = PropertyManager_manage_propertiesForm
from ZPublisher.Converters import type_converters, field2string from ZPublisher.Converters import type_converters, field2string
...@@ -226,54 +230,51 @@ except: Bucket=lambda:{} ...@@ -226,54 +230,51 @@ except: Bucket=lambda:{}
from Shared.DC.ZRDB.Aqueduct import decodestring, parse from Shared.DC.ZRDB.Aqueduct import decodestring, parse
from Shared.DC.ZRDB.DA import DA from Shared.DC.ZRDB.DA import DA
def DA_fromFile(self, filename):
class PatchedDA(DA): """
Read the file and update self
def fromFile(self, filename): """
""" f = file(filename)
Read the file and update self s = f.read()
""" f.close()
f = file(filename) self.fromText(s)
s = f.read()
f.close() def DA_fromText(self, text):
self.fromText(s) """
Read the string 'text' and updates self
def fromText(self, text): """
""" start = text.find('<dtml-comment>')
Read the string 'text' and updates self end = text.find('</dtml-comment>')
""" block = text[start+14:end]
start = text.find('<dtml-comment>') parameters = {}
end = text.find('</dtml-comment>') for line in block.split('\n'):
block = text[start+14:end] pair = line.split(':',1)
parameters = {} if len(pair)!=2:
for line in block.split('\n'): continue
pair = line.split(':',1) parameters[pair[0].strip().lower()]=pair[1].strip()
if len(pair)!=2: # check for required and optional parameters
continue max_rows = parameters.get('max_rows',1000)
parameters[pair[0].strip().lower()]=pair[1].strip() max_cache = parameters.get('max_cache',100)
# check for required and optional parameters cache_time = parameters.get('cache_time',0)
max_rows = parameters.get('max_rows',1000) class_name = parameters.get('class_name','')
max_cache = parameters.get('max_cache',100) class_file = parameters.get('class_file','')
cache_time = parameters.get('cache_time',0) title = parameters.get('title','')
class_name = parameters.get('class_name','') connection_id = parameters.get('connection_id','')
class_file = parameters.get('class_file','') arguments = parameters.get('arguments','')
title = parameters.get('title','') start = text.rfind('<params>')
connection_id = parameters.get('connection_id','') end = text.rfind('</params>')
arguments = parameters.get('arguments','') arguments = text[start+8:end]
start = text.rfind('<params>') template = text[end+9:]
end = text.rfind('</params>') while template.find('\n')==0:
arguments = text[start+8:end] template=template.replace('\n','',1)
template = text[end+9:] self.manage_edit(title=title, connection_id=connection_id,
while template.find('\n')==0: arguments=arguments, template=template)
template=template.replace('\n','',1) self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
self.manage_edit(title=title, connection_id=connection_id,
arguments=arguments, template=template) def DA_manage_FTPget(self):
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file) """Get source for FTP download"""
self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
def manage_FTPget(self): return """<dtml-comment>
"""Get source for FTP download"""
self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
return """<dtml-comment>
title:%s title:%s
connection_id:%s connection_id:%s
max_rows:%s max_rows:%s
...@@ -288,51 +289,51 @@ class_file:%s ...@@ -288,51 +289,51 @@ class_file:%s
self.class_name_, self.class_file_, self.class_name_, self.class_file_,
self.arguments_src, self.src) self.arguments_src, self.src)
# This function doesn't take care about properties by default # This function doesn't take care about properties by default
def PUT(self, REQUEST, RESPONSE): def DA_PUT(self, REQUEST, RESPONSE):
"""Handle put requests""" """Handle put requests"""
if RESPONSE is not None: self.dav__init(REQUEST, RESPONSE) if RESPONSE is not None: self.dav__init(REQUEST, RESPONSE)
if RESPONSE is not None: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1) if RESPONSE is not None: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '') body = REQUEST.get('BODY', '')
m = re.match('\s*<dtml-comment>(.*?)</dtml-comment>\s*\n', body, re.I | re.S) m = re.match('\s*<dtml-comment>(.*?)</dtml-comment>\s*\n', body, re.I | re.S)
if m: if m:
property_src = m.group(1) property_src = m.group(1)
parameters = {} parameters = {}
for line in property_src.split('\n'): for line in property_src.split('\n'):
pair = line.split(':',1) pair = line.split(':',1)
if len(pair)!=2: if len(pair)!=2:
continue continue
parameters[pair[0].strip().lower()]=pair[1].strip() parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters # check for required and optional parameters
max_rows = parameters.get('max_rows',1000) max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100) max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0) cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','') class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','') class_file = parameters.get('class_file','')
title = parameters.get('title','') title = parameters.get('title','')
connection_id = parameters.get('connection_id','') connection_id = parameters.get('connection_id','')
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file) self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
self.title = str(title) self.title = str(title)
self.connection_id = str(connection_id) self.connection_id = str(connection_id)
body = body[m.end():] body = body[m.end():]
m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I | re.S) m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I | re.S)
if m: if m:
self.arguments_src = m.group(1) self.arguments_src = m.group(1)
self._arg=parse(self.arguments_src) self._arg=parse(self.arguments_src)
body = body[m.end():] body = body[m.end():]
template = body template = body
self.src = template self.src = template
self.template=t=self.template_class(template) self.template=t=self.template_class(template)
t.cook() t.cook()
self._v_cache={}, Bucket() self._v_cache={}, Bucket()
if RESPONSE is not None: RESPONSE.setStatus(204) if RESPONSE is not None: RESPONSE.setStatus(204)
return RESPONSE return RESPONSE
DA.fromFile = PatchedDA.fromFile DA.fromFile = DA_fromFile
DA.fromText = PatchedDA.fromText DA.fromText = DA_fromText
DA.manage_FTPget = PatchedDA.manage_FTPget DA.manage_FTPget = DA_manage_FTPget
DA.PUT = PatchedDA.PUT DA.PUT = DA_PUT
############################################################################## ##############################################################################
# Optimized rendering of global actions (cache) # Optimized rendering of global actions (cache)
...@@ -345,78 +346,76 @@ from Products.CMFCore.utils import _getAuthenticatedUser ...@@ -345,78 +346,76 @@ from Products.CMFCore.utils import _getAuthenticatedUser
from time import time from time import time
from Products.ERP5Type.Cache import CachingMethod from Products.ERP5Type.Cache import CachingMethod
class PatchedDCWorkflowDefinition(DCWorkflowDefinition): def DCWorkflowDefinition_listGlobalActions(self, info):
'''
def listGlobalActions(self, info): Allows this workflow to
''' include actions to be displayed in the actions box.
Allows this workflow to Called on every request.
include actions to be displayed in the actions box. Returns the actions to be displayed to the user.
Called on every request. '''
Returns the actions to be displayed to the user. def _listGlobalActions(user=None, id=None, portal_path=None):
''' if not self.worklists:
def _listGlobalActions(user=None, id=None, portal_path=None): return None # Optimization
if not self.worklists: sm = getSecurityManager()
return None # Optimization portal = self._getPortalRoot()
sm = getSecurityManager() res = []
portal = self._getPortalRoot() fmt_data = None
res = [] # We want to display some actions depending on the current date
fmt_data = None # So, we can now put this kind of expression : <= "%(now)s"
# We want to display some actions depending on the current date # May be this patch should be moved to listFilteredActions in the future
# So, we can now put this kind of expression : <= "%(now)s" info.now = DateTime()
# May be this patch should be moved to listFilteredActions in the future for id, qdef in self.worklists.items():
info.now = DateTime() if qdef.actbox_name:
for id, qdef in self.worklists.items(): guard = qdef.guard
if qdef.actbox_name: # Patch for ERP5 by JP Smets in order
guard = qdef.guard # to implement worklists and search of local roles
# Patch for ERP5 by JP Smets in order searchres_len = 0
# to implement worklists and search of local roles var_match_keys = qdef.getVarMatchKeys()
searchres_len = 0 if var_match_keys:
var_match_keys = qdef.getVarMatchKeys() # Check the catalog for items in the worklist.
if var_match_keys: catalog = getToolByName(self, 'portal_catalog')
# Check the catalog for items in the worklist. dict = {}
catalog = getToolByName(self, 'portal_catalog') for k in var_match_keys:
dict = {} v = qdef.getVarMatch(k)
for k in var_match_keys: v_fmt = map(lambda x, info=info: x%info, v)
v = qdef.getVarMatch(k) dict[k] = v_fmt
v_fmt = map(lambda x, info=info: x%info, v)
dict[k] = v_fmt
# Patch for ERP5 by JP Smets in order
# to implement worklists and search of local roles
if not (guard is None or guard.check(sm, self, portal)):
dict['local_roles'] = guard.roles
# Patch to use ZSQLCatalog and get high speed
# LOG("PatchedDCWorkflowDefinition", 0, dict)
searchres_len = int(apply(catalog.countResults, (), dict)[0][0])
if searchres_len == 0:
continue
if fmt_data is None:
fmt_data = TemplateDict()
fmt_data._push(info)
fmt_data._push({'count': searchres_len})
# Patch for ERP5 by JP Smets in order # Patch for ERP5 by JP Smets in order
# to implement worklists and search of local roles # to implement worklists and search of local roles
if dict.has_key('local_roles'): if not (guard is None or guard.check(sm, self, portal)):
fmt_data._push({'local_roles': join(guard.roles,';')}) dict['local_roles'] = guard.roles
else: # Patch to use ZSQLCatalog and get high speed
fmt_data._push({'local_roles': ''}) # LOG("PatchedDCWorkflowDefinition", 0, dict)
res.append((id, {'name': qdef.actbox_name % fmt_data, searchres_len = int(apply(catalog.countResults, (), dict)[0][0])
'url': qdef.actbox_url % fmt_data, if searchres_len == 0:
'worklist_id': id, continue
'workflow_title': self.title, if fmt_data is None:
'workflow_id': self.id, fmt_data = TemplateDict()
'permissions': (), # Predetermined. fmt_data._push(info)
'category': qdef.actbox_category})) fmt_data._push({'count': searchres_len})
fmt_data._pop() # Patch for ERP5 by JP Smets in order
res.sort() # to implement worklists and search of local roles
return map((lambda (id, val): val), res) if dict.has_key('local_roles'):
fmt_data._push({'local_roles': join(guard.roles,';')})
# Return Cache else:
_listGlobalActions = CachingMethod(_listGlobalActions, id='listGlobalActions', cache_duration = 300) fmt_data._push({'local_roles': ''})
user = str(_getAuthenticatedUser(self)) res.append((id, {'name': qdef.actbox_name % fmt_data,
return _listGlobalActions(user=user, id=self.id, portal_path=self._getPortalRoot().getPhysicalPath()) 'url': qdef.actbox_url % fmt_data,
'worklist_id': id,
'workflow_title': self.title,
DCWorkflowDefinition.listGlobalActions = PatchedDCWorkflowDefinition.listGlobalActions 'workflow_id': self.id,
'permissions': (), # Predetermined.
'category': qdef.actbox_category}))
fmt_data._pop()
res.sort()
return map((lambda (id, val): val), res)
# Return Cache
_listGlobalActions = CachingMethod(_listGlobalActions, id='listGlobalActions', cache_duration = 300)
user = str(_getAuthenticatedUser(self))
return _listGlobalActions(user=user, id=self.id, portal_path=self._getPortalRoot().getPhysicalPath())
DCWorkflowDefinition.listGlobalActions = DCWorkflowDefinition_listGlobalActions
############################################################################## ##############################################################################
# Stribger repair of BTreeFolder2 # Stribger repair of BTreeFolder2
...@@ -516,133 +515,131 @@ from AccessControl import ModuleSecurityInfo ...@@ -516,133 +515,131 @@ from AccessControl import ModuleSecurityInfo
ModuleSecurityInfo('Products.DCWorkflow.DCWorkflow').declarePublic('ValidationFailed') ModuleSecurityInfo('Products.DCWorkflow.DCWorkflow').declarePublic('ValidationFailed')
class ERP5DCWorkflowDefinition (DCWorkflowDefinition): def DCWorkflowDefinition_executeTransition(self, ob, tdef=None, kwargs=None):
'''
def _executeTransition(self, ob, tdef=None, kwargs=None): Private method.
''' Puts object in a new state.
Private method. '''
Puts object in a new state. sci = None
''' econtext = None
sci = None moved_exc = None
econtext = None
moved_exc = None # Figure out the old and new states.
old_sdef = self._getWorkflowStateOf(ob)
# Figure out the old and new states. old_state = old_sdef.getId()
old_sdef = self._getWorkflowStateOf(ob) if tdef is None:
old_state = old_sdef.getId() new_state = self.initial_state
if tdef is None: former_status = {}
new_state = self.initial_state else:
former_status = {} new_state = tdef.new_state_id
if not new_state:
# Stay in same state.
new_state = old_state
former_status = self._getStatusOf(ob)
new_sdef = self.states.get(new_state, None)
if new_sdef is None:
raise WorkflowException, (
'Destination state undefined: ' + new_state)
# Execute the "before" script.
before_script_success = 1
if tdef is not None and tdef.script_name:
script = self.scripts[tdef.script_name]
# Pass lots of info to the script in a single parameter.
sci = StateChangeInfo(
ob, self, former_status, tdef, old_sdef, new_sdef, kwargs)
try:
#LOG('_executeTransition', 0, "script = %s, sci = %s" % (repr(script), repr(sci)))
script(sci) # May throw an exception.
except ValidationFailed, validation_exc:
before_script_success = 0
before_script_error_message = validation_exc
except ObjectMoved, moved_exc:
ob = moved_exc.getNewObject()
# Re-raise after transition
# Update variables.
state_values = new_sdef.var_values
if state_values is None: state_values = {}
tdef_exprs = None
if tdef is not None: tdef_exprs = tdef.var_exprs
if tdef_exprs is None: tdef_exprs = {}
status = {}
for id, vdef in self.variables.items():
if not vdef.for_status:
continue
expr = None
if state_values.has_key(id):
value = state_values[id]
elif tdef_exprs.has_key(id):
expr = tdef_exprs[id]
elif not vdef.update_always and former_status.has_key(id):
# Preserve former value
value = former_status[id]
else: else:
new_state = tdef.new_state_id if vdef.default_expr is not None:
if not new_state: expr = vdef.default_expr
# Stay in same state.
new_state = old_state
former_status = self._getStatusOf(ob)
new_sdef = self.states.get(new_state, None)
if new_sdef is None:
raise WorkflowException, (
'Destination state undefined: ' + new_state)
# Execute the "before" script.
before_script_success = 1
if tdef is not None and tdef.script_name:
script = self.scripts[tdef.script_name]
# Pass lots of info to the script in a single parameter.
sci = StateChangeInfo(
ob, self, former_status, tdef, old_sdef, new_sdef, kwargs)
try:
#LOG('_executeTransition', 0, "script = %s, sci = %s" % (repr(script), repr(sci)))
script(sci) # May throw an exception.
except ValidationFailed, validation_exc:
before_script_success = 0
before_script_error_message = validation_exc
except ObjectMoved, moved_exc:
ob = moved_exc.getNewObject()
# Re-raise after transition
# Update variables.
state_values = new_sdef.var_values
if state_values is None: state_values = {}
tdef_exprs = None
if tdef is not None: tdef_exprs = tdef.var_exprs
if tdef_exprs is None: tdef_exprs = {}
status = {}
for id, vdef in self.variables.items():
if not vdef.for_status:
continue
expr = None
if state_values.has_key(id):
value = state_values[id]
elif tdef_exprs.has_key(id):
expr = tdef_exprs[id]
elif not vdef.update_always and former_status.has_key(id):
# Preserve former value
value = former_status[id]
else: else:
if vdef.default_expr is not None: value = vdef.default_value
expr = vdef.default_expr if expr is not None:
else: # Evaluate an expression.
value = vdef.default_value if econtext is None:
if expr is not None: # Lazily create the expression context.
# Evaluate an expression. if sci is None:
if econtext is None: sci = StateChangeInfo(
# Lazily create the expression context. ob, self, former_status, tdef,
if sci is None: old_sdef, new_sdef, kwargs)
sci = StateChangeInfo( econtext = createExprContext(sci)
ob, self, former_status, tdef, value = expr(econtext)
old_sdef, new_sdef, kwargs) status[id] = value
econtext = createExprContext(sci)
value = expr(econtext) # Do not proceed in case of failure of before script
status[id] = value if not before_script_success:
status[self.state_var] = old_state # Remain in state
# Do not proceed in case of failure of before script
if not before_script_success:
status[self.state_var] = old_state # Remain in state
tool = aq_parent(aq_inner(self))
tool.setStatusOf(self.id, ob, status)
sci = StateChangeInfo(
ob, self, status, tdef, old_sdef, new_sdef, kwargs)
sci.setWorkflowVariable(ob, workflow_id=self.id, error_message = before_script_error_message)
return new_sdef
# Update state.
status[self.state_var] = new_state
tool = aq_parent(aq_inner(self)) tool = aq_parent(aq_inner(self))
tool.setStatusOf(self.id, ob, status) tool.setStatusOf(self.id, ob, status)
sci = StateChangeInfo(
# Make sure that the error message is empty. # Why ? ob, self, status, tdef, old_sdef, new_sdef, kwargs)
#sci = StateChangeInfo( sci.setWorkflowVariable(ob, workflow_id=self.id, error_message = before_script_error_message)
# ob, self, status, tdef, old_sdef, new_sdef, kwargs) return new_sdef
#sci.setWorkflowVariable(ob, error_message = '')
# Update state.
# Update role to permission assignments. status[self.state_var] = new_state
self.updateRoleMappingsFor(ob) tool = aq_parent(aq_inner(self))
tool.setStatusOf(self.id, ob, status)
# Execute the "after" script.
if tdef is not None and tdef.after_script_name: # Make sure that the error message is empty. # Why ?
# Script can be either script or workflow method #sci = StateChangeInfo(
#LOG('_executeTransition', 0, 'new_sdef.transitions = %s' % (repr(new_sdef.transitions))) # ob, self, status, tdef, old_sdef, new_sdef, kwargs)
if tdef.after_script_name in filter(lambda k: self.transitions[k].trigger_type == TRIGGER_WORKFLOW_METHOD, #sci.setWorkflowVariable(ob, error_message = '')
new_sdef.transitions):
script = getattr(ob, convertToMixedCase(tdef.after_script_name)) # Update role to permission assignments.
script() self.updateRoleMappingsFor(ob)
else:
script = self.scripts[tdef.after_script_name] # Execute the "after" script.
# Pass lots of info to the script in a single parameter. if tdef is not None and tdef.after_script_name:
sci = StateChangeInfo( # Script can be either script or workflow method
ob, self, status, tdef, old_sdef, new_sdef, kwargs) #LOG('_executeTransition', 0, 'new_sdef.transitions = %s' % (repr(new_sdef.transitions)))
script(sci) # May throw an exception. if tdef.after_script_name in filter(lambda k: self.transitions[k].trigger_type == TRIGGER_WORKFLOW_METHOD,
new_sdef.transitions):
# Return the new state object. script = getattr(ob, convertToMixedCase(tdef.after_script_name))
if moved_exc is not None: script()
# Propagate the notification that the object has moved.
raise moved_exc
else: else:
return new_sdef script = self.scripts[tdef.after_script_name]
# Pass lots of info to the script in a single parameter.
sci = StateChangeInfo(
ob, self, status, tdef, old_sdef, new_sdef, kwargs)
script(sci) # May throw an exception.
# Return the new state object.
if moved_exc is not None:
# Propagate the notification that the object has moved.
raise moved_exc
else:
return new_sdef
DCWorkflowDefinition._executeTransition = ERP5DCWorkflowDefinition._executeTransition DCWorkflowDefinition._executeTransition = DCWorkflowDefinition_executeTransition
# This patch allows to use workflowmethod as an after_script # This patch allows to use workflowmethod as an after_script
# However, the right way of doing would be to have a combined state of TRIGGER_USER_ACTION and TRIGGER_WORKFLOW_METHOD # However, the right way of doing would be to have a combined state of TRIGGER_USER_ACTION and TRIGGER_WORKFLOW_METHOD
...@@ -660,164 +657,164 @@ TransitionDefinition.getAvailableScriptIds = ERP5TransitionDefinition.getAvailab ...@@ -660,164 +657,164 @@ TransitionDefinition.getAvailableScriptIds = ERP5TransitionDefinition.getAvailab
############################################################################## ##############################################################################
# Adding commit_prepare to the zodb transaction # Adding commit_prepare to the zodb transaction
from ZODB import Transaction try:
from ZODB import Transaction
#class ERP5Transaction(Transaction):
hosed = Transaction.hosed
hosed = Transaction.hosed free_transaction = Transaction.free_transaction
free_transaction = Transaction.free_transaction jar_cmp = Transaction.jar_cmp
jar_cmp = Transaction.jar_cmp
def commit(self, subtransaction=None):
def commit(self, subtransaction=None): """Finalize the transaction."""
"""Finalize the transaction.""" objects = self._objects
objects = self._objects
subjars = []
subjars = [] if subtransaction:
if subtransaction: if self._sub is None:
if self._sub is None: # Must store state across multiple subtransactions
# Must store state across multiple subtransactions # so that the final commit can commit all subjars.
# so that the final commit can commit all subjars. self._sub = {}
self._sub = {} else:
else: if self._sub is not None:
if self._sub is not None: # This commit is for a top-level transaction that
# This commit is for a top-level transaction that # has previously committed subtransactions. Do
# has previously committed subtransactions. Do # one last subtransaction commit to clear out the
# one last subtransaction commit to clear out the # current objects, then commit all the subjars.
# current objects, then commit all the subjars. if objects:
if objects: self.commit(1)
self.commit(1) objects = []
objects = [] subjars = self._sub.values()
subjars = self._sub.values() subjars.sort(jar_cmp)
subjars.sort(jar_cmp) self._sub = None
self._sub = None
# If there were any non-subtransaction-aware jars
# If there were any non-subtransaction-aware jars # involved in earlier subtransaction commits, we need
# involved in earlier subtransaction commits, we need # to add them to the list of jars to commit.
# to add them to the list of jars to commit. if self._non_st_objects is not None:
if self._non_st_objects is not None: objects.extend(self._non_st_objects)
objects.extend(self._non_st_objects) self._non_st_objects = None
self._non_st_objects = None
if (objects or subjars) and hosed:
if (objects or subjars) and hosed: # Something really bad happened and we don't
# Something really bad happened and we don't # trust the system state.
# trust the system state. raise POSException.TransactionError, hosed_msg
raise POSException.TransactionError, hosed_msg
# It's important that:
# It's important that: #
# # - Every object in self._objects is either committed or
# - Every object in self._objects is either committed or # aborted.
# aborted. #
# # - For each object that is committed we call tpc_begin on
# - For each object that is committed we call tpc_begin on # it's jar at least once
# it's jar at least once #
# # - For every jar for which we've called tpc_begin on, we
# - For every jar for which we've called tpc_begin on, we # either call tpc_abort or tpc_finish. It is OK to call
# either call tpc_abort or tpc_finish. It is OK to call # these multiple times, as the storage is required to ignore
# these multiple times, as the storage is required to ignore # these calls if tpc_begin has not been called.
# these calls if tpc_begin has not been called. #
# # - That we call tpc_begin() in a globally consistent order,
# - That we call tpc_begin() in a globally consistent order, # so that concurrent transactions involving multiple storages
# so that concurrent transactions involving multiple storages # do not deadlock.
# do not deadlock.
try:
ncommitted = 0
# Do prepare until number of jars is stable - this could
# create infinite loop
jars_len = -1
jars = self._get_jars(objects, subtransaction)
while len(jars) != jars_len:
jars_len = len(jars)
self._commit_prepare(jars, subjars, subtransaction)
jars = self._get_jars(objects, subtransaction)
try: try:
# If not subtransaction, then jars will be modified. ncommitted = 0
self._commit_begin(jars, subjars, subtransaction) # Do prepare until number of jars is stable - this could
ncommitted += self._commit_objects(objects) # create infinite loop
if not subtransaction: jars_len = -1
# Unless this is a really old jar that doesn't jars = self._get_jars(objects, subtransaction)
# implement tpc_vote(), it must raise an exception while len(jars) != jars_len:
# if it can't commit the transaction. jars_len = len(jars)
for jar in jars: self._commit_prepare(jars, subjars, subtransaction)
try: jars = self._get_jars(objects, subtransaction)
vote = jar.tpc_vote
except AttributeError:
pass
else:
vote(self)
# Handle multiple jars separately. If there are
# multiple jars and one fails during the finish, we
# mark this transaction manager as hosed.
if len(jars) == 1:
self._finish_one(jars[0])
else:
self._finish_many(jars)
except:
# Ugh, we got an got an error during commit, so we
# have to clean up. First save the original exception
# in case the cleanup process causes another
# exception.
error = sys.exc_info()
try: try:
self._commit_error(objects, ncommitted, jars, subjars) # If not subtransaction, then jars will be modified.
self._commit_begin(jars, subjars, subtransaction)
ncommitted += self._commit_objects(objects)
if not subtransaction:
# Unless this is a really old jar that doesn't
# implement tpc_vote(), it must raise an exception
# if it can't commit the transaction.
for jar in jars:
try:
vote = jar.tpc_vote
except AttributeError:
pass
else:
vote(self)
# Handle multiple jars separately. If there are
# multiple jars and one fails during the finish, we
# mark this transaction manager as hosed.
if len(jars) == 1:
self._finish_one(jars[0])
else:
self._finish_many(jars)
except: except:
LOG('ZODB', ERROR, # Ugh, we got an got an error during commit, so we
"A storage error occured during transaction " # have to clean up. First save the original exception
"abort. This shouldn't happen.", # in case the cleanup process causes another
error=error) # exception.
raise error[0], error[1], error[2] error = sys.exc_info()
finally: try:
del objects[:] # clear registered self._commit_error(objects, ncommitted, jars, subjars)
if not subtransaction and self._id is not None: except:
free_transaction() LOG('ZODB', ERROR,
"A storage error occured during transaction "
def _commit_prepare(self, jars, subjars, subtransaction): "abort. This shouldn't happen.",
if subtransaction: error=error)
assert not subjars raise error[0], error[1], error[2]
for jar in jars: finally:
try: del objects[:] # clear registered
jar.tpc_prepare(self, subtransaction) if not subtransaction and self._id is not None:
except TypeError: free_transaction()
# Assume that TypeError means that tpc_begin() only
# takes one argument, and that the jar doesn't def _commit_prepare(self, jars, subjars, subtransaction):
# support subtransactions. if subtransaction:
jar.tpc_prepare(self) assert not subjars
except AttributeError: for jar in jars:
# Assume that KeyError means that tpc_prepare try:
# not available jar.tpc_prepare(self, subtransaction)
pass except TypeError:
else: # Assume that TypeError means that tpc_begin() only
# Merge in all the jars used by one of the subtransactions. # takes one argument, and that the jar doesn't
# support subtransactions.
# When the top-level subtransaction commits, the tm must jar.tpc_prepare(self)
# call commit_sub() for each jar involved in one of the except AttributeError:
# subtransactions. The commit_sub() method should call # Assume that KeyError means that tpc_prepare
# tpc_begin() on the storage object. # not available
pass
# It must also call tpc_begin() on jars that were used in else:
# a subtransaction but don't support subtransactions. # Merge in all the jars used by one of the subtransactions.
# These operations must be performed on the jars in order. # When the top-level subtransaction commits, the tm must
# call commit_sub() for each jar involved in one of the
# Modify jars inplace to include the subjars, too. # subtransactions. The commit_sub() method should call
jars += subjars # tpc_begin() on the storage object.
jars.sort(jar_cmp)
# assume that subjars is small, so that it's cheaper to test # It must also call tpc_begin() on jars that were used in
# whether jar in subjars than to make a dict and do has_key. # a subtransaction but don't support subtransactions.
for jar in jars:
#if jar in subjars: # These operations must be performed on the jars in order.
# pass
#else: # Modify jars inplace to include the subjars, too.
try: jars += subjars
jar.tpc_prepare(self) jars.sort(jar_cmp)
except AttributeError: # assume that subjars is small, so that it's cheaper to test
# Assume that KeyError means that tpc_prepare # whether jar in subjars than to make a dict and do has_key.
# not available for jar in jars:
pass #if jar in subjars:
# pass
Transaction.Transaction.commit = commit #else:
Transaction.Transaction._commit_prepare = _commit_prepare try:
jar.tpc_prepare(self)
except AttributeError:
# Assume that KeyError means that tpc_prepare
# not available
pass
Transaction.Transaction.commit = commit
Transaction.Transaction._commit_prepare = _commit_prepare
except ImportError:
pass
############################################################################## ##############################################################################
...@@ -825,77 +822,73 @@ Transaction.Transaction._commit_prepare = _commit_prepare ...@@ -825,77 +822,73 @@ Transaction.Transaction._commit_prepare = _commit_prepare
from Products.CMFCore.WorkflowTool import WorkflowTool from Products.CMFCore.WorkflowTool import WorkflowTool
class ERP5WorkflowTool(WorkflowTool): def WorkflowTool_wrapWorkflowMethod(self, ob, method_id, func, args, kw):
def wrapWorkflowMethod(self, ob, method_id, func, args, kw): """ To be invoked only by WorkflowCore.
Allows a workflow definition to wrap a WorkflowMethod.
""" To be invoked only by WorkflowCore. By default, the workflow tool takes the first workflow wich
Allows a workflow definition to wrap a WorkflowMethod. support the method_id. In ERP5, with Interaction Worfklows, we
may have many workflows wich can support a worfklow method,
that's why we need this patch
By default, the workflow tool takes the first workflow wich We should have 1 or 0 classic workflow (ie a DCWorkflow), and
support the method_id. In ERP5, with Interaction Worfklows, we 0 or many Interaction workflows. We should take care that the
may have many workflows wich can support a worfklow method, method will be called once
that's why we need this patch """
wf_list = []
We should have 1 or 0 classic workflow (ie a DCWorkflow), and wfs = self.getWorkflowsFor(ob)
0 or many Interaction workflows. We should take care that the if wfs:
method will be called once
"""
wf_list = []
wfs = self.getWorkflowsFor(ob)
if wfs:
for w in wfs:
#LOG('ERP5WorkflowTool.wrapWorkflowMethod, is wfMSupported', 0, repr(( w.isWorkflowMethodSupported(ob, method_id), w.getId(), ob, method_id )))
if (hasattr(w, 'isWorkflowMethodSupported')
and w.isWorkflowMethodSupported(ob, method_id)):
#wf = w
#break
wf_list.append(w)
else:
wfs = ()
if len(wf_list)==0:
return apply(func, args, kw)
no_interaction = 0
for w in wf_list:
if w.__class__.__name__ != 'InteractionWorkflowDefinition':
no_interaction = 1
for w in wfs:
w.notifyBefore(ob, method_id, args=args, kw=kw)
# Check if there is at least 1 non interaction workflow
if no_interaction:
for w in wf_list:
if w.__class__.__name__ != 'InteractionWorkflowDefinition':
result = self._invokeWithNotification(
[], ob, method_id, w.wrapWorkflowMethod,
(ob, method_id, func, args, kw), {})
else:
result = apply(func, args, kw)
for w in wfs: for w in wfs:
w.notifySuccess(ob, method_id, result, args=args, kw=kw) #LOG('ERP5WorkflowTool.wrapWorkflowMethod, is wfMSupported', 0, repr(( w.isWorkflowMethodSupported(ob, method_id), w.getId(), ob, method_id )))
if (hasattr(w, 'isWorkflowMethodSupported')
and w.isWorkflowMethodSupported(ob, method_id)):
#wf = w
#break
wf_list.append(w)
else:
wfs = ()
if len(wf_list)==0:
return apply(func, args, kw)
no_interaction = 0
for w in wf_list:
if w.__class__.__name__ != 'InteractionWorkflowDefinition':
no_interaction = 1
for w in wfs:
w.notifyBefore(ob, method_id, args=args, kw=kw)
# Check if there is at least 1 non interaction workflow
if no_interaction:
for w in wf_list:
if w.__class__.__name__ != 'InteractionWorkflowDefinition':
result = self._invokeWithNotification(
[], ob, method_id, w.wrapWorkflowMethod,
(ob, method_id, func, args, kw), {})
else:
result = apply(func, args, kw)
for w in wfs:
w.notifySuccess(ob, method_id, result, args=args, kw=kw)
WorkflowTool.wrapWorkflowMethod = ERP5WorkflowTool.wrapWorkflowMethod WorkflowTool.wrapWorkflowMethod = WorkflowTool_wrapWorkflowMethod
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition
class ERP5DCWorkflow(DCWorkflowDefinition): def DCWorkflowDefinition_notifyBefore(self, ob, action, args=None, kw=None):
'''
def notifyBefore(self, ob, action, args=None, kw=None): Notifies this workflow of an action before it happens,
''' allowing veto by exception. Unless an exception is thrown, either
Notifies this workflow of an action before it happens, a notifySuccess() or notifyException() can be expected later on.
allowing veto by exception. Unless an exception is thrown, either The action usually corresponds to a method name.
a notifySuccess() or notifyException() can be expected later on. '''
The action usually corresponds to a method name. pass
'''
pass
def notifySuccess(self, ob, action, result, args=None, kw=None): def DCWorkflowDefinition_notifySuccess(self, ob, action, result, args=None, kw=None):
''' '''
Notifies this workflow that an action has taken place. Notifies this workflow that an action has taken place.
''' '''
pass pass
DCWorkflowDefinition.notifyBefore = ERP5DCWorkflow.notifyBefore DCWorkflowDefinition.notifyBefore = DCWorkflowDefinition_notifyBefore
DCWorkflowDefinition.notifySuccess = ERP5DCWorkflow.notifySuccess DCWorkflowDefinition.notifySuccess = DCWorkflowDefinition_notifySuccess
############################################################################## ##############################################################################
# Make sure the xml export will be ordered # Make sure the xml export will be ordered
...@@ -903,7 +896,10 @@ DCWorkflowDefinition.notifySuccess = ERP5DCWorkflow.notifySuccess ...@@ -903,7 +896,10 @@ DCWorkflowDefinition.notifySuccess = ERP5DCWorkflow.notifySuccess
from Shared.DC.xml import ppml from Shared.DC.xml import ppml
from base64 import encodestring from base64 import encodestring
from cStringIO import StringIO from cStringIO import StringIO
from ZODB.referencesf import referencesf try:
from ZODB.serialize import referencesf
except ImportError:
from ZODB.referencesf import referencesf
from ZODB.ExportImport import TemporaryFile from ZODB.ExportImport import TemporaryFile
from pickle import Pickler, EMPTY_DICT, MARK, DICT from pickle import Pickler, EMPTY_DICT, MARK, DICT
from cPickle import loads, dumps from cPickle import loads, dumps
...@@ -1911,3 +1907,21 @@ def erp5_new_traverse(request, path, response=None, validated_hook=None): ...@@ -1911,3 +1907,21 @@ def erp5_new_traverse(request, path, response=None, validated_hook=None):
BaseRequest.traverse = erp5_new_traverse BaseRequest.traverse = erp5_new_traverse
######################################################################################
# AttrDict patch for more dict-like methods.
try:
from App.ProductContext import AttrDict
def AttrDict_getitem(self, name):
try:
return getattr(self.ob, name)
except AttributeError:
raise KeyError
def AttrDict_has_key(self, name):
return hasattr(self.ob, name)
AttrDict.__getitem__ = AttrDict_getitem
AttrDict.has_key = AttrDict_has_key
except ImportError:
pass
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