diff --git a/product/ERP5Form/Form.py b/product/ERP5Form/Form.py index f8423ea16d53b874e0de63b86012e7a9ee6c3c51..e5efb1e2b5e612e26221aaa0a7282c674fed81ec 100755 --- a/product/ERP5Form/Form.py +++ b/product/ERP5Form/Form.py @@ -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 diff --git a/product/ERP5Form/FormulatorPatch.py b/product/ERP5Form/FormulatorPatch.py index 3f0dcde469216a0a65344091f89c070de4a09f0c..deccb86c7484b65556974982773d0891b8c4c8aa 100755 --- a/product/ERP5Form/FormulatorPatch.py +++ b/product/ERP5Form/FormulatorPatch.py @@ -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 diff --git a/product/ERP5Form/__init__.py b/product/ERP5Form/__init__.py index 378bfde1aa1c7180fc9202bfff67d9792c6dee1c..14e2a7af962285f615b35341951a275ebaa06a88 100755 --- a/product/ERP5Form/__init__.py +++ b/product/ERP5Form/__init__.py @@ -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) diff --git a/product/ERP5Type/CMFCorePatch.py b/product/ERP5Type/CMFCorePatch.py index e48413196c754c8b545d7878d7b2d2b2f4c5a81e..eca074aa28540eeec29efd9a5ed8e9dd8d8d7ebf 100755 --- a/product/ERP5Type/CMFCorePatch.py +++ b/product/ERP5Type/CMFCorePatch.py @@ -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 diff --git a/product/ERP5Type/ZopePatch.py b/product/ERP5Type/ZopePatch.py index 7562a105bb17e57087b74e3c1308463672fb9355..49ed4a326e8a27050c6a460c176cde8906556eb3 100755 --- a/product/ERP5Type/ZopePatch.py +++ b/product/ERP5Type/ZopePatch.py @@ -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