Improved SAP interface and added historization of fields.
This commit is contained in:
		
							parent
							
								
									b888f8149b
								
							
						
					
					
						commit
						d320a369c9
					
				
					 13 changed files with 362 additions and 182 deletions
				
			
		| 
						 | 
					@ -47,7 +47,7 @@ class Type:
 | 
				
			||||||
    def __init__(self, validator, multiplicity, index, default, optional,
 | 
					    def __init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                 editDefault, show, page, group, move, indexed, searchable,
 | 
					                 editDefault, show, page, group, move, indexed, searchable,
 | 
				
			||||||
                 specificReadPermission, specificWritePermission, width,
 | 
					                 specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                 height, master, masterValue, focus):
 | 
					                 height, master, masterValue, focus, historized):
 | 
				
			||||||
        # The validator restricts which values may be defined. It can be an
 | 
					        # The validator restricts which values may be defined. It can be an
 | 
				
			||||||
        # interval (1,None), a list of string values ['choice1', 'choice2'],
 | 
					        # interval (1,None), a list of string values ['choice1', 'choice2'],
 | 
				
			||||||
        # a regular expression, a custom function, a Selection instance, etc.
 | 
					        # a regular expression, a custom function, a Selection instance, etc.
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,9 @@ class Type:
 | 
				
			||||||
        # If a field must retain attention in a particular way, set focus=True.
 | 
					        # If a field must retain attention in a particular way, set focus=True.
 | 
				
			||||||
        # It will be rendered in a special way.
 | 
					        # It will be rendered in a special way.
 | 
				
			||||||
        self.focus = focus
 | 
					        self.focus = focus
 | 
				
			||||||
 | 
					        # If we must keep track of changes performed on a field, "historized"
 | 
				
			||||||
 | 
					        # must be set to True.
 | 
				
			||||||
 | 
					        self.historized = historized
 | 
				
			||||||
        self.id = id(self)
 | 
					        self.id = id(self)
 | 
				
			||||||
        self.type = self.__class__.__name__
 | 
					        self.type = self.__class__.__name__
 | 
				
			||||||
        self.pythonType = None # The True corresponding Python type
 | 
					        self.pythonType = None # The True corresponding Python type
 | 
				
			||||||
| 
						 | 
					@ -128,11 +131,11 @@ class Integer(Type):
 | 
				
			||||||
                 page='main', group=None, move=0, indexed=False,
 | 
					                 page='main', group=None, move=0, indexed=False,
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 master=None, masterValue=None, focus=False):
 | 
					                 master=None, masterValue=None, focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, searchable,
 | 
					                      editDefault, show, page, group, move, indexed, searchable,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.pythonType = long
 | 
					        self.pythonType = long
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Float(Type):
 | 
					class Float(Type):
 | 
				
			||||||
| 
						 | 
					@ -141,11 +144,11 @@ class Float(Type):
 | 
				
			||||||
                 page='main', group=None, move=0, indexed=False,
 | 
					                 page='main', group=None, move=0, indexed=False,
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 master=None, masterValue=None, focus=False):
 | 
					                 master=None, masterValue=None, focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, False,
 | 
					                      editDefault, show, page, group, move, indexed, False,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.pythonType = float
 | 
					        self.pythonType = float
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class String(Type):
 | 
					class String(Type):
 | 
				
			||||||
| 
						 | 
					@ -249,11 +252,11 @@ class String(Type):
 | 
				
			||||||
                 show=True, page='main', group=None, move=0, indexed=False,
 | 
					                 show=True, page='main', group=None, move=0, indexed=False,
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 master=None, masterValue=None, focus=False):
 | 
					                 master=None, masterValue=None, focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, searchable,
 | 
					                      editDefault, show, page, group, move, indexed, searchable,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.format = format
 | 
					        self.format = format
 | 
				
			||||||
        self.isSelect = self.isSelection()
 | 
					        self.isSelect = self.isSelection()
 | 
				
			||||||
    def isSelection(self):
 | 
					    def isSelection(self):
 | 
				
			||||||
| 
						 | 
					@ -276,11 +279,11 @@ class Boolean(Type):
 | 
				
			||||||
                 page='main', group=None, move=0, indexed=False,
 | 
					                 page='main', group=None, move=0, indexed=False,
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 master=None, masterValue=None, focus=False):
 | 
					                 master=None, masterValue=None, focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, searchable,
 | 
					                      editDefault, show, page, group, move, indexed, searchable,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.pythonType = bool
 | 
					        self.pythonType = bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Date(Type):
 | 
					class Date(Type):
 | 
				
			||||||
| 
						 | 
					@ -294,11 +297,11 @@ class Date(Type):
 | 
				
			||||||
                 group=None, move=0, indexed=False, searchable=False,
 | 
					                 group=None, move=0, indexed=False, searchable=False,
 | 
				
			||||||
                 specificReadPermission=False, specificWritePermission=False,
 | 
					                 specificReadPermission=False, specificWritePermission=False,
 | 
				
			||||||
                 width=None, height=None, master=None, masterValue=None,
 | 
					                 width=None, height=None, master=None, masterValue=None,
 | 
				
			||||||
                 focus=False):
 | 
					                 focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, searchable,
 | 
					                      editDefault, show, page, group, move, indexed, searchable,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.format = format
 | 
					        self.format = format
 | 
				
			||||||
        self.startYear = startYear
 | 
					        self.startYear = startYear
 | 
				
			||||||
        self.endYear = endYear
 | 
					        self.endYear = endYear
 | 
				
			||||||
| 
						 | 
					@ -309,11 +312,12 @@ class File(Type):
 | 
				
			||||||
                 page='main', group=None, move=0, indexed=False,
 | 
					                 page='main', group=None, move=0, indexed=False,
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 master=None, masterValue=None, focus=False, isImage=False):
 | 
					                 master=None, masterValue=None, focus=False, historized=False,
 | 
				
			||||||
 | 
					                 isImage=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, False,
 | 
					                      editDefault, show, page, group, move, indexed, False,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.isImage = isImage
 | 
					        self.isImage = isImage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Ref(Type):
 | 
					class Ref(Type):
 | 
				
			||||||
| 
						 | 
					@ -325,11 +329,11 @@ class Ref(Type):
 | 
				
			||||||
                 maxPerPage=30, move=0, indexed=False, searchable=False,
 | 
					                 maxPerPage=30, move=0, indexed=False, searchable=False,
 | 
				
			||||||
                 specificReadPermission=False, specificWritePermission=False,
 | 
					                 specificReadPermission=False, specificWritePermission=False,
 | 
				
			||||||
                 width=None, height=None, master=None, masterValue=None,
 | 
					                 width=None, height=None, master=None, masterValue=None,
 | 
				
			||||||
                 focus=False):
 | 
					                 focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, validator, multiplicity, index, default, optional,
 | 
				
			||||||
                      editDefault, show, page, group, move, indexed, False,
 | 
					                      editDefault, show, page, group, move, indexed, False,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.klass = klass
 | 
					        self.klass = klass
 | 
				
			||||||
        self.attribute = attribute
 | 
					        self.attribute = attribute
 | 
				
			||||||
        self.add = add # May the user add new objects through this ref ?
 | 
					        self.add = add # May the user add new objects through this ref ?
 | 
				
			||||||
| 
						 | 
					@ -356,11 +360,11 @@ class Computed(Type):
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 method=None, plainText=True, master=None, masterValue=None,
 | 
					                 method=None, plainText=True, master=None, masterValue=None,
 | 
				
			||||||
                 focus=False):
 | 
					                 focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, None, multiplicity, index, default, optional,
 | 
					        Type.__init__(self, None, multiplicity, index, default, optional,
 | 
				
			||||||
                      False, show, page, group, move, indexed, False,
 | 
					                      False, show, page, group, move, indexed, False,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.method = method # The method used for computing the field value
 | 
					        self.method = method # The method used for computing the field value
 | 
				
			||||||
        self.plainText = plainText # Does field computation produce pain text
 | 
					        self.plainText = plainText # Does field computation produce pain text
 | 
				
			||||||
        # or XHTML?
 | 
					        # or XHTML?
 | 
				
			||||||
| 
						 | 
					@ -376,11 +380,11 @@ class Action(Type):
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 action=None, result='computation', master=None,
 | 
					                 action=None, result='computation', master=None,
 | 
				
			||||||
                 masterValue=None, focus=False):
 | 
					                 masterValue=None, focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, None, (0,1), index, default, optional,
 | 
					        Type.__init__(self, None, (0,1), index, default, optional,
 | 
				
			||||||
                      False, show, page, group, move, indexed, False,
 | 
					                      False, show, page, group, move, indexed, False,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
        self.action = action # Can be a single method or a list/tuple of methods
 | 
					        self.action = action # Can be a single method or a list/tuple of methods
 | 
				
			||||||
        self.result = result # 'computation' means that the action will simply
 | 
					        self.result = result # 'computation' means that the action will simply
 | 
				
			||||||
        # compute things and redirect the user to the same page, with some
 | 
					        # compute things and redirect the user to the same page, with some
 | 
				
			||||||
| 
						 | 
					@ -425,11 +429,11 @@ class Info(Type):
 | 
				
			||||||
                 page='main', group=None, move=0, indexed=False,
 | 
					                 page='main', group=None, move=0, indexed=False,
 | 
				
			||||||
                 searchable=False, specificReadPermission=False,
 | 
					                 searchable=False, specificReadPermission=False,
 | 
				
			||||||
                 specificWritePermission=False, width=None, height=None,
 | 
					                 specificWritePermission=False, width=None, height=None,
 | 
				
			||||||
                 master=None, masterValue=None, focus=False):
 | 
					                 master=None, masterValue=None, focus=False, historized=False):
 | 
				
			||||||
        Type.__init__(self, None, (0,1), index, default, optional,
 | 
					        Type.__init__(self, None, (0,1), index, default, optional,
 | 
				
			||||||
                      False, show, page, group, move, indexed, False,
 | 
					                      False, show, page, group, move, indexed, False,
 | 
				
			||||||
                      specificReadPermission, specificWritePermission, width,
 | 
					                      specificReadPermission, specificWritePermission, width,
 | 
				
			||||||
                      height, master, masterValue, focus)
 | 
					                      height, master, masterValue, focus, historized)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Workflow-specific types ------------------------------------------------------
 | 
					# Workflow-specific types ------------------------------------------------------
 | 
				
			||||||
class State:
 | 
					class State:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -91,6 +91,9 @@ class Generator(AbstractGenerator):
 | 
				
			||||||
        poMsg = msg(app, '', app); poMsg.produceNiceDefault()
 | 
					        poMsg = msg(app, '', app); poMsg.produceNiceDefault()
 | 
				
			||||||
        self.labels += [poMsg,
 | 
					        self.labels += [poMsg,
 | 
				
			||||||
            msg('workflow_state', '', msg.WORKFLOW_STATE),
 | 
					            msg('workflow_state', '', msg.WORKFLOW_STATE),
 | 
				
			||||||
 | 
					            msg('data_change', '', msg.DATA_CHANGE),
 | 
				
			||||||
 | 
					            msg('modified_field', '', msg.MODIFIED_FIELD),
 | 
				
			||||||
 | 
					            msg('previous_value', '', msg.PREVIOUS_VALUE),
 | 
				
			||||||
            msg('phase', '', msg.PHASE),
 | 
					            msg('phase', '', msg.PHASE),
 | 
				
			||||||
            msg('root_type', '', msg.ROOT_TYPE),
 | 
					            msg('root_type', '', msg.ROOT_TYPE),
 | 
				
			||||||
            msg('workflow_comment', '', msg.WORKFLOW_COMMENT),
 | 
					            msg('workflow_comment', '', msg.WORKFLOW_COMMENT),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,14 @@ def afterTest(test):
 | 
				
			||||||
    exec 'from Products.%s import numberOfExecutedTests' % appName
 | 
					    exec 'from Products.%s import numberOfExecutedTests' % appName
 | 
				
			||||||
    if cov and (numberOfExecutedTests == totalNumberOfTests):
 | 
					    if cov and (numberOfExecutedTests == totalNumberOfTests):
 | 
				
			||||||
        cov.stop()
 | 
					        cov.stop()
 | 
				
			||||||
        # Dumps the coverage report
 | 
					 | 
				
			||||||
        appModules = test.getNonEmptySubModules(appName)
 | 
					        appModules = test.getNonEmptySubModules(appName)
 | 
				
			||||||
 | 
					        # Dumps the coverage report
 | 
				
			||||||
 | 
					        # HTML version
 | 
				
			||||||
        cov.html_report(directory=covFolder, morfs=appModules)
 | 
					        cov.html_report(directory=covFolder, morfs=appModules)
 | 
				
			||||||
 | 
					        # Summary in a text file
 | 
				
			||||||
 | 
					        f = file('%s/summary.txt' % covFolder, 'w')
 | 
				
			||||||
 | 
					        cov.report(file=f, morfs=appModules)
 | 
				
			||||||
 | 
					        f.close()
 | 
				
			||||||
 | 
					        # Annotated modules
 | 
				
			||||||
 | 
					        cov.annotate(directory=covFolder, morfs=appModules)
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,16 +31,14 @@ class AbstractMixin:
 | 
				
			||||||
        if created:
 | 
					        if created:
 | 
				
			||||||
            obj = self.portal_factory.doCreate(self, self.id) # portal_factory
 | 
					            obj = self.portal_factory.doCreate(self, self.id) # portal_factory
 | 
				
			||||||
            # creates the final object from the temp object.
 | 
					            # creates the final object from the temp object.
 | 
				
			||||||
        if created and (obj._appy_meta_type == 'tool'):
 | 
					        previousData = None
 | 
				
			||||||
            # We are in the special case where the tool itself is being created.
 | 
					        if not created: previousData = self.rememberPreviousData()
 | 
				
			||||||
            # In this case, we do not process form data.
 | 
					        # We do not process form data (=real update on the object) if the tool
 | 
				
			||||||
            pass
 | 
					        # itself is being created.
 | 
				
			||||||
        else:
 | 
					        if obj._appy_meta_type != 'tool': obj.processForm()
 | 
				
			||||||
            obj.processForm()
 | 
					        if previousData:
 | 
				
			||||||
 | 
					            # Keep in history potential changes on historized fields
 | 
				
			||||||
        # Get the current language and put it in the request
 | 
					            self.historizeData(previousData)
 | 
				
			||||||
        #if rq.form.has_key('current_lang'):
 | 
					 | 
				
			||||||
        #    rq.form['language'] = rq.form.get('current_lang')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Manage references
 | 
					        # Manage references
 | 
				
			||||||
        obj._appy_manageRefs(created)
 | 
					        obj._appy_manageRefs(created)
 | 
				
			||||||
| 
						 | 
					@ -145,15 +143,79 @@ class AbstractMixin:
 | 
				
			||||||
        self.plone_utils.addPortalMessage(msg)
 | 
					        self.plone_utils.addPortalMessage(msg)
 | 
				
			||||||
        self.goto(rq['HTTP_REFERER'])
 | 
					        self.goto(rq['HTTP_REFERER'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def rememberPreviousData(self):
 | 
				
			||||||
 | 
					        '''This method is called before updating an object and remembers, for
 | 
				
			||||||
 | 
					           every historized field, the previous value. Result is a dict
 | 
				
			||||||
 | 
					           ~{s_fieldName: previousFieldValue}~'''
 | 
				
			||||||
 | 
					        res = {}
 | 
				
			||||||
 | 
					        for atField in self.Schema().filterFields(isMetadata=0):
 | 
				
			||||||
 | 
					            fieldName = atField.getName()
 | 
				
			||||||
 | 
					            appyType = self.getAppyType(fieldName, asDict=False)
 | 
				
			||||||
 | 
					            if appyType and appyType.historized:
 | 
				
			||||||
 | 
					                res[fieldName] = (getattr(self, fieldName),
 | 
				
			||||||
 | 
					                                  atField.widget.label_msgid)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def historizeData(self, previousData):
 | 
				
			||||||
 | 
					        '''Records in the object history potential changes on historized fields.
 | 
				
			||||||
 | 
					           p_previousData contains the values, before an update, of the
 | 
				
			||||||
 | 
					           historized fields, while p_self already contains the (potentially)
 | 
				
			||||||
 | 
					           modified values.'''
 | 
				
			||||||
 | 
					        # Remove from previousData all values that were not changed
 | 
				
			||||||
 | 
					        for fieldName in previousData.keys():
 | 
				
			||||||
 | 
					            if getattr(self, fieldName) == previousData[fieldName][0]:
 | 
				
			||||||
 | 
					                del previousData[fieldName]
 | 
				
			||||||
 | 
					        if previousData:
 | 
				
			||||||
 | 
					            # Create the event to add in the history
 | 
				
			||||||
 | 
					            DateTime = self.getProductConfig().DateTime
 | 
				
			||||||
 | 
					            state = self.portal_workflow.getInfoFor(self, 'review_state')
 | 
				
			||||||
 | 
					            user = self.portal_membership.getAuthenticatedMember()
 | 
				
			||||||
 | 
					            event = {'action': '_datachange_', 'changes': previousData,
 | 
				
			||||||
 | 
					                     'review_state': state, 'actor': user.id,
 | 
				
			||||||
 | 
					                     'time': DateTime(), 'comments': ''}
 | 
				
			||||||
 | 
					            # Add the event to the history
 | 
				
			||||||
 | 
					            histKey = self.workflow_history.keys()[0]
 | 
				
			||||||
 | 
					            self.workflow_history[histKey] += (event,)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def goto(self, url):
 | 
					    def goto(self, url):
 | 
				
			||||||
        '''Brings the user to some p_url after an action has been executed.'''
 | 
					        '''Brings the user to some p_url after an action has been executed.'''
 | 
				
			||||||
        return self.REQUEST.RESPONSE.redirect(url)
 | 
					        return self.REQUEST.RESPONSE.redirect(url)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getAppyAttribute(self, name):
 | 
					    def getAppyValue(self, name, appyType=None, useParamValue=False,value=None):
 | 
				
			||||||
        '''Returns method or attribute value corresponding to p_name.'''
 | 
					        '''Returns the value of field (or method) p_name for this object
 | 
				
			||||||
        return eval('self.%s' % name)
 | 
					           (p_self). If p_appyType (the corresponding Appy type) is provided,
 | 
				
			||||||
 | 
					           it gives additional information about the way to render the value.
 | 
				
			||||||
 | 
					           If p_useParamValue is True, the method uses p_value instead of the
 | 
				
			||||||
 | 
					           real field value (useful for rendering a value from the object
 | 
				
			||||||
 | 
					           history, for example).'''
 | 
				
			||||||
 | 
					        # Which value will we use ?
 | 
				
			||||||
 | 
					        if useParamValue: v = value
 | 
				
			||||||
 | 
					        else: v = eval('self.%s' % name)
 | 
				
			||||||
 | 
					        if not appyType: return v
 | 
				
			||||||
 | 
					        if (v == None) or (v == ''): return v
 | 
				
			||||||
 | 
					        vType = appyType['type']
 | 
				
			||||||
 | 
					        if vType == 'Date':
 | 
				
			||||||
 | 
					            res = v.strftime('%d/%m/') + str(v.year())
 | 
				
			||||||
 | 
					            if appyType['format'] == 0:
 | 
				
			||||||
 | 
					                res += ' %s' % v.strftime('%H:%M')
 | 
				
			||||||
 | 
					            return res
 | 
				
			||||||
 | 
					        elif vType == 'String':
 | 
				
			||||||
 | 
					            if not v: return v
 | 
				
			||||||
 | 
					            if appyType['isSelect']:
 | 
				
			||||||
 | 
					                maxMult = appyType['multiplicity'][1]
 | 
				
			||||||
 | 
					                t = self.translate
 | 
				
			||||||
 | 
					                if (maxMult == None) or (maxMult > 1):
 | 
				
			||||||
 | 
					                    return [t('%s_%s_list_%s' % (self.meta_type, name, e)) \
 | 
				
			||||||
 | 
					                            for e in v]
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    return t('%s_%s_list_%s' % (self.meta_type, name, v))
 | 
				
			||||||
 | 
					            return v
 | 
				
			||||||
 | 
					        elif vType == 'Boolean':
 | 
				
			||||||
 | 
					            if v: return self.translate('yes', domain='plone')
 | 
				
			||||||
 | 
					            else: return self.translate('no', domain='plone')
 | 
				
			||||||
 | 
					        return v
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getAppyType(self, fieldName, forward=True):
 | 
					    def getAppyType(self, fieldName, forward=True, asDict=True):
 | 
				
			||||||
        '''Returns the Appy type corresponding to p_fieldName. If you want to
 | 
					        '''Returns the Appy type corresponding to p_fieldName. If you want to
 | 
				
			||||||
           get the Appy type corresponding to a backward field, set p_forward
 | 
					           get the Appy type corresponding to a backward field, set p_forward
 | 
				
			||||||
           to False and specify the corresponding Archetypes relationship in
 | 
					           to False and specify the corresponding Archetypes relationship in
 | 
				
			||||||
| 
						 | 
					@ -166,22 +228,27 @@ class AbstractMixin:
 | 
				
			||||||
                try:
 | 
					                try:
 | 
				
			||||||
                    # If I get the attr on self instead of baseClass, I get the
 | 
					                    # If I get the attr on self instead of baseClass, I get the
 | 
				
			||||||
                    # property field that is redefined at the wrapper level.
 | 
					                    # property field that is redefined at the wrapper level.
 | 
				
			||||||
                    appyType = getattr(baseClass, fieldName)
 | 
					                    res = appyType = getattr(baseClass, fieldName)
 | 
				
			||||||
                    res = self._appy_getTypeAsDict(fieldName, appyType, baseClass)
 | 
					                    if asDict:
 | 
				
			||||||
 | 
					                        res = self._appy_getTypeAsDict(
 | 
				
			||||||
 | 
					                            fieldName, appyType, baseClass)
 | 
				
			||||||
                except AttributeError:
 | 
					                except AttributeError:
 | 
				
			||||||
                    # Check for another parent
 | 
					                    # Check for another parent
 | 
				
			||||||
                    if self.wrapperClass.__bases__[0].__bases__:
 | 
					                    if self.wrapperClass.__bases__[0].__bases__:
 | 
				
			||||||
                        baseClass = self.wrapperClass.__bases__[0].__bases__[-1]
 | 
					                        baseClass = self.wrapperClass.__bases__[0].__bases__[-1]
 | 
				
			||||||
                        try:
 | 
					                        try:
 | 
				
			||||||
                            appyType = getattr(baseClass, fieldName)
 | 
					                            res = appyType = getattr(baseClass, fieldName)
 | 
				
			||||||
                            res = self._appy_getTypeAsDict(fieldName, appyType,
 | 
					                            if asDict:
 | 
				
			||||||
                                                           baseClass)
 | 
					                                res = self._appy_getTypeAsDict(
 | 
				
			||||||
 | 
					                                    fieldName, appyType, baseClass)
 | 
				
			||||||
                        except AttributeError:
 | 
					                        except AttributeError:
 | 
				
			||||||
                            pass
 | 
					                            pass
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            referers = self.getProductConfig().referers
 | 
					            referers = self.getProductConfig().referers
 | 
				
			||||||
            for appyType, rel in referers[self.__class__.__name__]:
 | 
					            for appyType, rel in referers[self.__class__.__name__]:
 | 
				
			||||||
                if rel == fieldName:
 | 
					                if rel == fieldName:
 | 
				
			||||||
 | 
					                    res = appyType
 | 
				
			||||||
 | 
					                    if asDict:
 | 
				
			||||||
                        res = appyType.__dict__
 | 
					                        res = appyType.__dict__
 | 
				
			||||||
                        res['backd'] = appyType.back.__dict__
 | 
					                        res['backd'] = appyType.back.__dict__
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
| 
						 | 
					@ -357,8 +424,7 @@ class AbstractMixin:
 | 
				
			||||||
        groups = {} # The already encountered groups
 | 
					        groups = {} # The already encountered groups
 | 
				
			||||||
        for fieldDescr in self._appy_getOrderedFields(isEdit):
 | 
					        for fieldDescr in self._appy_getOrderedFields(isEdit):
 | 
				
			||||||
            # Select only widgets shown on current page
 | 
					            # Select only widgets shown on current page
 | 
				
			||||||
            if fieldDescr.page != page:
 | 
					            if fieldDescr.page != page: continue
 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
            # Do not take into account hidden fields and fields that can't be
 | 
					            # Do not take into account hidden fields and fields that can't be
 | 
				
			||||||
            # edited through the edit view
 | 
					            # edited through the edit view
 | 
				
			||||||
            if not self.showField(fieldDescr, isEdit): continue
 | 
					            if not self.showField(fieldDescr, isEdit): continue
 | 
				
			||||||
| 
						 | 
					@ -561,6 +627,27 @@ class AbstractMixin:
 | 
				
			||||||
                res = '%s_%s' % (wf.id, res)
 | 
					                res = '%s_%s' % (wf.id, res)
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def hasHistory(self):
 | 
				
			||||||
 | 
					        '''Has this object an history?'''
 | 
				
			||||||
 | 
					        if hasattr(self.aq_base, 'workflow_history') and self.workflow_history:
 | 
				
			||||||
 | 
					            key = self.workflow_history.keys()[0]
 | 
				
			||||||
 | 
					            for event in self.workflow_history[key]:
 | 
				
			||||||
 | 
					                if event['action'] and (event['comments'] != '_invisible_'):
 | 
				
			||||||
 | 
					                    return True
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getHistory(self, startNumber=0, reverse=True, includeInvisible=False):
 | 
				
			||||||
 | 
					        '''Returns the history for this object, sorted in reverse order (most
 | 
				
			||||||
 | 
					           recent change first) if p_reverse is True.'''
 | 
				
			||||||
 | 
					        batchSize = 3
 | 
				
			||||||
 | 
					        key = self.workflow_history.keys()[0]
 | 
				
			||||||
 | 
					        history = list(self.workflow_history[key][1:])
 | 
				
			||||||
 | 
					        if not includeInvisible:
 | 
				
			||||||
 | 
					            history = [e for e in history if e['comments'] != '_invisible_']
 | 
				
			||||||
 | 
					        if reverse: history.reverse()
 | 
				
			||||||
 | 
					        return {'events': history[startNumber:startNumber+batchSize],
 | 
				
			||||||
 | 
					                'totalNumber': len(history), 'batchSize':batchSize}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getComputedValue(self, appyType):
 | 
					    def getComputedValue(self, appyType):
 | 
				
			||||||
        '''Computes on p_self the value of the Computed field corresponding to
 | 
					        '''Computes on p_self the value of the Computed field corresponding to
 | 
				
			||||||
           p_appyType.'''
 | 
					           p_appyType.'''
 | 
				
			||||||
| 
						 | 
					@ -1080,7 +1167,7 @@ class AbstractMixin:
 | 
				
			||||||
        params = ''
 | 
					        params = ''
 | 
				
			||||||
        rq = self.REQUEST
 | 
					        rq = self.REQUEST
 | 
				
			||||||
        for k, v in kwargs.iteritems(): params += '&%s=%s' % (k, v)
 | 
					        for k, v in kwargs.iteritems(): params += '&%s=%s' % (k, v)
 | 
				
			||||||
        params = params[1:]
 | 
					        if params: params = params[1:]
 | 
				
			||||||
        if t == 'showRef':
 | 
					        if t == 'showRef':
 | 
				
			||||||
            chunk = '/skyn/ajax?objectUid=%s&page=ref&' \
 | 
					            chunk = '/skyn/ajax?objectUid=%s&page=ref&' \
 | 
				
			||||||
                'macro=showReferenceContent&' % self.UID()
 | 
					                'macro=showReferenceContent&' % self.UID()
 | 
				
			||||||
| 
						 | 
					@ -1088,6 +1175,11 @@ class AbstractMixin:
 | 
				
			||||||
            if rq.has_key(startKey) and not kwargs.has_key(startKey):
 | 
					            if rq.has_key(startKey) and not kwargs.has_key(startKey):
 | 
				
			||||||
                params += '&%s=%s' % (startKey, rq[startKey])
 | 
					                params += '&%s=%s' % (startKey, rq[startKey])
 | 
				
			||||||
            return baseUrl + chunk + params
 | 
					            return baseUrl + chunk + params
 | 
				
			||||||
 | 
					        elif t == 'showHistory':
 | 
				
			||||||
 | 
					            chunk = '/skyn/ajax?objectUid=%s&page=macros¯o=history' % \
 | 
				
			||||||
 | 
					                self.UID()
 | 
				
			||||||
 | 
					            if params: params = '&' + params
 | 
				
			||||||
 | 
					            return baseUrl + chunk + params
 | 
				
			||||||
        else: # We consider t=='view'
 | 
					        else: # We consider t=='view'
 | 
				
			||||||
            return baseUrl + '/skyn/view' + params
 | 
					            return baseUrl + '/skyn/view' + params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@
 | 
				
			||||||
                  dummy2 python:response.setHeader('Expires', 'Mon, 11 Dec 1975 12:05:05 GMT');
 | 
					                  dummy2 python:response.setHeader('Expires', 'Mon, 11 Dec 1975 12:05:05 GMT');
 | 
				
			||||||
                  dummy3 python:response.setHeader('CacheControl', 'no-cache')">
 | 
					                  dummy3 python:response.setHeader('CacheControl', 'no-cache')">
 | 
				
			||||||
  <tal:executeAction condition="action">
 | 
					  <tal:executeAction condition="action">
 | 
				
			||||||
    <tal:do define="dummy python: contextObj.getAppyAttribute('on'+action)()" omit-tag=""/>
 | 
					    <tal:do define="dummy python: contextObj.getAppyValue('on'+action)()" omit-tag=""/>
 | 
				
			||||||
  </tal:executeAction>
 | 
					  </tal:executeAction>
 | 
				
			||||||
  <metal:callMacro use-macro="python: context.get(page).macros.get(macro)"/>
 | 
					  <metal:callMacro use-macro="python: context.get(page).macros.get(macro)"/>
 | 
				
			||||||
</tal:ajax>
 | 
					</tal:ajax>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,4 +12,4 @@ else:
 | 
				
			||||||
        from Products.CMFCore.utils import getToolByName
 | 
					        from Products.CMFCore.utils import getToolByName
 | 
				
			||||||
        portal = getToolByName(obj, 'portal_url').getPortalObject()
 | 
					        portal = getToolByName(obj, 'portal_url').getPortalObject()
 | 
				
			||||||
        obj = portal.get('portal_%s' % obj.id.lower()) # The tool
 | 
					        obj = portal.get('portal_%s' % obj.id.lower()) # The tool
 | 
				
			||||||
return obj.getAppyAttribute('on'+action)()
 | 
					return obj.getAppyValue('on'+action)()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,15 +116,13 @@
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<metal:showDate define-macro="showDateField"
 | 
					<metal:showDate define-macro="showDateField"
 | 
				
			||||||
                tal:define="v python: field.getAccessor(contextObj)()">
 | 
					                tal:define="v python: contextObj.getAppyValue(field.getName(), appyType)">
 | 
				
			||||||
  <span tal:condition="showLabel" tal:content="label" class="appyLabel"></span>
 | 
					  <span tal:condition="showLabel" tal:content="label" class="appyLabel"></span>
 | 
				
			||||||
  <span tal:condition="v" tal:content="python: v.strftime('%d/%m/') + str(v.year())"></span>
 | 
					  <span tal:replace="v"></span>
 | 
				
			||||||
  <span tal:condition="python: v and (appyType['format'] == 0)"
 | 
					 | 
				
			||||||
        tal:content="python: v.strftime('%H:%M')"></span>
 | 
					 | 
				
			||||||
</metal:showDate>
 | 
					</metal:showDate>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<metal:showString define-macro="showStringField"
 | 
					<metal:showString define-macro="showStringField"
 | 
				
			||||||
                  tal:define="v python: field.getAccessor(contextObj)();
 | 
					                  tal:define="v python: contextObj.getAppyValue(field.getName(), appyType);
 | 
				
			||||||
                              fmt python: appyType['format'];
 | 
					                              fmt python: appyType['format'];
 | 
				
			||||||
                              maxMult python: appyType['multiplicity'][1];
 | 
					                              maxMult python: appyType['multiplicity'][1];
 | 
				
			||||||
                              severalValues python: (maxMult == None) or (maxMult > 1)">
 | 
					                              severalValues python: (maxMult == None) or (maxMult > 1)">
 | 
				
			||||||
| 
						 | 
					@ -132,30 +130,12 @@
 | 
				
			||||||
    <span tal:condition="showLabel" tal:content="label" class="appyLabel"
 | 
					    <span tal:condition="showLabel" tal:content="label" class="appyLabel"
 | 
				
			||||||
          tal:attributes="class python: 'appyLabel ' + contextObj.getCssClasses(appyType, asSlave=False);
 | 
					          tal:attributes="class python: 'appyLabel ' + contextObj.getCssClasses(appyType, asSlave=False);
 | 
				
			||||||
                          id python: v"></span>
 | 
					                          id python: v"></span>
 | 
				
			||||||
    <tal:severalValues condition="python: v and severalValues">
 | 
					    <ul class="appyList" tal:condition="python: v and severalValues">
 | 
				
			||||||
    <ul class="appyList">
 | 
					      <li class="appyBullet" tal:repeat="sv v"><i tal:content="structure sv"></i></li>
 | 
				
			||||||
      <tal:items repeat="sv v">
 | 
					 | 
				
			||||||
      <tal:select condition="appyType/isSelect">
 | 
					 | 
				
			||||||
      <li class="appyBullet">
 | 
					 | 
				
			||||||
        <i tal:content="python: tool.translate('%s_%s_list_%s' % (contextObj.meta_type, field.getName(), sv))"></i>
 | 
					 | 
				
			||||||
      </li>
 | 
					 | 
				
			||||||
      </tal:select>
 | 
					 | 
				
			||||||
      <tal:string condition="not: appyType/isSelect">
 | 
					 | 
				
			||||||
      <li class="appyBullet"><i tal:content="sv"></i></li>
 | 
					 | 
				
			||||||
      </tal:string>
 | 
					 | 
				
			||||||
      </tal:items>
 | 
					 | 
				
			||||||
    </ul>
 | 
					    </ul>
 | 
				
			||||||
    </tal:severalValues>
 | 
					 | 
				
			||||||
    <tal:singleValue condition="python: v and not severalValues">
 | 
					    <tal:singleValue condition="python: v and not severalValues">
 | 
				
			||||||
      <tal:select condition="appyType/isSelect">
 | 
					      <span tal:condition="python: fmt != 3" tal:replace="structure v"/>
 | 
				
			||||||
        <span tal:replace="python: tool.translate('%s_%s_list_%s' % (contextObj.meta_type, field.getName(), v))"/>
 | 
					      <span tal:condition="python: fmt == 3">********</span>
 | 
				
			||||||
      </tal:select>
 | 
					 | 
				
			||||||
      <tal:noSelect condition="python: not appyType['isSelect'] and (fmt != 3)">
 | 
					 | 
				
			||||||
        <span tal:replace="structure v"/>
 | 
					 | 
				
			||||||
      </tal:noSelect>
 | 
					 | 
				
			||||||
      <tal:password condition="python: not appyType['isSelect'] and (fmt == 3)">
 | 
					 | 
				
			||||||
        ********
 | 
					 | 
				
			||||||
      </tal:password>
 | 
					 | 
				
			||||||
    </tal:singleValue>
 | 
					    </tal:singleValue>
 | 
				
			||||||
  </tal:simpleString>
 | 
					  </tal:simpleString>
 | 
				
			||||||
  <tal:formattedString condition="python: fmt not in (0, 3)">
 | 
					  <tal:formattedString condition="python: fmt not in (0, 3)">
 | 
				
			||||||
| 
						 | 
					@ -275,7 +255,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<metal:fields define-macro="listFields"
 | 
					<metal:fields define-macro="listFields"
 | 
				
			||||||
     tal:repeat="widgetDescr python: contextObj.getAppyFields(isEdit, pageName)">
 | 
					     tal:repeat="widgetDescr python: contextObj.getAppyFields(isEdit, pageName)">
 | 
				
			||||||
 | 
					 | 
				
			||||||
    <tal:displayArchetypesField condition="python: widgetDescr['widgetType'] == 'field'">
 | 
					    <tal:displayArchetypesField condition="python: widgetDescr['widgetType'] == 'field'">
 | 
				
			||||||
      <tal:atField condition="python: widgetDescr['page'] == pageName">
 | 
					      <tal:atField condition="python: widgetDescr['page'] == pageName">
 | 
				
			||||||
        <metal:field use-macro="here/skyn/macros/macros/showArchetypesField" />
 | 
					        <metal:field use-macro="here/skyn/macros/macros/showArchetypesField" />
 | 
				
			||||||
| 
						 | 
					@ -293,63 +272,65 @@
 | 
				
			||||||
    </tal:displayGroup>
 | 
					    </tal:displayGroup>
 | 
				
			||||||
</metal:fields>
 | 
					</metal:fields>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<span metal:define-macro="byline"
 | 
					<metal:history define-macro="history"
 | 
				
			||||||
     tal:condition="python: site_properties.allowAnonymousViewAbout or not isAnon"
 | 
					      tal:define="startNumber request/startNumber|python:0;
 | 
				
			||||||
     tal:define="creator here/Creator;" class="documentByLine">
 | 
					                  startNumber python: int(startNumber);
 | 
				
			||||||
  <tal:name tal:condition="creator"
 | 
					                  historyInfo python: contextObj.getHistory(startNumber);
 | 
				
			||||||
            tal:define="author python:contextObj.portal_membership.getMemberInfo(creator)">
 | 
					                  objs        historyInfo/events;
 | 
				
			||||||
    <span class="documentAuthor" i18n:domain="plone" i18n:translate="label_by_author">
 | 
					                  batchSize   historyInfo/batchSize;
 | 
				
			||||||
      by <a tal:attributes="href string:${portal_url}/author/${creator}"
 | 
					                  totalNumber historyInfo/totalNumber;
 | 
				
			||||||
            tal:content="python:author and author['fullname'] or creator"
 | 
					                  ajaxHookId  python:'appyHistory';
 | 
				
			||||||
            tal:omit-tag="not:author" i18n:name="author"/>
 | 
					                  baseUrl     python: contextObj.getUrl('showHistory', startNumber='**v**');
 | 
				
			||||||
      —
 | 
					                  tool        contextObj/getTool">
 | 
				
			||||||
    </span>
 | 
					 | 
				
			||||||
  </tal:name>
 | 
					 | 
				
			||||||
  <span class="documentModified">
 | 
					 | 
				
			||||||
    <span i18n:translate="box_last_modified" i18n:domain="plone"/>
 | 
					 | 
				
			||||||
    <span tal:replace="python:toLocalizedTime(here.ModificationDate(),long_format=1)"/>
 | 
					 | 
				
			||||||
  </span>
 | 
					 | 
				
			||||||
</span>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
<span metal:define-macro="workflowHistory" class="reviewHistory"
 | 
					  <tal:comment replace="nothing">Table containing the history</tal:comment>
 | 
				
			||||||
      tal:define="history contextObj/getWorkflowHistory" tal:condition="history">
 | 
					  <tal:history condition="objs">
 | 
				
			||||||
  <dl id="history" class="collapsible inline collapsedOnLoad">
 | 
					  <metal:nav use-macro="here/skyn/macros/macros/appyNavigate"/>
 | 
				
			||||||
    <dt class="collapsibleHeader" i18n:translate="label_history" i18n:domain="plone">History</dt>
 | 
					  <table width="100%" class="listing nosort">
 | 
				
			||||||
    <dd class="collapsibleContent">
 | 
					 | 
				
			||||||
    <table width="100%" class="listing nosort" i18n:attributes="summary summary_review_history"
 | 
					 | 
				
			||||||
           tal:define="review_history python:contextObj.portal_workflow.getInfoFor(contextObj, 'review_history', []);
 | 
					 | 
				
			||||||
                       review_history python:[review for review in review_history if review.get('action','')]"
 | 
					 | 
				
			||||||
           tal:condition="review_history">
 | 
					 | 
				
			||||||
    <tr i18n:domain="plone">
 | 
					    <tr i18n:domain="plone">
 | 
				
			||||||
      <th i18n:translate="listingheader_action"/>
 | 
					      <th i18n:translate="listingheader_action"/>
 | 
				
			||||||
      <th i18n:translate="listingheader_performed_by"/>
 | 
					      <th i18n:translate="listingheader_performed_by"/>
 | 
				
			||||||
      <th i18n:translate="listingheader_date_and_time"/>
 | 
					      <th i18n:translate="listingheader_date_and_time"/>
 | 
				
			||||||
      <th i18n:translate="listingheader_comment"/>
 | 
					      <th i18n:translate="listingheader_comment"/>
 | 
				
			||||||
    </tr>
 | 
					    </tr>
 | 
				
			||||||
        <metal:block tal:define="review_history python: portal.reverseList(review_history);"
 | 
					    <tal:event repeat="event objs">
 | 
				
			||||||
                     tal:repeat="items review_history">
 | 
					    <tr tal:define="odd repeat/event/odd;
 | 
				
			||||||
        <tr tal:define="odd repeat/items/odd;
 | 
					                    rhComments event/comments|nothing;
 | 
				
			||||||
                        rhComments items/comments|nothing;
 | 
					                    state event/review_state|nothing;
 | 
				
			||||||
                        state items/review_state|nothing"
 | 
					                    isDataChange python: event['action'] == '_datachange_'"
 | 
				
			||||||
            tal:condition="python: items['action'] and (rhComments != '_invisible_')"
 | 
					        tal:attributes="class python:test(odd, 'even', 'odd')" valign="top">
 | 
				
			||||||
            tal:attributes="class python:test(odd, 'even', 'odd')">
 | 
					      <td tal:condition="isDataChange" tal:content="python: tool.translate('data_change')"></td>
 | 
				
			||||||
            
 | 
					      <td tal:condition="not: isDataChange"
 | 
				
			||||||
            <td tal:content="python: tool.translate(contextObj.getWorkflowLabel(items['action']))"
 | 
					          tal:content="python: tool.translate(contextObj.getWorkflowLabel(event['action']))"
 | 
				
			||||||
          tal:attributes="class string:state-${state}"/>
 | 
					          tal:attributes="class string:state-${state}"/>
 | 
				
			||||||
            <td tal:define="actorid python:items.get('actor');
 | 
					      <td tal:define="actorid python:event.get('actor');
 | 
				
			||||||
                      actor python:contextObj.portal_membership.getMemberInfo(actorid);
 | 
					                      actor python:contextObj.portal_membership.getMemberInfo(actorid);
 | 
				
			||||||
                      fullname actor/fullname|nothing;
 | 
					                      fullname actor/fullname|nothing;
 | 
				
			||||||
                      username actor/username|nothing"
 | 
					                      username actor/username|nothing"
 | 
				
			||||||
          tal:content="python:fullname or username or actorid"/>
 | 
					          tal:content="python:fullname or username or actorid"/>
 | 
				
			||||||
            <td tal:content="python:toLocalizedTime(items['time'],long_format=True)"/>
 | 
					      <td tal:content="python:contextObj.toLocalizedTime(event['time'],long_format=True)"/>
 | 
				
			||||||
            <td><tal:comment condition="rhComments" tal:content="structure rhComments"/>
 | 
					      <td tal:condition="not: isDataChange"><tal:comment condition="rhComments" tal:content="structure rhComments"/>
 | 
				
			||||||
        <tal:noComment condition="not: rhComments" i18n:translate="no_comments" i18n:domain="plone"/></td>
 | 
					        <tal:noComment condition="not: rhComments" i18n:translate="no_comments" i18n:domain="plone"/></td>
 | 
				
			||||||
 | 
					      <td tal:condition="isDataChange">
 | 
				
			||||||
 | 
					        <tal:comment replace="nothing">
 | 
				
			||||||
 | 
					          Display the previous values of the fields whose value were modified in this change.</tal:comment>
 | 
				
			||||||
 | 
					        <table class="appyChanges" width="100%">
 | 
				
			||||||
 | 
					          <tr>
 | 
				
			||||||
 | 
					            <th tal:content="python: tool.translate('modified_field')"></th>
 | 
				
			||||||
 | 
					            <th tal:content="python: tool.translate('previous_value')"></th>
 | 
				
			||||||
 | 
					          </tr>
 | 
				
			||||||
 | 
					          <tr tal:repeat="change event/changes/items">
 | 
				
			||||||
 | 
					            <td tal:content="python: tool.translate(change[1][1])"></td>
 | 
				
			||||||
 | 
					            <td tal:define="appyType python:contextObj.getAppyType(change[0])"
 | 
				
			||||||
 | 
					                tal:content="python: contextObj.getAppyValue(change[0], appyType, True, change[1][0])"></td>
 | 
				
			||||||
          </tr>
 | 
					          </tr>
 | 
				
			||||||
        </metal:block>
 | 
					 | 
				
			||||||
        </table>
 | 
					        </table>
 | 
				
			||||||
    </dd>
 | 
					      </td>
 | 
				
			||||||
  </dl>
 | 
					    </tr>
 | 
				
			||||||
</span>
 | 
					    </tal:event>
 | 
				
			||||||
 | 
					  </table>
 | 
				
			||||||
 | 
					  </tal:history>
 | 
				
			||||||
 | 
					</metal:history>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div metal:define-macro="pagePrologue">
 | 
					<div metal:define-macro="pagePrologue">
 | 
				
			||||||
  <tal:comment replace="nothing">Global elements used in every page.</tal:comment>
 | 
					  <tal:comment replace="nothing">Global elements used in every page.</tal:comment>
 | 
				
			||||||
| 
						 | 
					@ -468,6 +449,32 @@
 | 
				
			||||||
       f.submit();
 | 
					       f.submit();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  function toggleCookie(cookieId) {
 | 
				
			||||||
 | 
					    // What is the state of this boolean (expanded/collapsed) cookie?
 | 
				
			||||||
 | 
					    var state = readCookie(cookieId);
 | 
				
			||||||
 | 
					    if ((state != 'collapsed') && (state != 'expanded')) {
 | 
				
			||||||
 | 
					      // No cookie yet, create it.
 | 
				
			||||||
 | 
					      createCookie(cookieId, 'collapsed');
 | 
				
			||||||
 | 
					      state = 'collapsed';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var hook = document.getElementById(cookieId); // The hook is the part of
 | 
				
			||||||
 | 
					    // the HTML document that needs to be shown or hidden.
 | 
				
			||||||
 | 
					    var displayValue = 'none';
 | 
				
			||||||
 | 
					    var newState = 'collapsed';
 | 
				
			||||||
 | 
					    var imgSrc = 'skyn/expand.gif';
 | 
				
			||||||
 | 
					    if (state == 'collapsed') {
 | 
				
			||||||
 | 
					      // Show the HTML zone
 | 
				
			||||||
 | 
					      displayValue = 'block';
 | 
				
			||||||
 | 
					      imgSrc = 'skyn/collapse.gif';
 | 
				
			||||||
 | 
					      newState = 'expanded';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Update the corresponding HTML element
 | 
				
			||||||
 | 
					    hook.style.display = displayValue;
 | 
				
			||||||
 | 
					    var img = document.getElementById(cookieId + '_img');
 | 
				
			||||||
 | 
					    img.src = imgSrc;
 | 
				
			||||||
 | 
					    // Inverse the cookie value
 | 
				
			||||||
 | 
					    createCookie(cookieId, newState);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
-->
 | 
					-->
 | 
				
			||||||
  </script>
 | 
					  </script>
 | 
				
			||||||
  <tal:comment replace="nothing">Global form for deleting an object</tal:comment>
 | 
					  <tal:comment replace="nothing">Global form for deleting an object</tal:comment>
 | 
				
			||||||
| 
						 | 
					@ -479,7 +486,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<div metal:define-macro="showPageHeader"
 | 
					<div metal:define-macro="showPageHeader"
 | 
				
			||||||
     tal:define="appyPages python: contextObj.getAppyPages(phase);
 | 
					     tal:define="appyPages python: contextObj.getAppyPages(phase);
 | 
				
			||||||
                 showCommonInfo python: not isEdit"
 | 
					                 showCommonInfo python: not isEdit;
 | 
				
			||||||
 | 
					                 hasHistory contextObj/hasHistory;
 | 
				
			||||||
 | 
					                 historyExpanded python: tool.getCookieValue('appyHistory', default='collapsed') == 'expanded';
 | 
				
			||||||
 | 
					                 creator contextObj/Creator"
 | 
				
			||||||
     tal:condition="not: contextObj/isTemporary">
 | 
					     tal:condition="not: contextObj/isTemporary">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <tal:comment replace="nothing">Information that is common to all tabs (object title, state, etc)</tal:comment>
 | 
					    <tal:comment replace="nothing">Information that is common to all tabs (object title, state, etc)</tal:comment>
 | 
				
			||||||
| 
						 | 
					@ -509,15 +519,47 @@
 | 
				
			||||||
        <td colspan="2" class="discreet" tal:content="descrLabel"/>
 | 
					        <td colspan="2" class="discreet" tal:content="descrLabel"/>
 | 
				
			||||||
      </tr>
 | 
					      </tr>
 | 
				
			||||||
      <tr>
 | 
					      <tr>
 | 
				
			||||||
        <td>
 | 
					        <td class="documentByLine">
 | 
				
			||||||
          <metal:byLine use-macro="here/skyn/macros/macros/byline"/>
 | 
					          <tal:comment replace="nothing">Creator and last modification date</tal:comment>
 | 
				
			||||||
          <tal:showWorkflow condition="showWorkflow">
 | 
					            <tal:comment replace="nothing">Plus/minus icon for accessing history</tal:comment>
 | 
				
			||||||
            <metal:workflowHistory use-macro="here/skyn/macros/macros/workflowHistory"/>
 | 
					            <tal:accessHistory condition="hasHistory">
 | 
				
			||||||
          </tal:showWorkflow>
 | 
					            <img align="left" style="cursor:pointer" onClick="javascript:toggleCookie('appyHistory')"
 | 
				
			||||||
 | 
					                 tal:attributes="src python:test(historyExpanded, 'skyn/collapse.gif', 'skyn/expand.gif');"
 | 
				
			||||||
 | 
					                 id="appyHistory_img"/> 
 | 
				
			||||||
 | 
					            <span i18n:translate="label_history" i18n:domain="plone" class="appyHistory"></span> 
 | 
				
			||||||
 | 
					            </tal:accessHistory>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <tal:comment replace="nothing">Show document creator</tal:comment>
 | 
				
			||||||
 | 
					            <tal:creator condition="creator"
 | 
				
			||||||
 | 
					                 define="author python:contextObj.portal_membership.getMemberInfo(creator)">
 | 
				
			||||||
 | 
					            <span class="documentAuthor" i18n:domain="plone" i18n:translate="label_by_author">
 | 
				
			||||||
 | 
					            by <a tal:attributes="href string:${portal_url}/author/${creator}"
 | 
				
			||||||
 | 
					                  tal:content="python:author and author['fullname'] or creator"
 | 
				
			||||||
 | 
					                  tal:omit-tag="not:author" i18n:name="author"/>
 | 
				
			||||||
 | 
					            —
 | 
				
			||||||
 | 
					            </span>
 | 
				
			||||||
 | 
					            </tal:creator>
 | 
				
			||||||
 | 
					            <tal:comment replace="nothing">Show last modification date</tal:comment>
 | 
				
			||||||
 | 
					            <span i18n:translate="box_last_modified" i18n:domain="plone"></span>
 | 
				
			||||||
 | 
					            <span tal:replace="python:contextObj.toLocalizedTime(contextObj.ModificationDate(),long_format=1)"></span>
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
        <td valign="top"><metal:pod use-macro="here/skyn/macros/macros/listPodTemplates"/>
 | 
					        <td valign="top"><metal:pod use-macro="here/skyn/macros/macros/listPodTemplates"/>
 | 
				
			||||||
        </td>
 | 
					        </td>
 | 
				
			||||||
      </tr>
 | 
					      </tr>
 | 
				
			||||||
 | 
					      <tal:comment replace="nothing">Object history</tal:comment>
 | 
				
			||||||
 | 
					      <tr tal:condition="hasHistory">
 | 
				
			||||||
 | 
					        <td colspan="2">
 | 
				
			||||||
 | 
					          <span id="appyHistory"
 | 
				
			||||||
 | 
					                tal:attributes="style python:test(historyExpanded, 'display:block', 'display:none')">
 | 
				
			||||||
 | 
					          <div tal:define="ajaxHookId python: contextObj.UID() + '_history';
 | 
				
			||||||
 | 
					                           ajaxUrl python: contextObj.getUrl('showHistory')"
 | 
				
			||||||
 | 
					               tal:attributes="id ajaxHookId">
 | 
				
			||||||
 | 
					             <script language="javascript" tal:content="python: 'askAjaxChunk(\'%s\',\'%s\')' % (ajaxHookId, ajaxUrl)">
 | 
				
			||||||
 | 
					             </script>
 | 
				
			||||||
 | 
					          </div>
 | 
				
			||||||
 | 
					          </span>
 | 
				
			||||||
 | 
					        </td>
 | 
				
			||||||
 | 
					      </tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <tal:comment replace="nothing">Workflow-related information and actions</tal:comment>
 | 
					      <tal:comment replace="nothing">Workflow-related information and actions</tal:comment>
 | 
				
			||||||
      <tr tal:condition="python: showWorkflow and contextObj.getWorkflowLabel()">
 | 
					      <tr tal:condition="python: showWorkflow and contextObj.getWorkflowLabel()">
 | 
				
			||||||
| 
						 | 
					@ -819,35 +861,6 @@
 | 
				
			||||||
   tal:define="queryUrl python: '%s/skyn/query' % appFolder.absolute_url();
 | 
					   tal:define="queryUrl python: '%s/skyn/query' % appFolder.absolute_url();
 | 
				
			||||||
               currentSearch request/search|nothing;
 | 
					               currentSearch request/search|nothing;
 | 
				
			||||||
               currentType request/type_name|nothing;">
 | 
					               currentType request/type_name|nothing;">
 | 
				
			||||||
  <script language="javascript">
 | 
					 | 
				
			||||||
  <!--
 | 
					 | 
				
			||||||
  function toggleSearchGroup(groupId) {
 | 
					 | 
				
			||||||
    // What is the state of this toggle?
 | 
					 | 
				
			||||||
    var state = readCookie(groupId);
 | 
					 | 
				
			||||||
    if ((state != 'collapsed') && (state != 'expanded')) {
 | 
					 | 
				
			||||||
      // No cookie yet, create it.
 | 
					 | 
				
			||||||
      createCookie(groupId, 'collapsed');
 | 
					 | 
				
			||||||
      state = 'collapsed';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    var group = document.getElementById(groupId);
 | 
					 | 
				
			||||||
    var displayValue = 'none';
 | 
					 | 
				
			||||||
    var newState = 'collapsed';
 | 
					 | 
				
			||||||
    var imgSrc = 'skyn/expand.gif';
 | 
					 | 
				
			||||||
    if (state == 'collapsed') {
 | 
					 | 
				
			||||||
      // Expand the group
 | 
					 | 
				
			||||||
      displayValue = 'block';
 | 
					 | 
				
			||||||
      imgSrc = 'skyn/collapse.gif';
 | 
					 | 
				
			||||||
      newState = 'expanded';
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    // Update group visibility and img
 | 
					 | 
				
			||||||
    group.style.display = displayValue;
 | 
					 | 
				
			||||||
    var img = document.getElementById(groupId + '_img');
 | 
					 | 
				
			||||||
    img.src = imgSrc;
 | 
					 | 
				
			||||||
    // Inverse the cookie value
 | 
					 | 
				
			||||||
    createCookie(groupId, newState);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  -->
 | 
					 | 
				
			||||||
  </script>
 | 
					 | 
				
			||||||
  <tal:comment replace="nothing">Portlet title, with link to tool.</tal:comment>
 | 
					  <tal:comment replace="nothing">Portlet title, with link to tool.</tal:comment>
 | 
				
			||||||
  <dt class="portletHeader">
 | 
					  <dt class="portletHeader">
 | 
				
			||||||
    <tal:comment replace="nothing">If there is only one flavour, clicking on the portlet
 | 
					    <tal:comment replace="nothing">If there is only one flavour, clicking on the portlet
 | 
				
			||||||
| 
						 | 
					@ -907,7 +920,7 @@
 | 
				
			||||||
          <img align="left" style="cursor:pointer"
 | 
					          <img align="left" style="cursor:pointer"
 | 
				
			||||||
               tal:attributes="id python: '%s_img' % group['labelId'];
 | 
					               tal:attributes="id python: '%s_img' % group['labelId'];
 | 
				
			||||||
                               src python:test(expanded, 'skyn/collapse.gif', 'skyn/expand.gif');
 | 
					                               src python:test(expanded, 'skyn/collapse.gif', 'skyn/expand.gif');
 | 
				
			||||||
                               onClick python:'javascript:toggleSearchGroup(\'%s\')' % group['labelId']"/> 
 | 
					                               onClick python:'javascript:toggleCookie(\'%s\')' % group['labelId']"/> 
 | 
				
			||||||
          <span tal:replace="group/label"/>
 | 
					          <span tal:replace="group/label"/>
 | 
				
			||||||
        </dt>
 | 
					        </dt>
 | 
				
			||||||
        <tal:comment replace="nothing">Group searches</tal:comment>
 | 
					        <tal:comment replace="nothing">Group searches</tal:comment>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,10 +27,10 @@
 | 
				
			||||||
                  phase request/phase|phaseInfo/name;
 | 
					                  phase request/phase|phaseInfo/name;
 | 
				
			||||||
                  pageName python: contextObj.getAppyPage(isEdit, phaseInfo);
 | 
					                  pageName python: contextObj.getAppyPage(isEdit, phaseInfo);
 | 
				
			||||||
                  showWorkflow python: flavour.getAttr('showWorkflowFor' + contextObj.meta_type)">
 | 
					                  showWorkflow python: flavour.getAttr('showWorkflowFor' + contextObj.meta_type)">
 | 
				
			||||||
      <div metal:use-macro="here/skyn/macros/macros/pagePrologue"/>
 | 
					      <metal:prologue use-macro="here/skyn/macros/macros/pagePrologue"/>
 | 
				
			||||||
      <div metal:use-macro="here/skyn/macros/macros/showPageHeader"/>
 | 
					      <metal:header   use-macro="here/skyn/macros/macros/showPageHeader"/>
 | 
				
			||||||
      <div metal:use-macro="here/skyn/macros/macros/listFields" />
 | 
					      <metal:fields   use-macro="here/skyn/macros/macros/listFields" />
 | 
				
			||||||
      <div metal:use-macro="here/skyn/macros/macros/showPageFooter"/>
 | 
					      <metal:footer   use-macro="here/skyn/macros/macros/showPageFooter"/>
 | 
				
			||||||
    </metal:fill>
 | 
					    </metal:fill>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@
 | 
				
			||||||
         tal:define="tool python: context.<!toolInstanceName!>"
 | 
					         tal:define="tool python: context.<!toolInstanceName!>"
 | 
				
			||||||
         tal:condition="tool/showPortlet">
 | 
					         tal:condition="tool/showPortlet">
 | 
				
			||||||
      <metal:block metal:use-macro="here/global_defines/macros/defines" />
 | 
					      <metal:block metal:use-macro="here/global_defines/macros/defines" />
 | 
				
			||||||
 | 
					      <metal:prologue use-macro="here/skyn/macros/macros/pagePrologue"/>
 | 
				
			||||||
      <dl tal:define="rootClasses tool/getRootClasses;
 | 
					      <dl tal:define="rootClasses tool/getRootClasses;
 | 
				
			||||||
                      appName string:<!applicationName!>;
 | 
					                      appName string:<!applicationName!>;
 | 
				
			||||||
                      appFolder tool/getAppFolder;
 | 
					                      appFolder tool/getAppFolder;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,31 @@
 | 
				
			||||||
  padding: 0.1em 1em 0.1em 1.3em;
 | 
					  padding: 0.1em 1em 0.1em 1.3em;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.appyChanges th {
 | 
				
			||||||
 | 
					  font-style: italic;
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
 | 
					  border-bottom: 1px dashed #8CACBB;
 | 
				
			||||||
 | 
					  border-top: 0 none transparent;
 | 
				
			||||||
 | 
					  border-left: 0 none transparent;
 | 
				
			||||||
 | 
					  border-right: 0 none transparent; 
 | 
				
			||||||
 | 
					  padding: 0.1em 0.1em 0.1em 0.1em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.appyChanges td {
 | 
				
			||||||
 | 
					  padding: 0.1em 0.1em 0.1em 0.1em !important;
 | 
				
			||||||
 | 
					  border-right: 0 none transparent !important; 
 | 
				
			||||||
 | 
					  border-top: 0 none transparent;
 | 
				
			||||||
 | 
					  border-left: 0 none transparent;
 | 
				
			||||||
 | 
					  border-right: 0 none transparent;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.appyHistory {
 | 
				
			||||||
 | 
					  font-variant: small-caps;
 | 
				
			||||||
 | 
					  font-weight: bold;
 | 
				
			||||||
 | 
					  color: black;
 | 
				
			||||||
 | 
					  font-size: 105%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* stepxx classes are used for displaying status of a phase or state. */
 | 
					/* stepxx classes are used for displaying status of a phase or state. */
 | 
				
			||||||
.stepDone {
 | 
					.stepDone {
 | 
				
			||||||
  background-color: #cde2a7;
 | 
					  background-color: #cde2a7;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@
 | 
				
			||||||
   developer the real classes used by the underlying web framework.'''
 | 
					   developer the real classes used by the underlying web framework.'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
import time, os.path, mimetypes, unicodedata
 | 
					import time, os.path, mimetypes, unicodedata, random
 | 
				
			||||||
from appy.gen import Search
 | 
					from appy.gen import Search
 | 
				
			||||||
from appy.gen.utils import sequenceTypes
 | 
					from appy.gen.utils import sequenceTypes
 | 
				
			||||||
from appy.shared.utils import getOsTempFolder
 | 
					from appy.shared.utils import getOsTempFolder
 | 
				
			||||||
| 
						 | 
					@ -153,7 +153,8 @@ class AbstractWrapper:
 | 
				
			||||||
            objId = kwargs['id']
 | 
					            objId = kwargs['id']
 | 
				
			||||||
            del kwargs['id']
 | 
					            del kwargs['id']
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            objId = '%s.%f' % (idPrefix, time.time())
 | 
					            objId = '%s.%f.%s' % (idPrefix, time.time(),
 | 
				
			||||||
 | 
					                                  str(random.random()).split('.')[1])
 | 
				
			||||||
        # Determine if object must be created from external data
 | 
					        # Determine if object must be created from external data
 | 
				
			||||||
        externalData = None
 | 
					        externalData = None
 | 
				
			||||||
        if kwargs.has_key('_data'):
 | 
					        if kwargs.has_key('_data'):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +59,9 @@ class PoMessage:
 | 
				
			||||||
    IMPORT_DONE = 'Import terminated successfully.'
 | 
					    IMPORT_DONE = 'Import terminated successfully.'
 | 
				
			||||||
    WORKFLOW_COMMENT = 'Optional comment'
 | 
					    WORKFLOW_COMMENT = 'Optional comment'
 | 
				
			||||||
    WORKFLOW_STATE = 'state'
 | 
					    WORKFLOW_STATE = 'state'
 | 
				
			||||||
 | 
					    DATA_CHANGE = 'Data change'
 | 
				
			||||||
 | 
					    MODIFIED_FIELD = 'Modified field'
 | 
				
			||||||
 | 
					    PREVIOUS_VALUE = 'Previous value'
 | 
				
			||||||
    PHASE = 'phase'
 | 
					    PHASE = 'phase'
 | 
				
			||||||
    ROOT_TYPE = 'type'
 | 
					    ROOT_TYPE = 'type'
 | 
				
			||||||
    CHOOSE_A_VALUE = ' - '
 | 
					    CHOOSE_A_VALUE = ' - '
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,6 +14,12 @@ SAP_FUNCTION_ERROR = 'Error while calling function "%s". %s'
 | 
				
			||||||
SAP_DISCONNECT_ERROR = 'Error while disconnecting from SAP. %s'
 | 
					SAP_DISCONNECT_ERROR = 'Error while disconnecting from SAP. %s'
 | 
				
			||||||
SAP_TABLE_PARAM_ERROR = 'Param "%s" does not correspond to a valid table ' \
 | 
					SAP_TABLE_PARAM_ERROR = 'Param "%s" does not correspond to a valid table ' \
 | 
				
			||||||
    'parameter for function "%s".'
 | 
					    'parameter for function "%s".'
 | 
				
			||||||
 | 
					SAP_STRUCT_ELEM_NOT_FOUND = 'Structure used by parameter "%s" does not define '\
 | 
				
			||||||
 | 
					    'an attribute named "%s."'
 | 
				
			||||||
 | 
					SAP_STRING_REQUIRED = 'Type mismatch for attribute "%s" used in parameter ' \
 | 
				
			||||||
 | 
					    '"%s": a string value is expected (SAP type is %s).'
 | 
				
			||||||
 | 
					SAP_STRING_OVERFLOW = 'A string value for attribute "%s" used in parameter ' \
 | 
				
			||||||
 | 
					    '"%s" is too long (SAP type is %s).'
 | 
				
			||||||
SAP_FUNCTION_NOT_FOUND = 'Function "%s" does not exist.'
 | 
					SAP_FUNCTION_NOT_FOUND = 'Function "%s" does not exist.'
 | 
				
			||||||
SAP_FUNCTION_INFO_ERROR = 'Error while asking information about function ' \
 | 
					SAP_FUNCTION_INFO_ERROR = 'Error while asking information about function ' \
 | 
				
			||||||
    '"%s". %s'
 | 
					    '"%s". %s'
 | 
				
			||||||
| 
						 | 
					@ -52,6 +58,32 @@ class Sap:
 | 
				
			||||||
            connNoPasswd = params[:params.index('PASSWD')] + 'PASSWD=********'
 | 
					            connNoPasswd = params[:params.index('PASSWD')] + 'PASSWD=********'
 | 
				
			||||||
            raise SapError(SAP_CONNECT_ERROR % (connNoPasswd, str(se)))
 | 
					            raise SapError(SAP_CONNECT_ERROR % (connNoPasswd, str(se)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def createStructure(self, structDef, userData, paramName):
 | 
				
			||||||
 | 
					        '''Create a struct corresponding to SAP/C structure definition
 | 
				
			||||||
 | 
					           p_structDef and fills it with dict p_userData.'''
 | 
				
			||||||
 | 
					        res = structDef()
 | 
				
			||||||
 | 
					        for name, value in userData.iteritems():
 | 
				
			||||||
 | 
					            if name not in structDef._sfield_names_:
 | 
				
			||||||
 | 
					                raise SapError(SAP_STRUCT_ELEM_NOT_FOUND % (paramName, name))
 | 
				
			||||||
 | 
					            sapType = structDef._sfield_sap_types_[name]
 | 
				
			||||||
 | 
					            # Check if the value is valid according to the required type
 | 
				
			||||||
 | 
					            if sapType[0] == 'C':
 | 
				
			||||||
 | 
					                sType = '%s%d' % (sapType[0], sapType[1])
 | 
				
			||||||
 | 
					                # "None" value is tolerated.
 | 
				
			||||||
 | 
					                if value == None: value = ''
 | 
				
			||||||
 | 
					                if not isinstance(value, basestring):
 | 
				
			||||||
 | 
					                    raise SapError(
 | 
				
			||||||
 | 
					                        SAP_STRING_REQUIRED % (name, paramName, sType))
 | 
				
			||||||
 | 
					                if len(value) > sapType[1]:
 | 
				
			||||||
 | 
					                    raise SapError(
 | 
				
			||||||
 | 
					                        SAP_STRING_OVERFLOW % (name, paramName, sType))
 | 
				
			||||||
 | 
					                # Left-fill the string with blanks.
 | 
				
			||||||
 | 
					                v = value.ljust(sapType[1])
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                v = value
 | 
				
			||||||
 | 
					            res[name.lower()] = v
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def call(self, functionName, **params):
 | 
					    def call(self, functionName, **params):
 | 
				
			||||||
        '''Calls a function on the SAP server.'''
 | 
					        '''Calls a function on the SAP server.'''
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					@ -60,8 +92,8 @@ class Sap:
 | 
				
			||||||
            for name, value in params.iteritems():
 | 
					            for name, value in params.iteritems():
 | 
				
			||||||
                if type(value) == dict:
 | 
					                if type(value) == dict:
 | 
				
			||||||
                    # The param corresponds to a SAP/C "struct"
 | 
					                    # The param corresponds to a SAP/C "struct"
 | 
				
			||||||
                    v = self.sap.get_structure(name)()
 | 
					                    v = self.createStructure(
 | 
				
			||||||
                    v.from_dict(value)
 | 
					                        self.sap.get_structure(name),value, name)
 | 
				
			||||||
                elif type(value) in sequenceTypes:
 | 
					                elif type(value) in sequenceTypes:
 | 
				
			||||||
                    # The param must be a SAP/C "table" (a list of structs)
 | 
					                    # The param must be a SAP/C "table" (a list of structs)
 | 
				
			||||||
                    # Retrieve the name of the struct type related to this
 | 
					                    # Retrieve the name of the struct type related to this
 | 
				
			||||||
| 
						 | 
					@ -78,8 +110,7 @@ class Sap:
 | 
				
			||||||
                            SAP_TABLE_PARAM_ERROR % (name, functionName))
 | 
					                            SAP_TABLE_PARAM_ERROR % (name, functionName))
 | 
				
			||||||
                    v = self.sap.get_table(tableTypeName)
 | 
					                    v = self.sap.get_table(tableTypeName)
 | 
				
			||||||
                    for dValue in value:
 | 
					                    for dValue in value:
 | 
				
			||||||
                        v.append_from_dict(dValue)
 | 
					                        v.append(self.createStructure(v.struc, dValue, name))
 | 
				
			||||||
                    #v = v.handle
 | 
					 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    v = value
 | 
					                    v = value
 | 
				
			||||||
                function[name] = v
 | 
					                function[name] = v
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue