[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
|
@ -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,14 +202,27 @@
|
|||
<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>
|
||||
<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>
|
||||
|
@ -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>
|
||||
|
|
22
gen/utils.py
22
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,21 +163,10 @@ 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
|
||||
else:
|
||||
# Compute status based on current page
|
||||
page = self.obj.REQUEST.get('page', 'main')
|
||||
if page in self.pages:
|
||||
|
@ -194,11 +183,6 @@ class PhaseDescr(Descr):
|
|||
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…
Reference in a new issue