[gen] Bugfix in the Ref field; added method workflow.Transition.getBack that finds the 'back' transition of a given transition.
This commit is contained in:
parent
14f85509e1
commit
1d0ee7a614
|
@ -219,8 +219,7 @@ class UiGroup:
|
||||||
</tr>
|
</tr>
|
||||||
<!-- The rows of widgets -->
|
<!-- The rows of widgets -->
|
||||||
<tr valign=":field.valign" for="row in field.elements">
|
<tr valign=":field.valign" for="row in field.elements">
|
||||||
<td for="field in row"
|
<td for="field in row" colspan=":field.colspan"
|
||||||
colspan="field.colspan"
|
|
||||||
style=":not loop.field.last and ('padding-right:%s'% cellgap) or ''">
|
style=":not loop.field.last and ('padding-right:%s'% cellgap) or ''">
|
||||||
<x if="field">
|
<x if="field">
|
||||||
<x if="field.type == 'group'">:field.pxView</x>
|
<x if="field.type == 'group'">:field.pxView</x>
|
||||||
|
|
149
fields/ref.py
149
fields/ref.py
|
@ -195,86 +195,85 @@ class Ref(Field):
|
||||||
|
|
||||||
# PX that displays referred objects as a list.
|
# PX that displays referred objects as a list.
|
||||||
pxViewList = Px('''
|
pxViewList = Px('''
|
||||||
<!-- No object at all -->
|
<div if="not innerRef or showPlusIcon" style="margin-bottom: 4px">
|
||||||
<div if="not objects" class="smaller">:_('no_ref')</div>
|
<span if="subLabel" class="discreet">:_(subLabel)</span>
|
||||||
|
(<span class="discreet">:totalNumber</span>)
|
||||||
|
<x>:field.pxAdd</x>
|
||||||
|
<!-- The search button if field is queryable -->
|
||||||
|
<input if="objects and field.queryable" type="button"
|
||||||
|
class="buttonSmall button"
|
||||||
|
var2="label=_('search_button')" value=":label"
|
||||||
|
style=":'%s; %s' % (url('search', bg=True), \
|
||||||
|
ztool.getButtonWidth(label))"
|
||||||
|
onclick=":'goto(%s)' % \
|
||||||
|
q('%s/search?className=%s&ref=%s:%s' % \
|
||||||
|
(ztool.absolute_url(), tiedClassName, zobj.id, field.name))"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<x if="objects">
|
<!-- (Top) navigation -->
|
||||||
<div if="not innerRef or showPlusIcon" style="margin-bottom: 4px">
|
<x>:tool.pxNavigate</x>
|
||||||
<span if="subLabel" class="discreet">:_(subLabel)</span>
|
|
||||||
(<span class="discreet">:totalNumber</span>)
|
|
||||||
<x>:field.pxAdd</x>
|
|
||||||
<!-- The search button if field is queryable -->
|
|
||||||
<input if="objects and field.queryable" type="button"
|
|
||||||
class="buttonSmall button"
|
|
||||||
var2="label=_('search_button')" value=":label"
|
|
||||||
style=":'%s; %s' % (url('search', bg=True), \
|
|
||||||
ztool.getButtonWidth(label))"
|
|
||||||
onclick=":'goto(%s)' % \
|
|
||||||
q('%s/search?className=%s&ref=%s:%s' % \
|
|
||||||
(ztool.absolute_url(), tiedClassName, zobj.id, field.name))"/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- (Top) navigation -->
|
<!-- No object is present -->
|
||||||
<x>:tool.pxNavigate</x>
|
<p class="discreet" if="not objects and not showPlusIcon">:_('no_ref')</p>
|
||||||
|
|
||||||
<!-- Linked objects -->
|
<!-- Linked objects -->
|
||||||
<table if="objects" class=":not innerRef and 'list' or ''"
|
<table if="objects" class=":not innerRef and 'list' or ''"
|
||||||
width=":innerRef and '100%' or field.layouts['view'].width"
|
width=":innerRef and '100%' or field.layouts['view'].width"
|
||||||
var2="columns=ztool.getColumnsSpecifiers(tiedClassName, \
|
var2="columns=ztool.getColumnsSpecifiers(tiedClassName, \
|
||||||
field.shownInfo, dir)">
|
field.shownInfo, dir)">
|
||||||
<tr if="field.showHeaders">
|
<tr if="field.showHeaders">
|
||||||
<th if="not inPickList and numbered" width=":numbered"></th>
|
<th if="not inPickList and numbered" width=":numbered"></th>
|
||||||
<th for="column in columns" width=":column.width"
|
<th for="column in columns" width=":column.width"
|
||||||
align=":column.align" var2="refField=column.field">
|
align=":column.align" var2="refField=column.field">
|
||||||
<span>:_(refField.labelId)</span>
|
<span>:_(refField.labelId)</span>
|
||||||
<x>:field.pxSortIcons</x>
|
<x>:field.pxSortIcons</x>
|
||||||
<x var="className=tiedClassName;
|
<x var="className=tiedClassName;
|
||||||
field=refField">:tool.pxShowDetails</x>
|
field=refField">:tool.pxShowDetails</x>
|
||||||
</th>
|
</th>
|
||||||
<th if="checkboxes" class="cbCell">
|
<th if="checkboxes" class="cbCell">
|
||||||
<img src=":url('checkall')" class="clickable"
|
<img src=":url('checkall')" class="clickable"
|
||||||
title=":_('check_uncheck')"
|
title=":_('check_uncheck')"
|
||||||
onclick=":'toggleAllRefCbs(%s)' % q(ajaxHookId)"/>
|
onclick=":'toggleAllRefCbs(%s)' % q(ajaxHookId)"/>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Loop on every (tied or selectable) object. -->
|
<!-- Loop on every (tied or selectable) object. -->
|
||||||
<tr for="tied in objects" valign="top"
|
<tr for="tied in objects" valign="top"
|
||||||
class=":loop.tied.odd and 'even' or 'odd'"
|
class=":loop.tied.odd and 'even' or 'odd'"
|
||||||
var2="tiedUid=tied.o.id;
|
var2="tiedUid=tied.o.id;
|
||||||
objectIndex=field.getIndexOf(zobj, tiedUid)|None">
|
objectIndex=field.getIndexOf(zobj, tiedUid)|None">
|
||||||
<td if="not inPickList and numbered">:field.pxNumber</td>
|
<td if="not inPickList and numbered">:field.pxNumber</td>
|
||||||
<td for="column in columns" width=":column.width" align=":column.align"
|
<td for="column in columns" width=":column.width" align=":column.align"
|
||||||
var2="refField=column.field">
|
var2="refField=column.field">
|
||||||
<!-- The "title" field -->
|
<!-- The "title" field -->
|
||||||
<x if="refField.name == 'title'">
|
<x if="refField.name == 'title'">
|
||||||
<x>:field.pxObjectTitle</x>
|
<x>:field.pxObjectTitle</x>
|
||||||
<div if="tied.o.mayAct()">:field.pxObjectActions</div>
|
<div if="tied.o.mayAct()">:field.pxObjectActions</div>
|
||||||
</x>
|
</x>
|
||||||
<!-- Any other field -->
|
<!-- Any other field -->
|
||||||
<x if="refField.name != 'title'">
|
<x if="refField.name != 'title'">
|
||||||
<x var="zobj=tied.o; obj=tied; layoutType='cell';
|
<x var="zobj=tied.o; obj=tied; layoutType='cell';
|
||||||
innerRef=True; field=refField"
|
innerRef=True; field=refField"
|
||||||
if="field.isShowable(zobj, 'result')">:field.pxRender</x>
|
if="field.isShowable(zobj, 'result')">:field.pxRender</x>
|
||||||
</x>
|
</x>
|
||||||
</td>
|
</td>
|
||||||
<td if="checkboxes" class="cbCell">
|
<td if="checkboxes" class="cbCell">
|
||||||
<input type="checkbox" name=":ajaxHookId" checked="checked"
|
<input type="checkbox" name=":ajaxHookId" checked="checked"
|
||||||
value=":tiedUid" onclick="toggleRefCb(this)"/>
|
value=":tiedUid" onclick="toggleRefCb(this)"/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Global actions -->
|
<!-- Global actions -->
|
||||||
<div if="canWrite and (totalNumber > 1)"
|
<div if="canWrite and (totalNumber > 1)"
|
||||||
align=":dright">:field.pxGlobalActions</div>
|
align=":dright">:field.pxGlobalActions</div>
|
||||||
|
|
||||||
<!-- (Bottom) navigation -->
|
<!-- (Bottom) navigation -->
|
||||||
<x>:tool.pxNavigate</x>
|
<x>:tool.pxNavigate</x>
|
||||||
|
|
||||||
<!-- Init checkboxes if present. -->
|
<!-- Init checkboxes if present. -->
|
||||||
<script if="checkboxes"
|
<script if="checkboxes"
|
||||||
type="text/javascript">:'initRefCbs(%s)' % q(ajaxHookId)</script>
|
type="text/javascript">:'initRefCbs(%s)' % q(ajaxHookId)
|
||||||
</x>''')
|
</script>''')
|
||||||
|
|
||||||
# PX that displays the list of objects the user may select to insert into a
|
# PX that displays the list of objects the user may select to insert into a
|
||||||
# ref field with link="list".
|
# ref field with link="list".
|
||||||
|
@ -410,7 +409,7 @@ class Ref(Field):
|
||||||
title=field.getReferenceLabel(tied, unlimited=True)"
|
title=field.getReferenceLabel(tied, unlimited=True)"
|
||||||
selected=":inRequest and (uid in requestValue) or \
|
selected=":inRequest and (uid in requestValue) or \
|
||||||
(uid in uids)" value=":uid"
|
(uid in uids)" value=":uid"
|
||||||
title=":title">:ztool.truncateValue(title, field.swidth)</option>
|
title=":title">:ztool.truncateValue(title, field.width)</option>
|
||||||
</select>''')
|
</select>''')
|
||||||
|
|
||||||
pxSearch = Px('''
|
pxSearch = Px('''
|
||||||
|
|
|
@ -397,6 +397,30 @@ class Transition:
|
||||||
if not obj.isTemporary(): obj.reindex()
|
if not obj.isTemporary(): obj.reindex()
|
||||||
return tool.goto(obj.getUrl(rq['HTTP_REFERER']))
|
return tool.goto(obj.getUrl(rq['HTTP_REFERER']))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def getBack(workflow, transition):
|
||||||
|
'''Returns the name of the transition (in p_workflow) that "cancels" the
|
||||||
|
triggering of p_transition and allows to go back to p_transition's
|
||||||
|
start state.'''
|
||||||
|
# Get the end state(s) of p_transition
|
||||||
|
transition = getattr(workflow, transition)
|
||||||
|
# Browse all transitions and find the one starting at p_transition's end
|
||||||
|
# state and coming back to p_transition's start state.
|
||||||
|
for trName, tr in workflow.__dict__.iteritems():
|
||||||
|
if not isinstance(tr, Transition) or (tr == transition): continue
|
||||||
|
if transition.isSingle():
|
||||||
|
if tr.hasState(transition.states[1], True) and \
|
||||||
|
tr.hasState(transition.states[0], False): return trName
|
||||||
|
else:
|
||||||
|
startOk = False
|
||||||
|
endOk = False
|
||||||
|
for start, end in transition.states:
|
||||||
|
if (not startOk) and tr.hasState(end, True):
|
||||||
|
startOk = True
|
||||||
|
if (not endOk) and tr.hasState(start, False):
|
||||||
|
endOk = True
|
||||||
|
if startOk and endOk: return trName
|
||||||
|
|
||||||
class UiTransition:
|
class UiTransition:
|
||||||
'''Represents a widget that displays a transition.'''
|
'''Represents a widget that displays a transition.'''
|
||||||
pxView = Px('''<x var="buttonCss = (buttonsMode == 'small') and \
|
pxView = Px('''<x var="buttonCss = (buttonsMode == 'small') and \
|
||||||
|
|
|
@ -18,8 +18,9 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_noroles = []
|
_noroles = []
|
||||||
|
|
||||||
# Errors -----------------------------------------------------------------------
|
# Global JS internationalized messages that will be computed in every page -----
|
||||||
jsMessages = ('no_elem_selected', 'action_confirm', 'warn_leave_form')
|
jsMessages = ('no_elem_selected', 'action_confirm', 'save_confirm',
|
||||||
|
'warn_leave_form')
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class ToolMixin(BaseMixin):
|
class ToolMixin(BaseMixin):
|
||||||
|
@ -389,25 +390,26 @@ class ToolMixin(BaseMixin):
|
||||||
k = self.getAppyClass(className)
|
k = self.getAppyClass(className)
|
||||||
return hasattr(k, 'listColumns') and k.listColumns or ('title',)
|
return hasattr(k, 'listColumns') and k.listColumns or ('title',)
|
||||||
|
|
||||||
def truncateValue(self, value, width=15):
|
def truncateValue(self, value, width=20):
|
||||||
'''Truncates the p_value according to p_width.'''
|
'''Truncates the p_value according to p_width. p_value has to be
|
||||||
|
unicode-encoded for being truncated (else, one char may be spread on
|
||||||
|
2 chars).'''
|
||||||
|
# Param p_width can be None.
|
||||||
|
if not width: width = 20
|
||||||
if isinstance(value, str): value = value.decode('utf-8')
|
if isinstance(value, str): value = value.decode('utf-8')
|
||||||
if len(value) > width:
|
if len(value) > width: return value[:width] + '...'
|
||||||
return value[:width].encode('utf-8') + '...'
|
return value
|
||||||
return value.encode('utf-8')
|
|
||||||
|
|
||||||
def truncateText(self, text, width=15):
|
def truncateText(self, text, width=20):
|
||||||
'''Truncates p_text to max p_width chars. If the text is longer than
|
'''Truncates p_text to max p_width chars. If the text is longer than
|
||||||
p_width, the truncated part is put in a "acronym" html tag.'''
|
p_width, the truncated part is put in a "acronym" html tag. p_text
|
||||||
# p_text has to be unicode-encoded for being truncated (else, one char
|
has to be unicode-encoded for being truncated (else, one char may be
|
||||||
# may be spread on 2 chars). But this method must return an encoded
|
spread on 2 chars).'''
|
||||||
# string, else, ZPT crashes. The same remark holds for m_truncateValue
|
# Param p_width can be None.
|
||||||
# above.
|
if not width: width = 20
|
||||||
uText = text # uText will store the unicode version
|
if isinstance(text, str): text = text.decode('utf-8')
|
||||||
if isinstance(text, str): uText = text.decode('utf-8')
|
if len(text) <= width: return text
|
||||||
if len(uText) <= width: return text
|
return '<acronym title="%s">%s...</acronym>' % (text, text[:width])
|
||||||
return '<acronym title="%s">%s</acronym>' % \
|
|
||||||
(text, uText[:width].encode('utf-8') + '...')
|
|
||||||
|
|
||||||
def splitList(self, l, sub):
|
def splitList(self, l, sub):
|
||||||
'''Returns a list made of the same elements as p_l, but grouped into
|
'''Returns a list made of the same elements as p_l, but grouped into
|
||||||
|
@ -1226,13 +1228,13 @@ class ToolMixin(BaseMixin):
|
||||||
return [f for f in self.getAllAppyTypes(contentType) \
|
return [f for f in self.getAllAppyTypes(contentType) \
|
||||||
if (f.type == 'Pod') and (f.show == 'result')]
|
if (f.type == 'Pod') and (f.show == 'result')]
|
||||||
|
|
||||||
def formatDate(self, aDate, withHour=True):
|
def formatDate(self, date, withHour=True):
|
||||||
'''Returns aDate formatted as specified by tool.dateFormat.
|
'''Returns p_date formatted as specified by tool.dateFormat.
|
||||||
If p_withHour is True, hour is appended, with a format specified
|
If p_withHour is True, hour is appended, with a format specified
|
||||||
in tool.hourFormat.'''
|
in tool.hourFormat.'''
|
||||||
tool = self.appy()
|
tool = self.appy()
|
||||||
res = aDate.strftime(tool.dateFormat)
|
res = date.strftime(tool.dateFormat)
|
||||||
if withHour: res += ' (%s)' % aDate.strftime(tool.hourFormat)
|
if withHour: res += ' (%s)' % date.strftime(tool.hourFormat)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def generateUid(self, className):
|
def generateUid(self, className):
|
||||||
|
|
|
@ -942,12 +942,11 @@ class BaseMixin:
|
||||||
if not name: return wf
|
if not name: return wf
|
||||||
return WorkflowDescriptor.getWorkflowName(wf)
|
return WorkflowDescriptor.getWorkflowName(wf)
|
||||||
|
|
||||||
def getWorkflowLabel(self, stateName=None):
|
def getWorkflowLabel(self, name=None):
|
||||||
'''Gets the i18n label for p_stateName, or for the current object state
|
'''Gets the i18n label for p_name (which can denote a state or a
|
||||||
if p_stateName is not given. Note that if p_stateName is given, it
|
transition), or for the current object state if p_name is None.'''
|
||||||
can also represent the name of a transition.'''
|
name = name or self.State()
|
||||||
stateName = stateName or self.State()
|
return '%s_%s' % (self.getWorkflow(name=True), name)
|
||||||
return '%s_%s' % (self.getWorkflow(name=True), stateName)
|
|
||||||
|
|
||||||
def getTransitions(self, includeFake=True, includeNotShowable=False,
|
def getTransitions(self, includeFake=True, includeNotShowable=False,
|
||||||
grouped=True):
|
grouped=True):
|
||||||
|
|
|
@ -203,7 +203,7 @@ class Translation(ModelClass):
|
||||||
title = gen.String(show=False, indexed=True,
|
title = gen.String(show=False, indexed=True,
|
||||||
page=gen.Page('main',label='Main'))
|
page=gen.Page('main',label='Main'))
|
||||||
def getPoFile(self): pass
|
def getPoFile(self): pass
|
||||||
po = gen.Action(action=getPoFile, result='filetmp')
|
po = gen.Action(action=getPoFile, result='file')
|
||||||
sourceLanguage = gen.String(width=4)
|
sourceLanguage = gen.String(width=4)
|
||||||
def label(self): pass
|
def label(self): pass
|
||||||
def show(self, name): pass
|
def show(self, name): pass
|
||||||
|
|
|
@ -247,6 +247,10 @@ msgstr ""
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -247,6 +247,10 @@ msgstr ""
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -247,6 +247,10 @@ msgstr ""
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr "Zurück zum Anfang"
|
msgstr "Zurück zum Anfang"
|
||||||
|
|
|
@ -248,6 +248,10 @@ msgstr "Action could not be performed on ${nb} element(s)."
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr "Action had no effect."
|
msgstr "Action had no effect."
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr "Are you sure you want to apply this change?"
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr "Go to top"
|
msgstr "Go to top"
|
||||||
|
|
|
@ -247,6 +247,10 @@ msgstr ""
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr "Ir al inicio"
|
msgstr "Ir al inicio"
|
||||||
|
|
|
@ -248,6 +248,10 @@ msgstr "L'action n'a pu être réalisée pour ${nb} élément(s)."
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr "L'action n'a eu aucun effet."
|
msgstr "L'action n'a eu aucun effet."
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr "Êtes-vous sûr de vouloir appliquer ce changement?"
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr "Aller au début"
|
msgstr "Aller au début"
|
||||||
|
|
|
@ -247,6 +247,10 @@ msgstr ""
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr "Andare all'inizio"
|
msgstr "Andare all'inizio"
|
||||||
|
|
|
@ -247,6 +247,10 @@ msgstr ""
|
||||||
msgid "action_null"
|
msgid "action_null"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#. Default: "Are you sure you want to apply this change?"
|
||||||
|
msgid "save_confirm"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#. Default: "Go to top"
|
#. Default: "Go to top"
|
||||||
msgid "goto_first"
|
msgid "goto_first"
|
||||||
msgstr "Ga naar het begin"
|
msgstr "Ga naar het begin"
|
||||||
|
|
|
@ -62,7 +62,7 @@ img { border: 0; vertical-align: middle }
|
||||||
.userStripText { padding: 0 0.3em 0 0.3em; color: white }
|
.userStripText { padding: 0 0.3em 0 0.3em; color: white }
|
||||||
.userStrip a { color: #e7e7e7 }
|
.userStrip a { color: #e7e7e7 }
|
||||||
.userStrip a:visited { color: #e7e7e7 }
|
.userStrip a:visited { color: #e7e7e7 }
|
||||||
.breadcrumb { font-size: 11pt }
|
.breadcrumb { font-size: 11pt; padding-bottom: 6px }
|
||||||
.login { margin: 3px; color: black }
|
.login { margin: 3px; color: black }
|
||||||
input.button { color: #666666; height: 20px; width: 130px;
|
input.button { color: #666666; height: 20px; width: 130px;
|
||||||
cursor:pointer; font-size: 90%; padding: 1px 0 0 10px;
|
cursor:pointer; font-size: 90%; padding: 1px 0 0 10px;
|
||||||
|
@ -104,12 +104,12 @@ td.search { padding-top: 8px }
|
||||||
.dropdownMenu { cursor: pointer; padding-right: 4px; font-size: 93% }
|
.dropdownMenu { cursor: pointer; padding-right: 4px; font-size: 93% }
|
||||||
.dropdown a:hover { text-decoration: underline }
|
.dropdown a:hover { text-decoration: underline }
|
||||||
.list { margin-bottom: 3px }
|
.list { margin-bottom: 3px }
|
||||||
.list td, .list th { border: 1px solid grey;
|
.list td, .list th { border: 3px solid #ededed; color: grey;
|
||||||
padding-left: 5px; padding-right: 5px; padding-top: 3px }
|
padding: 3px 5px 0 5px }
|
||||||
.list th { background-color: #e5e5e5; font-style: italic; font-weight: normal }
|
.list th { background-color: #e5e5e5; font-style: italic; font-weight: normal }
|
||||||
.grid th { font-style: italic; font-weight: normal;
|
.grid th { font-style: italic; font-weight: normal;
|
||||||
border-bottom: 2px solid grey; padding: 2px 2px }
|
border-bottom: 5px solid #fdfdfd; padding: 3px 5px 0 5px }
|
||||||
.grid td { padding-right: 5px }
|
.grid td { padding: 3px 3px 0 3px }
|
||||||
.cellGap { padding-right: 0.4em }
|
.cellGap { padding-right: 0.4em }
|
||||||
.cellDashed { border: 1px dashed grey !important }
|
.cellDashed { border: 1px dashed grey !important }
|
||||||
.noStyle { border: 0 !important; padding: 0 !important; margin: 0 !important }
|
.noStyle { border: 0 !important; padding: 0 !important; margin: 0 !important }
|
||||||
|
@ -127,7 +127,8 @@ td.search { padding-top: 8px }
|
||||||
color: white }
|
color: white }
|
||||||
.even { background-color: #f9f9f9 }
|
.even { background-color: #f9f9f9 }
|
||||||
.odd { background-color: #f4f4f4 }
|
.odd { background-color: #f4f4f4 }
|
||||||
.summary { margin-bottom: 5px; background-color: #f9f9f9 }
|
.summary { margin-bottom: 5px; background-color: #f9f9f9;
|
||||||
|
border: 2px solid #f9f9f9 }
|
||||||
.by { padding: 5px; color: grey; font-size: 97% }
|
.by { padding: 5px; color: grey; font-size: 97% }
|
||||||
.underline { border-bottom: 1px dotted grey }
|
.underline { border-bottom: 1px dotted grey }
|
||||||
.state { font-weight: bold; border-bottom: 1px dashed grey }
|
.state { font-weight: bold; border-bottom: 1px dashed grey }
|
||||||
|
|
|
@ -279,7 +279,7 @@ function doInlineSave(objectUid, name, objectUrl, content){
|
||||||
/* Ajax-saves p_content of field named p_name on object whose id is
|
/* Ajax-saves p_content of field named p_name on object whose id is
|
||||||
p_objectUid and whose URL is p_objectUrl. Asks a confirmation before
|
p_objectUid and whose URL is p_objectUrl. Asks a confirmation before
|
||||||
doing it. */
|
doing it. */
|
||||||
var doIt = confirm('Do it?');
|
var doIt = confirm(save_confirm);
|
||||||
var params = {'action': 'storeFromAjax', 'layoutType': 'view'};
|
var params = {'action': 'storeFromAjax', 'layoutType': 'view'};
|
||||||
var hook = null;
|
var hook = null;
|
||||||
if (!doIt) {
|
if (!doIt) {
|
||||||
|
|
|
@ -587,6 +587,14 @@ class ToolWrapper(AbstractWrapper):
|
||||||
'''Sends a mail. See doc for appy.gen.mail.sendMail.'''
|
'''Sends a mail. See doc for appy.gen.mail.sendMail.'''
|
||||||
sendMail(self, to, subject, body, attachments=attachments)
|
sendMail(self, to, subject, body, attachments=attachments)
|
||||||
|
|
||||||
|
def formatDate(self, date, withHour=True):
|
||||||
|
'''Check doc @ToolMixin::formatDate.'''
|
||||||
|
if not date: return
|
||||||
|
return self.o.formatDate(date, withHour=withHour)
|
||||||
|
|
||||||
|
def getUserName(self, login=None, normalized=False):
|
||||||
|
return self.o.getUserName(login=login, normalized=normalized)
|
||||||
|
|
||||||
def refreshCatalog(self, startObject=None):
|
def refreshCatalog(self, startObject=None):
|
||||||
'''Reindex all Appy objects. For some unknown reason, method
|
'''Reindex all Appy objects. For some unknown reason, method
|
||||||
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
catalog.refreshCatalog is not able to recatalog Appy objects.'''
|
||||||
|
|
|
@ -329,18 +329,17 @@ class AbstractWrapper(object):
|
||||||
<th align=":dleft">:_('action_comment')</th>
|
<th align=":dleft">:_('action_comment')</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr for="event in objs"
|
<tr for="event in objs"
|
||||||
var2="odd=loop.event.odd;
|
var2="rhComments=event.get('comments', None);
|
||||||
rhComments=event.get('comments', None);
|
|
||||||
state=event.get('review_state', None);
|
state=event.get('review_state', None);
|
||||||
action=event['action'];
|
action=event['action'];
|
||||||
isDataChange=action == '_datachange_'"
|
isDataChange=action == '_datachange_'"
|
||||||
class="odd and 'even' or 'odd'" valign="top">
|
class=":loop.event.odd and 'even' or 'odd'" valign="top">
|
||||||
<td if="isDataChange">
|
<td if="isDataChange">
|
||||||
<x>:_('data_change')</x>
|
<x>:_('data_change')</x>
|
||||||
<img if="user.has_role('Manager')" class="clickable"
|
<img if="user.has_role('Manager')" class="clickable"
|
||||||
src=":url('delete')"
|
src=":url('delete')"
|
||||||
onclick=":'onDeleteEvent(%s,%s)' % \
|
onclick=":'onDeleteEvent(%s,%s)' % \
|
||||||
(q(zobj.UID()), q(event['time']))"/>
|
(q(zobj.id), q(event['time']))"/>
|
||||||
</td>
|
</td>
|
||||||
<td if="not isDataChange">:_(zobj.getWorkflowLabel(action))</td>
|
<td if="not isDataChange">:_(zobj.getWorkflowLabel(action))</td>
|
||||||
<td var="actorId=event.get('actor')">
|
<td var="actorId=event.get('actor')">
|
||||||
|
@ -408,7 +407,7 @@ class AbstractWrapper(object):
|
||||||
<img class="clickable" onclick="toggleCookie('appyHistory')"
|
<img class="clickable" onclick="toggleCookie('appyHistory')"
|
||||||
src=":historyExpanded and url('collapse.gif') or url('expand.gif')"
|
src=":historyExpanded and url('collapse.gif') or url('expand.gif')"
|
||||||
align=":dleft" id="appyHistory_img" style="padding-right:4px"/>
|
align=":dleft" id="appyHistory_img" style="padding-right:4px"/>
|
||||||
<x>:_('object_history')</x> ||
|
<x>:_('object_history')</x> —
|
||||||
</x>
|
</x>
|
||||||
|
|
||||||
<!-- Creator and last modification date -->
|
<!-- Creator and last modification date -->
|
||||||
|
@ -437,7 +436,7 @@ class AbstractWrapper(object):
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
<span id="appyHistory"
|
<span id="appyHistory"
|
||||||
style=":historyExpanded and 'display:block' or 'display:none'">
|
style=":historyExpanded and 'display:block' or 'display:none'">
|
||||||
<div var="ajaxHookId=zobj.UID() + '_history'" id=":ajaxHookId">
|
<div var="ajaxHookId=zobj.id + '_history'" id=":ajaxHookId">
|
||||||
<script type="text/javascript">::'askObjectHistory(%s,%s,%d,0)' % \
|
<script type="text/javascript">::'askObjectHistory(%s,%s,%d,0)' % \
|
||||||
(q(ajaxHookId), q(zobj.absolute_url()), \
|
(q(ajaxHookId), q(zobj.absolute_url()), \
|
||||||
historyMaxPerPage)</script>
|
historyMaxPerPage)</script>
|
||||||
|
@ -701,9 +700,10 @@ class AbstractWrapper(object):
|
||||||
elif name == 'session': return self.o.REQUEST.SESSION
|
elif name == 'session': return self.o.REQUEST.SESSION
|
||||||
elif name == 'typeName': return self.__class__.__bases__[-1].__name__
|
elif name == 'typeName': return self.__class__.__bases__[-1].__name__
|
||||||
elif name == 'id': return self.o.id
|
elif name == 'id': return self.o.id
|
||||||
elif name == 'uid': return self.o.UID()
|
elif name == 'uid': return self.o.id
|
||||||
elif name == 'klass': return self.__class__.__bases__[-1]
|
elif name == 'klass': return self.__class__.__bases__[-1]
|
||||||
elif name == 'created': return self.o.created
|
elif name == 'created': return self.o.created
|
||||||
|
elif name == 'creator': return self.o.creator
|
||||||
elif name == 'modified': return self.o.modified
|
elif name == 'modified': return self.o.modified
|
||||||
elif name == 'url': return self.o.absolute_url()
|
elif name == 'url': return self.o.absolute_url()
|
||||||
elif name == 'state': return self.o.State()
|
elif name == 'state': return self.o.State()
|
||||||
|
@ -751,6 +751,14 @@ class AbstractWrapper(object):
|
||||||
if custom: return custom(self, *args, **kwargs)
|
if custom: return custom(self, *args, **kwargs)
|
||||||
|
|
||||||
def getField(self, name): return self.o.getAppyType(name)
|
def getField(self, name): return self.o.getAppyType(name)
|
||||||
|
def getLabel(self, name, type='field'):
|
||||||
|
'''Gets the translated label of field named p_name. If p_type is
|
||||||
|
"workflow", p_name denotes a workflow state or transition, not a
|
||||||
|
field.'''
|
||||||
|
o = self.o
|
||||||
|
if type == 'field': return o.translate(o.getAppyType(name).labelId)
|
||||||
|
elif type == 'workflow': return o.translate(o.getWorkflowLabel(name))
|
||||||
|
|
||||||
def isEmpty(self, name):
|
def isEmpty(self, name):
|
||||||
'''Returns True if value of field p_name is considered as being
|
'''Returns True if value of field p_name is considered as being
|
||||||
empty.'''
|
empty.'''
|
||||||
|
@ -1004,7 +1012,7 @@ class AbstractWrapper(object):
|
||||||
# Determine where to put the result
|
# Determine where to put the result
|
||||||
toDisk = (at != 'string')
|
toDisk = (at != 'string')
|
||||||
if toDisk and not at:
|
if toDisk and not at:
|
||||||
at = getOsTempFolder() + '/' + self.o.UID() + '.xml'
|
at = getOsTempFolder() + '/' + self.o.id + '.xml'
|
||||||
# Create the XML version of the object
|
# Create the XML version of the object
|
||||||
marshaller = XmlMarshaller(cdata=True, dumpUnicode=True,
|
marshaller = XmlMarshaller(cdata=True, dumpUnicode=True,
|
||||||
dumpXmlPrologue=toDisk,
|
dumpXmlPrologue=toDisk,
|
||||||
|
@ -1036,7 +1044,7 @@ class AbstractWrapper(object):
|
||||||
whose values are the previous field values.'''
|
whose values are the previous field values.'''
|
||||||
self.o.addDataChange(data)
|
self.o.addDataChange(data)
|
||||||
|
|
||||||
def getLastEvent(self, transition, notBefore=''):
|
def getLastEvent(self, transition, notBefore=None):
|
||||||
'''Gets, from the object history, the last occurrence of transition
|
'''Gets, from the object history, the last occurrence of transition
|
||||||
named p_transition. p_transition can be a list of names: in this
|
named p_transition. p_transition can be a list of names: in this
|
||||||
case, it returns the most recent occurrence of those transitions. If
|
case, it returns the most recent occurrence of those transitions. If
|
||||||
|
@ -1069,4 +1077,10 @@ class AbstractWrapper(object):
|
||||||
if getattr(workflow, name).__class__.__name__ != 'State': continue
|
if getattr(workflow, name).__class__.__name__ != 'State': continue
|
||||||
res.append((name, o.translate(o.getWorkflowLabel(name))))
|
res.append((name, o.translate(o.getWorkflowLabel(name))))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def path(self, name):
|
||||||
|
'''Returns the absolute file name of file stored in File field p_nnamed
|
||||||
|
p_name.'''
|
||||||
|
v = getattr(self, name)
|
||||||
|
if v: return v.getFilePath(self)
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -288,17 +288,18 @@ class Renderer:
|
||||||
supposed to be in binary format in p_content. The document
|
supposed to be in binary format in p_content. The document
|
||||||
p_format may be: odt or any format in imageFormats.
|
p_format may be: odt or any format in imageFormats.
|
||||||
|
|
||||||
p_anchor, p_wrapInPara and p_size are only relevant for images:
|
p_anchor, p_wrapInPara and p_size, p_sizeUnit and p_style are only
|
||||||
|
relevant for images:
|
||||||
* p_anchor defines the way the image is anchored into the document;
|
* p_anchor defines the way the image is anchored into the document;
|
||||||
Valid values are 'page','paragraph', 'char' and 'as-char';
|
Valid values are 'page','paragraph', 'char' and 'as-char';
|
||||||
* p_wrapInPara, if true, wraps the resulting 'image' tag into a 'p'
|
* p_wrapInPara, if true, wraps the resulting 'image' tag into a 'p'
|
||||||
tag;
|
tag;
|
||||||
* p_size, if specified, is a tuple of float or integers
|
* p_size, if specified, is a tuple of float or integers
|
||||||
(width, height) expressing size in p_sizeUnit (see below).
|
(width, height) expressing size in p_sizeUnit (see below).
|
||||||
If not specified, size will be computed from image info.
|
If not specified, size will be computed from image info;
|
||||||
* p_sizeUnit is the unit for p_size elements, it can be "cm"
|
* p_sizeUnit is the unit for p_size elements, it can be "cm"
|
||||||
(centimeters) or "px" (pixels).
|
(centimeters) or "px" (pixels);
|
||||||
* If p_style is given, it is the content of a "style" attribute,
|
* if p_style is given, it is the content of a "style" attribute,
|
||||||
containing CSS attributes. If "width" and "heigth" attributes are
|
containing CSS attributes. If "width" and "heigth" attributes are
|
||||||
found there, they will override p_size and p_sizeUnit.
|
found there, they will override p_size and p_sizeUnit.
|
||||||
|
|
||||||
|
@ -308,8 +309,7 @@ class Renderer:
|
||||||
'''
|
'''
|
||||||
importer = None
|
importer = None
|
||||||
# Is there someting to import?
|
# Is there someting to import?
|
||||||
if not content and not at:
|
if not content and not at: raise PodError(DOC_NOT_SPECIFIED)
|
||||||
raise PodError(DOC_NOT_SPECIFIED)
|
|
||||||
# Convert Zope files into Appy wrappers.
|
# Convert Zope files into Appy wrappers.
|
||||||
if content.__class__.__name__ in ('File', 'Image'):
|
if content.__class__.__name__ in ('File', 'Image'):
|
||||||
content = FileWrapper(content)
|
content = FileWrapper(content)
|
||||||
|
|
Loading…
Reference in a new issue