[gen] Added an index 'Modified' on every object (it represents the date of the last modification); removed obsolete workflow-related code; removed attributes Tool.showAllStatesInPhaseFor...; changed the way to display the object's current state in the UI.
This commit is contained in:
		
							parent
							
								
									f31cbc4d12
								
							
						
					
					
						commit
						21585df6a1
					
				
					 12 changed files with 69 additions and 123 deletions
				
			
		| 
						 | 
				
			
			@ -1547,9 +1547,15 @@ class Date(Type):
 | 
			
		|||
 | 
			
		||||
    def getFormattedValue(self, obj, value):
 | 
			
		||||
        if self.isEmptyValue(value): return ''
 | 
			
		||||
        res = value.strftime('%d/%m/') + str(value.year())
 | 
			
		||||
        tool = obj.getTool().appy()
 | 
			
		||||
        # A problem may occur with some extreme year values. Replace the "year"
 | 
			
		||||
        # part "by hand".
 | 
			
		||||
        dateFormat = tool.dateFormat
 | 
			
		||||
        if '%Y' in dateFormat:
 | 
			
		||||
            dateFormat = dateFormat.replace('%Y', str(value.year()))
 | 
			
		||||
        res = value.strftime(dateFormat)
 | 
			
		||||
        if self.format == Date.WITH_HOUR:
 | 
			
		||||
            res += ' %s' % value.strftime('%H:%M')
 | 
			
		||||
            res += ' %s' % value.strftime(tool.hourFormat)
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def getRequestValue(self, request, requestName=None):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -539,18 +539,6 @@ class ToolClassDescriptor(ClassDescriptor):
 | 
			
		|||
        fieldType = gen.Boolean(default=True, page='userInterface',
 | 
			
		||||
                                group=groupName)
 | 
			
		||||
        self.addField(fieldName, fieldType)
 | 
			
		||||
        # Adds the boolean field for showing all states in current state or not.
 | 
			
		||||
        # If this boolean is True but the current phase counts only one state,
 | 
			
		||||
        # we will not show the state at all: the fact of knowing in what phase
 | 
			
		||||
        # we are is sufficient. If this boolean is False, we simply show the
 | 
			
		||||
        # current state.
 | 
			
		||||
        defaultValue = False
 | 
			
		||||
        if len(classDescr.getPhases()) > 1:
 | 
			
		||||
            defaultValue = True
 | 
			
		||||
        fieldName = 'showAllStatesInPhaseFor%s' % className
 | 
			
		||||
        fieldType = gen.Boolean(default=defaultValue, page='userInterface',
 | 
			
		||||
                                group=groupName)
 | 
			
		||||
        self.addField(fieldName, fieldType)
 | 
			
		||||
 | 
			
		||||
class UserClassDescriptor(ClassDescriptor):
 | 
			
		||||
    '''Appy-specific class for representing a user.'''
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -502,6 +502,7 @@ class ZopeGenerator(Generator):
 | 
			
		|||
            msg('object_history',       '', msg.OBJECT_HISTORY),
 | 
			
		||||
            msg('object_created_by',    '', msg.OBJECT_CREATED_BY),
 | 
			
		||||
            msg('object_created_on',    '', msg.OBJECT_CREATED_ON),
 | 
			
		||||
            msg('object_modified_on',   '', msg.OBJECT_MODIFIED_ON),
 | 
			
		||||
            msg('object_action',        '', msg.OBJECT_ACTION),
 | 
			
		||||
            msg('object_author',        '', msg.OBJECT_AUTHOR),
 | 
			
		||||
            msg('action_date',          '', msg.ACTION_DATE),
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,8 +9,8 @@ from appy.shared.utils import normalizeText
 | 
			
		|||
defaultIndexes = {
 | 
			
		||||
    'State': 'ListIndex', 'UID': 'FieldIndex', 'Title': 'TextIndex',
 | 
			
		||||
    'SortableTitle': 'FieldIndex', 'SearchableText': 'TextIndex',
 | 
			
		||||
    'Creator': 'FieldIndex', 'Created': 'DateIndex', 'ClassName': 'FieldIndex',
 | 
			
		||||
    'Allowed': 'KeywordIndex'}
 | 
			
		||||
    'Creator': 'FieldIndex', 'Created': 'DateIndex', 'Modified': 'DateIndex',
 | 
			
		||||
    'ClassName': 'FieldIndex', 'Allowed': 'KeywordIndex'}
 | 
			
		||||
 | 
			
		||||
# Stuff for creating or updating the indexes -----------------------------------
 | 
			
		||||
class TextIndexInfo:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -87,6 +87,10 @@ class BaseMixin:
 | 
			
		|||
            appyObject = obj.appy()
 | 
			
		||||
            if hasattr(appyObject, 'onEdit'):
 | 
			
		||||
                msg = appyObject.onEdit(created)
 | 
			
		||||
        # Update last modification date
 | 
			
		||||
        if not created:
 | 
			
		||||
            from DateTime import DateTime
 | 
			
		||||
            obj.modified = DateTime()
 | 
			
		||||
        obj.reindex()
 | 
			
		||||
        return obj, msg
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -700,29 +704,6 @@ class BaseMixin:
 | 
			
		|||
                res.append({'field':field, 'width':width, 'align': align})
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def getAppyStates(self, phase, currentOnly=False):
 | 
			
		||||
        '''Returns information about the states that are related to p_phase.
 | 
			
		||||
           If p_currentOnly is True, we return the current state, even if not
 | 
			
		||||
           related to p_phase.'''
 | 
			
		||||
        currentState = self.State()
 | 
			
		||||
        if currentOnly:
 | 
			
		||||
            return [StateDescr(currentState, 'current').get()]
 | 
			
		||||
        res = []
 | 
			
		||||
        workflow = self.getWorkflow()
 | 
			
		||||
        stateStatus = 'done'
 | 
			
		||||
        for stateName in dir(workflow):
 | 
			
		||||
            if getattr(workflow, stateName).__class__.__name__ != 'State':
 | 
			
		||||
                continue
 | 
			
		||||
            if stateName == currentState:
 | 
			
		||||
                stateStatus = 'current'
 | 
			
		||||
            elif stateStatus != 'done':
 | 
			
		||||
                stateStatus = 'future'
 | 
			
		||||
            state = getattr(workflow, stateName)
 | 
			
		||||
            if (state.phase == phase) and \
 | 
			
		||||
               (self._appy_showState(workflow, state.show)):
 | 
			
		||||
                res.append(StateDescr(stateName, stateStatus).get())
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
    def getAppyTransitions(self, includeFake=True, includeNotShowable=False):
 | 
			
		||||
        '''This method returns info about transitions that one can trigger from
 | 
			
		||||
           the user interface.
 | 
			
		||||
| 
						 | 
				
			
			@ -778,8 +759,7 @@ class BaseMixin:
 | 
			
		|||
        for appyType in self.getAllAppyTypes():
 | 
			
		||||
            typePhase = appyType.page.phase
 | 
			
		||||
            if typePhase not in phases:
 | 
			
		||||
                states = self.getAppyStates(typePhase)
 | 
			
		||||
                phase = PhaseDescr(typePhase, states, self)
 | 
			
		||||
                phase = PhaseDescr(typePhase, self)
 | 
			
		||||
                res.append(phase.__dict__)
 | 
			
		||||
                phases[typePhase] = phase
 | 
			
		||||
            else:
 | 
			
		||||
| 
						 | 
				
			
			@ -1213,7 +1193,7 @@ class BaseMixin:
 | 
			
		|||
        return wrapper
 | 
			
		||||
 | 
			
		||||
    # --------------------------------------------------------------------------
 | 
			
		||||
    # Standard methods for computing values of standard Appy indexes
 | 
			
		||||
    # Methods for computing values of standard Appy indexes
 | 
			
		||||
    # --------------------------------------------------------------------------
 | 
			
		||||
    def UID(self):
 | 
			
		||||
        '''Returns the unique identifier for this object.'''
 | 
			
		||||
| 
						 | 
				
			
			@ -1246,6 +1226,11 @@ class BaseMixin:
 | 
			
		|||
        '''When was this object created ?'''
 | 
			
		||||
        return self.created
 | 
			
		||||
 | 
			
		||||
    def Modified(self):
 | 
			
		||||
        '''When was this object last modified ?'''
 | 
			
		||||
        if hasattr(self.aq_base, 'modified'): return self.modified
 | 
			
		||||
        return self.created
 | 
			
		||||
 | 
			
		||||
    def State(self, name=True, initial=False):
 | 
			
		||||
        '''Returns information about the current object state. If p_name is
 | 
			
		||||
           True, the returned info is the state name. Else, it is the State
 | 
			
		||||
| 
						 | 
				
			
			@ -1291,11 +1276,12 @@ class BaseMixin:
 | 
			
		|||
                    res.add('user:%s' % id)
 | 
			
		||||
        return list(res)
 | 
			
		||||
 | 
			
		||||
    def _appy_showState(self, workflow, stateShow):
 | 
			
		||||
        '''Must I show a state whose "show value" is p_stateShow?'''
 | 
			
		||||
    def showState(self):
 | 
			
		||||
        '''Must I show self's current state ?'''
 | 
			
		||||
        stateShow = self.State(name=False).show
 | 
			
		||||
        if callable(stateShow):
 | 
			
		||||
            return stateShow(workflow, self.appy())
 | 
			
		||||
        else: return stateShow
 | 
			
		||||
            return stateShow(self.getWorkflow(), self.appy())
 | 
			
		||||
        return stateShow
 | 
			
		||||
 | 
			
		||||
    def _appy_listStates(self):
 | 
			
		||||
        '''Lists the possible states for this object.'''
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -210,7 +210,7 @@ setattr(Page, Page.pages.back.attribute, Page.pages.back)
 | 
			
		|||
# Prefixes of the fields generated on the Tool.
 | 
			
		||||
toolFieldPrefixes = ('podTemplate', 'formats', 'resultColumns',
 | 
			
		||||
                     'enableAdvancedSearch', 'numberOfSearchColumns',
 | 
			
		||||
                     'searchFields', 'showWorkflow', 'showAllStatesInPhase')
 | 
			
		||||
                     'searchFields', 'showWorkflow')
 | 
			
		||||
defaultToolFields = ('title', 'mailHost', 'mailEnabled', 'mailFrom',
 | 
			
		||||
                     'appyVersion', 'dateFormat', 'hourFormat', 'users',
 | 
			
		||||
                     'connectedUsers', 'groups', 'translations',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,6 @@ class PoMessage:
 | 
			
		|||
    MSG_numberOfSearchColumns = "Number of search columns"
 | 
			
		||||
    MSG_searchFields = "Search fields"
 | 
			
		||||
    MSG_showWorkflow = 'Show workflow-related information'
 | 
			
		||||
    MSG_showAllStatesInPhase = 'Show all states in phase'
 | 
			
		||||
    POD_ASKACTION = 'Trigger related action'
 | 
			
		||||
    REF_NO = 'No object.'
 | 
			
		||||
    REF_ADD = 'Add a new one'
 | 
			
		||||
| 
						 | 
				
			
			@ -163,6 +162,7 @@ class PoMessage:
 | 
			
		|||
    OBJECT_HISTORY = 'History'
 | 
			
		||||
    OBJECT_CREATED_BY = 'By'
 | 
			
		||||
    OBJECT_CREATED_ON = 'On'
 | 
			
		||||
    OBJECT_MODIFIED_ON = 'Last updated on'
 | 
			
		||||
    OBJECT_ACTION = 'Action'
 | 
			
		||||
    OBJECT_AUTHOR = 'Author'
 | 
			
		||||
    ACTION_DATE = 'Date'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,7 +115,7 @@ img { border: 0; vertical-align: middle}
 | 
			
		|||
.summary {margin-bottom: 5px;}
 | 
			
		||||
.objectTitle { font-size: 11pt; border-bottom: 3px solid grey;
 | 
			
		||||
               font-weight: bold;}
 | 
			
		||||
.by { padding-top: 5px;}
 | 
			
		||||
.by { padding-top: 5px; color: grey; font-size: 97% }
 | 
			
		||||
.workflow { text-align: center; border-top: 1px solid grey }
 | 
			
		||||
.underTitle { background-color: #e9e9e9 }
 | 
			
		||||
.objectNavigate { margin-top: 3px;}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 192 B  | 
| 
						 | 
				
			
			@ -137,27 +137,6 @@
 | 
			
		|||
  </tal:history>
 | 
			
		||||
</metal:history>
 | 
			
		||||
 | 
			
		||||
<tal:comment replace="nothing">
 | 
			
		||||
  This macro displays an object's state(s). It is used by macro "header" below.
 | 
			
		||||
</tal:comment>
 | 
			
		||||
<metal:states define-macro="states"
 | 
			
		||||
              tal:define="showAllStatesInPhase python: tool.getAttr('showAllStatesInPhaseFor' + contextObj.meta_type);
 | 
			
		||||
                          states python: contextObj.getAppyStates(phase, currentOnly=not showAllStatesInPhase)"
 | 
			
		||||
              tal:condition="python: test(showAllStatesInPhase, len(states)>1, True)">
 | 
			
		||||
  <table>
 | 
			
		||||
    <tr>
 | 
			
		||||
      <tal:state repeat="stateInfo states">
 | 
			
		||||
        <td class="state"
 | 
			
		||||
            tal:content="python: _(contextObj.getWorkflowLabel(stateInfo['name']))">
 | 
			
		||||
        </td>
 | 
			
		||||
        <td tal:condition="python: stateInfo['name'] != states[-1]['name']">
 | 
			
		||||
          <img tal:attributes="src string: $appUrl/ui/nextState.png"/>
 | 
			
		||||
        </td>
 | 
			
		||||
      </tal:state>
 | 
			
		||||
    </tr>
 | 
			
		||||
  </table>
 | 
			
		||||
</metal:states>
 | 
			
		||||
 | 
			
		||||
<tal:comment replace="nothing">
 | 
			
		||||
  This macro displays an object's transitions(s). It is used by macro "header" below.
 | 
			
		||||
</tal:comment>
 | 
			
		||||
| 
						 | 
				
			
			@ -223,15 +202,28 @@
 | 
			
		|||
            <span tal:replace="python: _('object_history')"></span> || 
 | 
			
		||||
            </tal:accessHistory>
 | 
			
		||||
 | 
			
		||||
            <tal:comment replace="nothing">Show document creator</tal:comment>
 | 
			
		||||
            <span class="by" tal:condition="python: creator != 'Anonymous User'">
 | 
			
		||||
            <tal:comment replace="nothing">Document creator</tal:comment>
 | 
			
		||||
            <tal:creator condition="python: creator != 'Anonymous User'">
 | 
			
		||||
             <tal:by replace="python: _('object_created_by')"/>
 | 
			
		||||
             <tal:creator replace="python: tool.getUserName(creator)"/> —
 | 
			
		||||
            </span>
 | 
			
		||||
            <tal:comment replace="nothing">Show creation date</tal:comment>
 | 
			
		||||
             <tal:creator replace="python: tool.getUserName(creator)"/>
 | 
			
		||||
            </tal:creator>
 | 
			
		||||
            <tal:comment replace="nothing">Creation and last modification dates</tal:comment>
 | 
			
		||||
            <tal:by replace="python: _('object_created_on')"/>
 | 
			
		||||
            <span tal:replace="python: tool.formatDate(contextObj.created, withHour=True)"></span>
 | 
			
		||||
        </td>
 | 
			
		||||
            <tal:dates define="creationDate contextObj/Created;
 | 
			
		||||
                               modificationDate contextObj/Modified">
 | 
			
		||||
             <tal:date replace="python: tool.formatDate(creationDate, withHour=True)"/>
 | 
			
		||||
             <tal:modified condition="python: modificationDate != creationDate">—
 | 
			
		||||
              <tal:by replace="python: _('object_modified_on')"/>
 | 
			
		||||
              <tal:date replace="python: tool.formatDate(modificationDate, withHour=True)"/>
 | 
			
		||||
             </tal:modified>
 | 
			
		||||
            </tal:dates>
 | 
			
		||||
            <tal:comment replace="nothing">State</tal:comment>
 | 
			
		||||
            <tal:state condition="contextObj/showState">
 | 
			
		||||
             —
 | 
			
		||||
             <tal:label replace="python: _('workflow_state')"/>:
 | 
			
		||||
             <b tal:content="python: _(contextObj.getWorkflowLabel())"></b>
 | 
			
		||||
            </tal:state>
 | 
			
		||||
        </td>   
 | 
			
		||||
      </tr>
 | 
			
		||||
      <tal:comment replace="nothing">Object history</tal:comment>
 | 
			
		||||
      <tr tal:condition="hasHistory" class="underTitle">
 | 
			
		||||
| 
						 | 
				
			
			@ -246,15 +238,10 @@
 | 
			
		|||
          </span>
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
      <tal:comment replace="nothing">Workflow-related information and actions</tal:comment>
 | 
			
		||||
      <tal:comment replace="nothing">Possible transitions</tal:comment>
 | 
			
		||||
      <tr tal:condition="python: showWorkflow and contextObj.getWorkflowLabel()" class="workflow">
 | 
			
		||||
        <td colspan="2">
 | 
			
		||||
          <table width="100%">
 | 
			
		||||
            <tr>
 | 
			
		||||
              <td><metal:states use-macro="here/ui/page/macros/states"/></td>
 | 
			
		||||
              <td tal:attributes="align dright"><metal:states use-macro="here/ui/page/macros/transitions"/></td>
 | 
			
		||||
            </tr>
 | 
			
		||||
          </table>
 | 
			
		||||
        <td colspan="2" tal:attributes="align dright">
 | 
			
		||||
          <metal:states use-macro="here/ui/page/macros/transitions"/>
 | 
			
		||||
        </td>
 | 
			
		||||
      </tr>
 | 
			
		||||
    </table>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										48
									
								
								gen/utils.py
									
										
									
									
									
								
							
							
						
						
									
										48
									
								
								gen/utils.py
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -48,6 +48,7 @@ def createObject(folder, id, className, appName, wf=True, noSecurity=False):
 | 
			
		|||
    obj.creator = userId or 'Anonymous User'
 | 
			
		||||
    from DateTime import DateTime
 | 
			
		||||
    obj.created = DateTime()
 | 
			
		||||
    obj.modified = obj.created
 | 
			
		||||
    obj.__ac_local_roles__ = { userId: ['Owner'] } # userId can be None (anon).
 | 
			
		||||
    if wf: obj.notifyWorkflowCreated()
 | 
			
		||||
    return obj
 | 
			
		||||
| 
						 | 
				
			
			@ -115,9 +116,8 @@ class GroupDescr(Descr):
 | 
			
		|||
            groupDict['widgets'].append(newRow)
 | 
			
		||||
 | 
			
		||||
class PhaseDescr(Descr):
 | 
			
		||||
    def __init__(self, name, states, obj):
 | 
			
		||||
    def __init__(self, name, obj):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.states = states
 | 
			
		||||
        self.obj = obj
 | 
			
		||||
        self.phaseStatus = None
 | 
			
		||||
        # The list of names of pages in this phase
 | 
			
		||||
| 
						 | 
				
			
			@ -163,42 +163,26 @@ class PhaseDescr(Descr):
 | 
			
		|||
            self.hiddenPages.append(appyType.page.name)
 | 
			
		||||
 | 
			
		||||
    def computeStatus(self, allPhases):
 | 
			
		||||
        '''Compute status of whole phase based on individual status of states
 | 
			
		||||
           in this phase. If this phase includes no state, the concept of phase
 | 
			
		||||
           is simply used as a tab, and its status depends on the page currently
 | 
			
		||||
        '''Compute status of this phase, which depends on the page currently
 | 
			
		||||
           shown. This method also fills fields "previousPhase" and "nextPhase"
 | 
			
		||||
           if relevant, based on list of p_allPhases.'''
 | 
			
		||||
        res = 'Current'
 | 
			
		||||
        if self.states:
 | 
			
		||||
            # Compute status base on states
 | 
			
		||||
            res = self.states[0]['stateStatus']
 | 
			
		||||
            if len(self.states) > 1:
 | 
			
		||||
                for state in self.states[1:]:
 | 
			
		||||
                    if res != state['stateStatus']:
 | 
			
		||||
                        res = 'Current'
 | 
			
		||||
                        break
 | 
			
		||||
        # Compute status based on current page
 | 
			
		||||
        page = self.obj.REQUEST.get('page', 'main')
 | 
			
		||||
        if page in self.pages:
 | 
			
		||||
            res = 'Current'
 | 
			
		||||
        else:
 | 
			
		||||
            # Compute status based on current page
 | 
			
		||||
            page = self.obj.REQUEST.get('page', 'main')
 | 
			
		||||
            if page in self.pages:
 | 
			
		||||
                res = 'Current'
 | 
			
		||||
            else:
 | 
			
		||||
                res = 'Deselected'
 | 
			
		||||
            # Identify previous and next phases
 | 
			
		||||
            for phaseInfo in allPhases:
 | 
			
		||||
                if phaseInfo['name'] == self.name:
 | 
			
		||||
                    i = allPhases.index(phaseInfo)
 | 
			
		||||
                    if i > 0:
 | 
			
		||||
                        self.previousPhase = allPhases[i-1]
 | 
			
		||||
                    if i < (len(allPhases)-1):
 | 
			
		||||
                        self.nextPhase = allPhases[i+1]
 | 
			
		||||
            res = 'Deselected'
 | 
			
		||||
        # Identify previous and next phases
 | 
			
		||||
        for phaseInfo in allPhases:
 | 
			
		||||
            if phaseInfo['name'] == self.name:
 | 
			
		||||
                i = allPhases.index(phaseInfo)
 | 
			
		||||
                if i > 0:
 | 
			
		||||
                    self.previousPhase = allPhases[i-1]
 | 
			
		||||
                if i < (len(allPhases)-1):
 | 
			
		||||
                    self.nextPhase = allPhases[i+1]
 | 
			
		||||
        self.phaseStatus = res
 | 
			
		||||
 | 
			
		||||
class StateDescr(Descr):
 | 
			
		||||
    def __init__(self, name, stateStatus):
 | 
			
		||||
        self.name = name
 | 
			
		||||
        self.stateStatus = stateStatus.capitalize()
 | 
			
		||||
 | 
			
		||||
# ------------------------------------------------------------------------------
 | 
			
		||||
upperLetter = re.compile('[A-Z]')
 | 
			
		||||
def produceNiceMessage(msg):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -113,12 +113,6 @@ class ToolWrapper(AbstractWrapper):
 | 
			
		|||
           "showWorkflow"
 | 
			
		||||
               Stores the boolean field indicating if we must show workflow-
 | 
			
		||||
               related information for p_klass or not.
 | 
			
		||||
 | 
			
		||||
           "showAllStatesInPhase"
 | 
			
		||||
               Stores the boolean field indicating if we must show all states
 | 
			
		||||
               linked to the current phase or not. If this field is False, we
 | 
			
		||||
               simply show the current state, be it linked to the current phase
 | 
			
		||||
               or not.
 | 
			
		||||
        '''
 | 
			
		||||
        fullClassName = self.o.getPortalType(klass)
 | 
			
		||||
        res = '%sFor%s' % (attributeType, fullClassName)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue