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
from zLOG import LOG
class ERP5Field(Field):
"""
The ERP5Field provides here, request,
container etc. names to TALES expressions. It is used to dynamically
patch the standard Formulator
"""
security = ClassSecurityInfo()
# this is a field
is_field = 1
security.declareProtected('Access contents information', 'get_value')
def get_value(self, id, **kw):
"""Get value for id."""
# FIXME: backwards compat hack to make sure tales dict exists
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)
def get_value(self, id, **kw):
"""Get value for id."""
# FIXME: backwards compat hack to make sure tales dict exists
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:
# FIXME: backwards compat hack to make sure overrides dict exists
if not hasattr(self, 'overrides'):
self.overrides = {}
override = self.overrides.get(id, "")
if override:
# call wrapped method to get answer
value = override.__of__(self)()
else:
# 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
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 = 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())
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:
# FIXME: backwards compat hack to make sure overrides dict exists
if not hasattr(self, 'overrides'):
self.overrides = {}
override = self.overrides.get(id, "")
if override:
# call wrapped method to get answer
value = override.__of__(self)()
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
Field.get_value = ERP5Field.get_value
Field._get_default = ERP5Field._get_default
Field.om_icons = ERP5Field.om_icons
Field.get_value = get_value
Field._get_default = _get_default
Field.om_icons = om_icons
# Constructors
......
......@@ -26,70 +26,63 @@ from Products.Formulator.Widget import Widget
from AccessControl import ClassSecurityInfo
from zLOG import LOG
class PatchedField(Field):
security = ClassSecurityInfo()
security.declareProtected('Access contents information',
'generate_field_key')
def generate_field_key(self, validation=0, key=None):
"""Generate the key Silva uses to render the field in the form.
"""
# Patched by JPS for ERP5 in order to
# dynamically change the name
if key is not None:
return 'field_%s' % key
if self.field_record is None:
return 'field_%s' % self.id
elif validation:
return self.id
elif isinstance(self.widget, MultiItemsWidget):
return "%s.%s:record:list" % (self.field_record, self.id)
else:
return '%s.%s:record' % (self.field_record, self.id)
security.declareProtected('View', 'render')
def render(self, value=None, REQUEST=None, key=None):
"""Render the field widget.
value -- the value the field should have (for instance
from validation).
REQUEST -- REQUEST can contain raw (unvalidated) field
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)
def Field_generate_field_key(self, validation=0, key=None):
"""Generate the key Silva uses to render the field in the form.
"""
# Patched by JPS for ERP5 in order to
# dynamically change the name
if key is not None:
return 'field_%s' % key
if self.field_record is None:
return 'field_%s' % self.id
elif validation:
return self.id
elif isinstance(self.widget, MultiItemsWidget):
return "%s.%s:record:list" % (self.field_record, self.id)
else:
return '%s.%s:record' % (self.field_record, self.id)
def Field_render(self, value=None, REQUEST=None, key=None):
"""Render the field widget.
value -- the value the field should have (for instance
from validation).
REQUEST -- REQUEST can contain raw (unvalidated) field
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 render_sub_field(self, id, value=None, REQUEST=None, key=None):
"""Render a sub field, as part of complete rendering of widget in
a form. Works like render() but for sub field.
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
"""
return self.sub_form.get_field(id)._render_helper(
self.generate_subfield_key(id, key=key), value, REQUEST)
def Field_render_sub_field(self, id, value=None, REQUEST=None, key=None):
"""Render a sub field, as part of complete rendering of widget in
a form. Works like render() but for sub field.
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
"""
return self.sub_form.get_field(id)._render_helper(
self.generate_subfield_key(id, key=key), value, REQUEST)
def generate_subfield_key(self, id, validation=0, key=None):
"""Generate the key Silva uses to render a sub field.
Added key parameter for ERP5
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
"""
if key is None: key = self.id
if self.field_record is None or validation:
return 'subfield_%s_%s'%(key, id)
return '%s.subfield_%s_%s:record' % (self.field_record, key, id)
def Field_generate_subfield_key(self, id, validation=0, key=None):
"""Generate the key Silva uses to render a sub field.
Added key parameter for ERP5
Added key parameter for ERP5 in order to be compatible with listbox/matrixbox
"""
if key is None: key = self.id
if self.field_record is None or validation:
return 'subfield_%s_%s'%(key, id)
return '%s.subfield_%s_%s:record' % (self.field_record, key, id)
def validate_sub_field(self, id, REQUEST, key=None):
"""Validates a subfield (as part of field validation).
"""
return self.sub_form.get_field(id)._validate_helper(
self.generate_subfield_key(id, validation=1, key=key), REQUEST)
def Field_validate_sub_field(self, id, REQUEST, key=None):
"""Validates a subfield (as part of field validation).
"""
return self.sub_form.get_field(id)._validate_helper(
self.generate_subfield_key(id, validation=1, key=key), REQUEST)
Field.generate_field_key = PatchedField.generate_field_key
Field.render = PatchedField.render
Field.render_sub_field = PatchedField.render_sub_field
Field.generate_subfield_key = PatchedField.generate_subfield_key
Field.validate_sub_field = PatchedField.validate_sub_field
Field.generate_field_key = Field_generate_field_key
Field.render = Field_render
Field.render_sub_field = Field_render_sub_field
Field.generate_subfield_key = Field_generate_subfield_key
Field.validate_sub_field = Field_validate_sub_field
from Products.Formulator.Validator import SelectionValidator
from Products.Formulator.Validator import StringBaseValidator
......@@ -564,25 +557,18 @@ MultiItemsWidget.render_items = MultiItemsWidget_render_items
# JPS - Subfield handling with listbox requires extension
from Products.Formulator.StandardFields import DateTimeField
class PatchedDateTimeField(DateTimeField):
"""
Make sur we test if this REQUEST parameter has a form
attribute. In ERP5, we sometimes use the REQUEST to pass
subobjects to forms.
"""
def _get_default(self, key, value, REQUEST):
if value is not None:
return value
# 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')
def DateTimeField_get_default(self, key, value, REQUEST):
if value is not None:
return value
# 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
......
......@@ -61,7 +61,7 @@ import FormulatorPatch
import psyco
psyco.bind(ListBox.ListBoxWidget.render)
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._exec)
......
......@@ -24,39 +24,37 @@ from Products.CMFCore.FSZSQLMethod import FSZSQLMethod
from Products.CMFCore.DirectoryView import expandpath
from Products.ZSQLMethods.SQL import SQL
class PatchedFSZSQLMethod(FSZSQLMethod):
def _readFile(self, reparse):
fp = expandpath(self._filepath)
file = open(fp, 'r') # not 'rb', as this is a text file!
try:
data = file.read()
finally: file.close()
RESPONSE = {}
RESPONSE['BODY'] = data
self.PUT(RESPONSE,None)
def _createZODBClone(self):
"""Create a ZODB (editable) equivalent of this object."""
# I guess it's bad to 'reach inside' ourselves like this,
# but Z SQL Methods don't have accessor methdods ;-)
s = SQL(self.id,
self.title,
self.connection_id,
self.arguments_src,
self.src)
s.manage_advanced(self.max_rows_,
self.max_cache_,
self.cache_time_,
self.class_name_,
self.class_file_)
return s
FSZSQLMethod._readFile = PatchedFSZSQLMethod._readFile
FSZSQLMethod._createZODBClone = PatchedFSZSQLMethod._createZODBClone
def FSZSQLMethod_readFile(self, reparse):
fp = expandpath(self._filepath)
file = open(fp, 'r') # not 'rb', as this is a text file!
try:
data = file.read()
finally: file.close()
RESPONSE = {}
RESPONSE['BODY'] = data
self.PUT(RESPONSE,None)
def FSZSQLMethod_createZODBClone(self):
"""Create a ZODB (editable) equivalent of this object."""
# I guess it's bad to 'reach inside' ourselves like this,
# but Z SQL Methods don't have accessor methdods ;-)
s = SQL(self.id,
self.title,
self.connection_id,
self.arguments_src,
self.src)
s.manage_advanced(self.max_rows_,
self.max_cache_,
self.cache_time_,
self.class_name_,
self.class_file_)
return s
FSZSQLMethod._readFile = FSZSQLMethod_readFile
FSZSQLMethod._createZODBClone = FSZSQLMethod_createZODBClone
from Products.CMFCore import ActionInformation
from AccessControl import ClassSecurityInfo
......@@ -188,146 +186,144 @@ ActionInformation.ActionInformation = PatchedActionInformation
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.ActionInformation import ActionInformation
class PatchedActionProviderBase(ActionProviderBase):
def manage_editActionsForm( self, REQUEST, manage_tabs_message=None ):
""" Show the 'Actions' management tab.
"""
actions = []
for a in self.listActions():
a1 = {}
a1['id'] = a.getId()
a1['name'] = a.Title()
p = a.getPermissions()
if p:
a1['permission'] = p[0]
else:
a1['permission'] = ''
a1['category'] = a.getCategory() or 'object'
a1['visible'] = a.getVisibility()
a1['action'] = a.getActionExpression()
a1['condition'] = a.getCondition()
if hasattr(a, 'getIconExpression') :
a1['icon'] = a.getIconExpression()
if hasattr(a, 'getOption') :
a1['optional'] = a.getOption()
actions.append(a1)
# possible_permissions is in AccessControl.Role.RoleManager.
pp = self.possible_permissions()
return self._actions_form( self
, REQUEST
, actions=actions
, possible_permissions=pp
, management_view='Actions'
, manage_tabs_message=manage_tabs_message
)
def addAction( self
, id
, name
, action
, condition
, permission
, category
, icon=None
, visible=1
, optional=0
, REQUEST=None
):
""" Add an action to our list.
"""
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 ''
c_expr = condition and Expression(text=str(condition)) or ''
if type( permission ) != type( () ):
permission = permission and (str(permission),) or ()
new_actions = self._cloneActions()
new_action = ActionInformation( id=str(id)
, title=str(name)
, action=a_expr
, icon=i_expr
, condition=c_expr
, permissions=permission
, category=str(category)
, visible=int(visible)
, optional=int(optional)
)
new_actions.append( new_action )
self._actions = tuple( new_actions )
if REQUEST is not None:
return self.manage_editActionsForm(
REQUEST, manage_tabs_message='Added.')
def _extractAction( self, properties, index ):
def ActionProviderBase_manage_editActionsForm( self, REQUEST, manage_tabs_message=None ):
""" Show the 'Actions' management tab.
"""
actions = []
for a in self.listActions():
a1 = {}
a1['id'] = a.getId()
a1['name'] = a.Title()
p = a.getPermissions()
if p:
a1['permission'] = p[0]
else:
a1['permission'] = ''
a1['category'] = a.getCategory() or 'object'
a1['visible'] = a.getVisibility()
a1['action'] = a.getActionExpression()
a1['condition'] = a.getCondition()
if hasattr(a, 'getIconExpression') :
a1['icon'] = a.getIconExpression()
if hasattr(a, 'getOption') :
a1['optional'] = a.getOption()
actions.append(a1)
# possible_permissions is in AccessControl.Role.RoleManager.
pp = self.possible_permissions()
return self._actions_form( self
, REQUEST
, actions=actions
, possible_permissions=pp
, management_view='Actions'
, manage_tabs_message=manage_tabs_message
)
def ActionProviderBase_addAction( self
, id
, name
, action
, condition
, permission
, category
, icon=None
, visible=1
, optional=0
, REQUEST=None
):
""" Add an action to our list.
"""
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 ''
c_expr = condition and Expression(text=str(condition)) or ''
if type( permission ) != type( () ):
permission = permission and (str(permission),) or ()
new_actions = self._cloneActions()
new_action = ActionInformation( id=str(id)
, title=str(name)
, action=a_expr
, icon=i_expr
, condition=c_expr
, permissions=permission
, category=str(category)
, visible=int(visible)
, optional=int(optional)
)
new_actions.append( new_action )
self._actions = tuple( new_actions )
if REQUEST is not None:
return self.manage_editActionsForm(
REQUEST, manage_tabs_message='Added.')
def ActionProviderBase_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.
"""
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
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
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 = ActionProviderBase_manage_editActionsForm
ActionProviderBase.addAction = ActionProviderBase_addAction
ActionProviderBase._extractAction = ActionProviderBase_extractAction
......@@ -36,30 +36,30 @@ MembershipTool.membersfolder_id = 'member'
##############################################################################
# Import: add rename feature
from OFS.ObjectManager import ObjectManager, customImporters
class PatchedObjectManager(ObjectManager):
def _importObjectFromFile(self, filepath, verify=1, set_owner=1, id=None):
#LOG('_importObjectFromFile, filepath',0,filepath)
# locate a valid connection
connection=self._p_jar
obj=self
while connection is None:
obj=obj.aq_parent
connection=obj._p_jar
ob=connection.importFile(
filepath, customImporters=customImporters)
if verify: self._verifyObjectPaste(ob, validate_src=0)
if id is None:
id=ob.id
if hasattr(id, 'im_func'): id=id()
self._setObject(id, ob, set_owner=set_owner)
# try to make ownership implicit if possible in the context
# that the object was imported into.
ob=self._getOb(id)
ob.manage_changeOwnershipType(explicit=0)
ObjectManager._importObjectFromFile=PatchedObjectManager._importObjectFromFile
def ObjectManager_importObjectFromFile(self, filepath, verify=1, set_owner=1, id=None):
#LOG('_importObjectFromFile, filepath',0,filepath)
# locate a valid connection
connection=self._p_jar
obj=self
while connection is None:
obj=obj.aq_parent
connection=obj._p_jar
ob=connection.importFile(
filepath, customImporters=customImporters)
if verify: self._verifyObjectPaste(ob, validate_src=0)
if id is None:
id=ob.id
if hasattr(id, 'im_func'): id=id()
self._setObject(id, ob, set_owner=set_owner)
# try to make ownership implicit if possible in the context
# that the object was imported into.
ob=self._getOb(id)
ob.manage_changeOwnershipType(explicit=0)
ObjectManager._importObjectFromFile=ObjectManager_importObjectFromFile
##############################################################################
# Properties
......@@ -71,148 +71,152 @@ from Products.ERP5Type.ERP5Type import ERP5TypeInformation
from Products.CMFCore.Expression import Expression
class ERP5PropertyManager(PropertyManager):
"""
This class is only for backward compatibility.
"""
pass
manage_propertiesForm=DTMLFile('dtml/properties', globals(),
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'
PropertyManager_manage_propertiesForm=DTMLFile('dtml/properties', globals(),
property_extensible_schema__=1)
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):
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 propertyIds(self):
"""Return a list of property ids """
return map(lambda i: i['id'], self._propertyMap())
def propertyValues(self):
"""Return a list of actual property objects """
return map(lambda i,s=self: getattr(s,i['id']), self._propertyMap())
def propertyItems(self):
"""Return a list of (id,property) tuples """
return map(lambda i,s=self: (i['id'],getattr(s,i['id'])), self._propertyMap())
def _propertyMap(self):
"""Return a tuple of mappings, giving meta-data for properties """
return tuple(list(self._properties) + list(getattr(self, '_local_properties', ())))
def propdict(self):
dict={}
for p in self._propertyMap():
dict[p['id']]=p
return dict
def 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 = ERP5PropertyManager.manage_addProperty
PropertyManager.manage_propertiesForm = ERP5PropertyManager.manage_propertiesForm
PropertyManager._updateProperty = ERP5PropertyManager._updateProperty
PropertyManager.getPropertyType = ERP5PropertyManager.getPropertyType
PropertyManager._setProperty = ERP5PropertyManager._setProperty
PropertyManager._delProperty = ERP5PropertyManager._delProperty
PropertyManager.propertyIds = ERP5PropertyManager.propertyIds
PropertyManager.propertyValues = ERP5PropertyManager.propertyValues
PropertyManager.propertyItems = ERP5PropertyManager.propertyItems
PropertyManager._propertyMap = ERP5PropertyManager._propertyMap
PropertyManager.propdict = ERP5PropertyManager.propdict
PropertyManager.hasProperty = ERP5PropertyManager.hasProperty
PropertyManager.getProperty = ERP5PropertyManager.getProperty
ERP5TypeInformation.manage_propertiesForm = ERP5PropertyManager.manage_propertiesForm
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 PropertyManager_hasProperty(self, id):
"""Return true if object has a property 'id'"""
for p in self.propertyIds():
if id==p:
return 1
return 0
def PropertyManager_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 PropertyManager_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 PropertyManager_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 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
......@@ -226,54 +230,51 @@ except: Bucket=lambda:{}
from Shared.DC.ZRDB.Aqueduct import decodestring, parse
from Shared.DC.ZRDB.DA import DA
class PatchedDA(DA):
def fromFile(self, filename):
"""
Read the file and update self
"""
f = file(filename)
s = f.read()
f.close()
self.fromText(s)
def fromText(self, text):
"""
Read the string 'text' and updates self
"""
start = text.find('<dtml-comment>')
end = text.find('</dtml-comment>')
block = text[start+14:end]
parameters = {}
for line in block.split('\n'):
pair = line.split(':',1)
if len(pair)!=2:
continue
parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters
max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','')
title = parameters.get('title','')
connection_id = parameters.get('connection_id','')
arguments = parameters.get('arguments','')
start = text.rfind('<params>')
end = text.rfind('</params>')
arguments = text[start+8:end]
template = text[end+9:]
while template.find('\n')==0:
template=template.replace('\n','',1)
self.manage_edit(title=title, connection_id=connection_id,
arguments=arguments, template=template)
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
def manage_FTPget(self):
"""Get source for FTP download"""
self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
return """<dtml-comment>
def DA_fromFile(self, filename):
"""
Read the file and update self
"""
f = file(filename)
s = f.read()
f.close()
self.fromText(s)
def DA_fromText(self, text):
"""
Read the string 'text' and updates self
"""
start = text.find('<dtml-comment>')
end = text.find('</dtml-comment>')
block = text[start+14:end]
parameters = {}
for line in block.split('\n'):
pair = line.split(':',1)
if len(pair)!=2:
continue
parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters
max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','')
title = parameters.get('title','')
connection_id = parameters.get('connection_id','')
arguments = parameters.get('arguments','')
start = text.rfind('<params>')
end = text.rfind('</params>')
arguments = text[start+8:end]
template = text[end+9:]
while template.find('\n')==0:
template=template.replace('\n','',1)
self.manage_edit(title=title, connection_id=connection_id,
arguments=arguments, template=template)
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
def DA_manage_FTPget(self):
"""Get source for FTP download"""
self.REQUEST.RESPONSE.setHeader('Content-Type', 'text/plain')
return """<dtml-comment>
title:%s
connection_id:%s
max_rows:%s
......@@ -288,51 +289,51 @@ class_file:%s
self.class_name_, self.class_file_,
self.arguments_src, self.src)
# This function doesn't take care about properties by default
def PUT(self, REQUEST, RESPONSE):
"""Handle put requests"""
if RESPONSE is not None: self.dav__init(REQUEST, RESPONSE)
if RESPONSE is not None: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '')
m = re.match('\s*<dtml-comment>(.*?)</dtml-comment>\s*\n', body, re.I | re.S)
if m:
property_src = m.group(1)
parameters = {}
for line in property_src.split('\n'):
pair = line.split(':',1)
if len(pair)!=2:
continue
parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters
max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','')
title = parameters.get('title','')
connection_id = parameters.get('connection_id','')
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
self.title = str(title)
self.connection_id = str(connection_id)
body = body[m.end():]
m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I | re.S)
if m:
self.arguments_src = m.group(1)
self._arg=parse(self.arguments_src)
body = body[m.end():]
template = body
self.src = template
self.template=t=self.template_class(template)
t.cook()
self._v_cache={}, Bucket()
if RESPONSE is not None: RESPONSE.setStatus(204)
return RESPONSE
DA.fromFile = PatchedDA.fromFile
DA.fromText = PatchedDA.fromText
DA.manage_FTPget = PatchedDA.manage_FTPget
DA.PUT = PatchedDA.PUT
# This function doesn't take care about properties by default
def DA_PUT(self, REQUEST, RESPONSE):
"""Handle put requests"""
if RESPONSE is not None: self.dav__init(REQUEST, RESPONSE)
if RESPONSE is not None: self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
body = REQUEST.get('BODY', '')
m = re.match('\s*<dtml-comment>(.*?)</dtml-comment>\s*\n', body, re.I | re.S)
if m:
property_src = m.group(1)
parameters = {}
for line in property_src.split('\n'):
pair = line.split(':',1)
if len(pair)!=2:
continue
parameters[pair[0].strip().lower()]=pair[1].strip()
# check for required and optional parameters
max_rows = parameters.get('max_rows',1000)
max_cache = parameters.get('max_cache',100)
cache_time = parameters.get('cache_time',0)
class_name = parameters.get('class_name','')
class_file = parameters.get('class_file','')
title = parameters.get('title','')
connection_id = parameters.get('connection_id','')
self.manage_advanced(max_rows, max_cache, cache_time, class_name, class_file)
self.title = str(title)
self.connection_id = str(connection_id)
body = body[m.end():]
m = re.match('\s*<params>(.*)</params>\s*\n', body, re.I | re.S)
if m:
self.arguments_src = m.group(1)
self._arg=parse(self.arguments_src)
body = body[m.end():]
template = body
self.src = template
self.template=t=self.template_class(template)
t.cook()
self._v_cache={}, Bucket()
if RESPONSE is not None: RESPONSE.setStatus(204)
return RESPONSE
DA.fromFile = DA_fromFile
DA.fromText = DA_fromText
DA.manage_FTPget = DA_manage_FTPget
DA.PUT = DA_PUT
##############################################################################
# Optimized rendering of global actions (cache)
......@@ -345,78 +346,76 @@ from Products.CMFCore.utils import _getAuthenticatedUser
from time import time
from Products.ERP5Type.Cache import CachingMethod
class PatchedDCWorkflowDefinition(DCWorkflowDefinition):
def listGlobalActions(self, info):
'''
Allows this workflow to
include actions to be displayed in the actions box.
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:
return None # Optimization
sm = getSecurityManager()
portal = self._getPortalRoot()
res = []
fmt_data = None
# We want to display some actions depending on the current date
# So, we can now put this kind of expression : <= "%(now)s"
# May be this patch should be moved to listFilteredActions in the future
info.now = DateTime()
for id, qdef in self.worklists.items():
if qdef.actbox_name:
guard = qdef.guard
# Patch for ERP5 by JP Smets in order
# to implement worklists and search of local roles
searchres_len = 0
var_match_keys = qdef.getVarMatchKeys()
if var_match_keys:
# Check the catalog for items in the worklist.
catalog = getToolByName(self, 'portal_catalog')
dict = {}
for k in var_match_keys:
v = qdef.getVarMatch(k)
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})
def DCWorkflowDefinition_listGlobalActions(self, info):
'''
Allows this workflow to
include actions to be displayed in the actions box.
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:
return None # Optimization
sm = getSecurityManager()
portal = self._getPortalRoot()
res = []
fmt_data = None
# We want to display some actions depending on the current date
# So, we can now put this kind of expression : <= "%(now)s"
# May be this patch should be moved to listFilteredActions in the future
info.now = DateTime()
for id, qdef in self.worklists.items():
if qdef.actbox_name:
guard = qdef.guard
# Patch for ERP5 by JP Smets in order
# to implement worklists and search of local roles
searchres_len = 0
var_match_keys = qdef.getVarMatchKeys()
if var_match_keys:
# Check the catalog for items in the worklist.
catalog = getToolByName(self, 'portal_catalog')
dict = {}
for k in var_match_keys:
v = qdef.getVarMatch(k)
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 dict.has_key('local_roles'):
fmt_data._push({'local_roles': join(guard.roles,';')})
else:
fmt_data._push({'local_roles': ''})
res.append((id, {'name': qdef.actbox_name % fmt_data,
'url': qdef.actbox_url % fmt_data,
'worklist_id': id,
'workflow_title': self.title,
'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 = PatchedDCWorkflowDefinition.listGlobalActions
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
# to implement worklists and search of local roles
if dict.has_key('local_roles'):
fmt_data._push({'local_roles': join(guard.roles,';')})
else:
fmt_data._push({'local_roles': ''})
res.append((id, {'name': qdef.actbox_name % fmt_data,
'url': qdef.actbox_url % fmt_data,
'worklist_id': id,
'workflow_title': self.title,
'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
......@@ -516,133 +515,131 @@ from AccessControl import ModuleSecurityInfo
ModuleSecurityInfo('Products.DCWorkflow.DCWorkflow').declarePublic('ValidationFailed')
class ERP5DCWorkflowDefinition (DCWorkflowDefinition):
def _executeTransition(self, ob, tdef=None, kwargs=None):
'''
Private method.
Puts object in a new state.
'''
sci = None
econtext = None
moved_exc = None
# Figure out the old and new states.
old_sdef = self._getWorkflowStateOf(ob)
old_state = old_sdef.getId()
if tdef is None:
new_state = self.initial_state
former_status = {}
def DCWorkflowDefinition_executeTransition(self, ob, tdef=None, kwargs=None):
'''
Private method.
Puts object in a new state.
'''
sci = None
econtext = None
moved_exc = None
# Figure out the old and new states.
old_sdef = self._getWorkflowStateOf(ob)
old_state = old_sdef.getId()
if tdef is None:
new_state = self.initial_state
former_status = {}
else:
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:
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]
if vdef.default_expr is not None:
expr = vdef.default_expr
else:
if vdef.default_expr is not None:
expr = vdef.default_expr
else:
value = vdef.default_value
if expr is not None:
# Evaluate an expression.
if econtext is None:
# Lazily create the expression context.
if sci is None:
sci = StateChangeInfo(
ob, self, former_status, tdef,
old_sdef, new_sdef, kwargs)
econtext = createExprContext(sci)
value = expr(econtext)
status[id] = value
# 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
value = vdef.default_value
if expr is not None:
# Evaluate an expression.
if econtext is None:
# Lazily create the expression context.
if sci is None:
sci = StateChangeInfo(
ob, self, former_status, tdef,
old_sdef, new_sdef, kwargs)
econtext = createExprContext(sci)
value = expr(econtext)
status[id] = value
# 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)
# Make sure that the error message is empty. # Why ?
#sci = StateChangeInfo(
# ob, self, status, tdef, old_sdef, new_sdef, kwargs)
#sci.setWorkflowVariable(ob, error_message = '')
# Update role to permission assignments.
self.updateRoleMappingsFor(ob)
# Execute the "after" script.
if tdef is not None and tdef.after_script_name:
# Script can be either script or workflow method
#LOG('_executeTransition', 0, 'new_sdef.transitions = %s' % (repr(new_sdef.transitions)))
if tdef.after_script_name in filter(lambda k: self.transitions[k].trigger_type == TRIGGER_WORKFLOW_METHOD,
new_sdef.transitions):
script = getattr(ob, convertToMixedCase(tdef.after_script_name))
script()
else:
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
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.setStatusOf(self.id, ob, status)
# Make sure that the error message is empty. # Why ?
#sci = StateChangeInfo(
# ob, self, status, tdef, old_sdef, new_sdef, kwargs)
#sci.setWorkflowVariable(ob, error_message = '')
# Update role to permission assignments.
self.updateRoleMappingsFor(ob)
# Execute the "after" script.
if tdef is not None and tdef.after_script_name:
# Script can be either script or workflow method
#LOG('_executeTransition', 0, 'new_sdef.transitions = %s' % (repr(new_sdef.transitions)))
if tdef.after_script_name in filter(lambda k: self.transitions[k].trigger_type == TRIGGER_WORKFLOW_METHOD,
new_sdef.transitions):
script = getattr(ob, convertToMixedCase(tdef.after_script_name))
script()
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
# 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
##############################################################################
# Adding commit_prepare to the zodb transaction
from ZODB import Transaction
#class ERP5Transaction(Transaction):
hosed = Transaction.hosed
free_transaction = Transaction.free_transaction
jar_cmp = Transaction.jar_cmp
def commit(self, subtransaction=None):
"""Finalize the transaction."""
objects = self._objects
subjars = []
if subtransaction:
if self._sub is None:
# Must store state across multiple subtransactions
# so that the final commit can commit all subjars.
self._sub = {}
else:
if self._sub is not None:
# This commit is for a top-level transaction that
# has previously committed subtransactions. Do
# one last subtransaction commit to clear out the
# current objects, then commit all the subjars.
if objects:
self.commit(1)
objects = []
subjars = self._sub.values()
subjars.sort(jar_cmp)
self._sub = None
# If there were any non-subtransaction-aware jars
# involved in earlier subtransaction commits, we need
# to add them to the list of jars to commit.
if self._non_st_objects is not None:
objects.extend(self._non_st_objects)
self._non_st_objects = None
if (objects or subjars) and hosed:
# Something really bad happened and we don't
# trust the system state.
raise POSException.TransactionError, hosed_msg
# It's important that:
#
# - Every object in self._objects is either committed or
# aborted.
#
# - For each object that is committed we call tpc_begin on
# it's jar at least once
#
# - For every jar for which we've called tpc_begin on, we
# either call tpc_abort or tpc_finish. It is OK to call
# these multiple times, as the storage is required to ignore
# these calls if tpc_begin has not been called.
#
# - That we call tpc_begin() in a globally consistent order,
# so that concurrent transactions involving multiple storages
# 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:
from ZODB import Transaction
hosed = Transaction.hosed
free_transaction = Transaction.free_transaction
jar_cmp = Transaction.jar_cmp
def commit(self, subtransaction=None):
"""Finalize the transaction."""
objects = self._objects
subjars = []
if subtransaction:
if self._sub is None:
# Must store state across multiple subtransactions
# so that the final commit can commit all subjars.
self._sub = {}
else:
if self._sub is not None:
# This commit is for a top-level transaction that
# has previously committed subtransactions. Do
# one last subtransaction commit to clear out the
# current objects, then commit all the subjars.
if objects:
self.commit(1)
objects = []
subjars = self._sub.values()
subjars.sort(jar_cmp)
self._sub = None
# If there were any non-subtransaction-aware jars
# involved in earlier subtransaction commits, we need
# to add them to the list of jars to commit.
if self._non_st_objects is not None:
objects.extend(self._non_st_objects)
self._non_st_objects = None
if (objects or subjars) and hosed:
# Something really bad happened and we don't
# trust the system state.
raise POSException.TransactionError, hosed_msg
# It's important that:
#
# - Every object in self._objects is either committed or
# aborted.
#
# - For each object that is committed we call tpc_begin on
# it's jar at least once
#
# - For every jar for which we've called tpc_begin on, we
# either call tpc_abort or tpc_finish. It is OK to call
# these multiple times, as the storage is required to ignore
# these calls if tpc_begin has not been called.
#
# - That we call tpc_begin() in a globally consistent order,
# so that concurrent transactions involving multiple storages
# do not deadlock.
try:
# 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:
# 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()
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:
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:
LOG('ZODB', ERROR,
"A storage error occured during transaction "
"abort. This shouldn't happen.",
error=error)
raise error[0], error[1], error[2]
finally:
del objects[:] # clear registered
if not subtransaction and self._id is not None:
free_transaction()
def _commit_prepare(self, jars, subjars, subtransaction):
if subtransaction:
assert not subjars
for jar in jars:
try:
jar.tpc_prepare(self, subtransaction)
except TypeError:
# Assume that TypeError means that tpc_begin() only
# takes one argument, and that the jar doesn't
# support subtransactions.
jar.tpc_prepare(self)
except AttributeError:
# Assume that KeyError means that tpc_prepare
# not available
pass
else:
# Merge in all the jars used by one of the subtransactions.
# When the top-level subtransaction commits, the tm must
# call commit_sub() for each jar involved in one of the
# subtransactions. The commit_sub() method should call
# tpc_begin() on the storage object.
# It must also call tpc_begin() on jars that were used in
# a subtransaction but don't support subtransactions.
# These operations must be performed on the jars in order.
# Modify jars inplace to include the subjars, too.
jars += subjars
jars.sort(jar_cmp)
# assume that subjars is small, so that it's cheaper to test
# whether jar in subjars than to make a dict and do has_key.
for jar in jars:
#if jar in subjars:
# pass
#else:
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
# 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:
self._commit_error(objects, ncommitted, jars, subjars)
except:
LOG('ZODB', ERROR,
"A storage error occured during transaction "
"abort. This shouldn't happen.",
error=error)
raise error[0], error[1], error[2]
finally:
del objects[:] # clear registered
if not subtransaction and self._id is not None:
free_transaction()
def _commit_prepare(self, jars, subjars, subtransaction):
if subtransaction:
assert not subjars
for jar in jars:
try:
jar.tpc_prepare(self, subtransaction)
except TypeError:
# Assume that TypeError means that tpc_begin() only
# takes one argument, and that the jar doesn't
# support subtransactions.
jar.tpc_prepare(self)
except AttributeError:
# Assume that KeyError means that tpc_prepare
# not available
pass
else:
# Merge in all the jars used by one of the subtransactions.
# When the top-level subtransaction commits, the tm must
# call commit_sub() for each jar involved in one of the
# subtransactions. The commit_sub() method should call
# tpc_begin() on the storage object.
# It must also call tpc_begin() on jars that were used in
# a subtransaction but don't support subtransactions.
# These operations must be performed on the jars in order.
# Modify jars inplace to include the subjars, too.
jars += subjars
jars.sort(jar_cmp)
# assume that subjars is small, so that it's cheaper to test
# whether jar in subjars than to make a dict and do has_key.
for jar in jars:
#if jar in subjars:
# pass
#else:
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
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.
Allows a workflow definition to wrap a WorkflowMethod.
By default, the workflow tool takes the first workflow wich
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
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
We should have 1 or 0 classic workflow (ie a DCWorkflow), and
0 or many Interaction workflows. We should take care that the
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)
We should have 1 or 0 classic workflow (ie a DCWorkflow), and
0 or many Interaction workflows. We should take care that the
method will be called once
"""
wf_list = []
wfs = self.getWorkflowsFor(ob)
if 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
class ERP5DCWorkflow(DCWorkflowDefinition):
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
a notifySuccess() or notifyException() can be expected later on.
The action usually corresponds to a method name.
'''
pass
def DCWorkflowDefinition_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
a notifySuccess() or notifyException() can be expected later on.
The action usually corresponds to a method name.
'''
pass
def notifySuccess(self, ob, action, result, args=None, kw=None):
'''
Notifies this workflow that an action has taken place.
'''
pass
def DCWorkflowDefinition_notifySuccess(self, ob, action, result, args=None, kw=None):
'''
Notifies this workflow that an action has taken place.
'''
pass
DCWorkflowDefinition.notifyBefore = ERP5DCWorkflow.notifyBefore
DCWorkflowDefinition.notifySuccess = ERP5DCWorkflow.notifySuccess
DCWorkflowDefinition.notifyBefore = DCWorkflowDefinition_notifyBefore
DCWorkflowDefinition.notifySuccess = DCWorkflowDefinition_notifySuccess
##############################################################################
# Make sure the xml export will be ordered
......@@ -903,7 +896,10 @@ DCWorkflowDefinition.notifySuccess = ERP5DCWorkflow.notifySuccess
from Shared.DC.xml import ppml
from base64 import encodestring
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 pickle import Pickler, EMPTY_DICT, MARK, DICT
from cPickle import loads, dumps
......@@ -1911,3 +1907,21 @@ def erp5_new_traverse(request, path, response=None, validated_hook=None):
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