[gen] [gen] Action field: param confirm can be 'text': in this case, the confirm popup contains a textarea whose content is passed as param to the method speficied in param 'action'. Action fields can now be defined in a new layout 'buttons' (show='buttons') in order to appear, on the view layout, besides the extsting series of buttons (edit, next/previous pages...).
This commit is contained in:
parent
960a4c6a46
commit
ccf7e44eef
|
@ -350,7 +350,7 @@ class Field:
|
||||||
for r in res:
|
for r in res:
|
||||||
if r == layoutType: return True
|
if r == layoutType: return True
|
||||||
return
|
return
|
||||||
elif res in ('view', 'edit', 'result'):
|
elif res in ('view', 'edit', 'result', 'buttons'):
|
||||||
return res == layoutType
|
return res == layoutType
|
||||||
return bool(res)
|
return bool(res)
|
||||||
|
|
||||||
|
|
|
@ -29,18 +29,22 @@ class Action(Field):
|
||||||
pxView = pxCell = Px('''
|
pxView = pxCell = Px('''
|
||||||
<form var="formId='%s_%s_form' % (zobj.id, name);
|
<form var="formId='%s_%s_form' % (zobj.id, name);
|
||||||
label=_(field.labelId);
|
label=_(field.labelId);
|
||||||
|
descr=field.descrId and _(field.descrId) or None;
|
||||||
buttonWidth=ztool.getButtonWidth(label)"
|
buttonWidth=ztool.getButtonWidth(label)"
|
||||||
id=":formId" action=":ztool.absolute_url() + '/do'">
|
id=":formId" action=":ztool.absolute_url() + '/do'">
|
||||||
<input type="hidden" name="action" value="ExecuteAction"/>
|
<input type="hidden" name="action" value="ExecuteAction"/>
|
||||||
<input type="hidden" name="objectUid" value=":zobj.id"/>
|
<input type="hidden" name="objectUid" value=":zobj.id"/>
|
||||||
<input type="hidden" name="fieldName" value=":name"/>
|
<input type="hidden" name="fieldName" value=":name"/>
|
||||||
<input if="field.confirm" type="button" class="button"
|
<input type="hidden" name="comment" value=""/>
|
||||||
var="labelConfirm=_(field.labelId + '_confirm')" value=":label"
|
<input if="field.confirm" type="button" class="button" title=":descr"
|
||||||
|
var="labelConfirm=_(field.labelId + '_confirm');
|
||||||
|
commentParam=(field.confirm == 'text') and 'true' or 'false'"
|
||||||
|
value=":label"
|
||||||
style=":'%s; %s' % (url('action', bg=True), buttonWidth)"
|
style=":'%s; %s' % (url('action', bg=True), buttonWidth)"
|
||||||
onclick=":'askConfirm(%s,%s,%s)' % (q('form'), q(formId), \
|
onclick=":'askConfirm(%s,%s,%s,%s)' % (q('form'), q(formId), \
|
||||||
q(labelConfirm))"/>
|
q(labelConfirm), commentParam)"/>
|
||||||
<input if="not field.confirm" type="submit" class="button" name="do"
|
<input if="not field.confirm" type="submit" class="button" name="do"
|
||||||
value=":label"
|
value=":label" title=":descr"
|
||||||
style=":'%s; %s' % (url('action', bg=True), buttonWidth)"/>
|
style=":'%s; %s' % (url('action', bg=True), buttonWidth)"/>
|
||||||
</form>''')
|
</form>''')
|
||||||
|
|
||||||
|
@ -78,13 +82,24 @@ class Action(Field):
|
||||||
|
|
||||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
|
|
||||||
|
def callAction(self, obj, method, hasParam, param):
|
||||||
|
'''Calls p_method on p_obj. m_method can be the single action as defined
|
||||||
|
in self.action or one of them is self.action contains several
|
||||||
|
methods. Calling m_method can be done with a p_param (when p_hasParam
|
||||||
|
is True), ie, when self.confirm is "text".'''
|
||||||
|
if hasParam: return method(obj, param)
|
||||||
|
else: return method(obj)
|
||||||
|
|
||||||
def __call__(self, obj):
|
def __call__(self, obj):
|
||||||
'''Calls the action on p_obj.'''
|
'''Calls the action on p_obj.'''
|
||||||
|
# Must we call the method(s) with a param ?
|
||||||
|
hasParam = self.confirm == 'text'
|
||||||
|
param = hasParam and obj.request.get('comment', None)
|
||||||
if type(self.action) in sutils.sequenceTypes:
|
if type(self.action) in sutils.sequenceTypes:
|
||||||
# There are multiple Python methods
|
# There are multiple Python methods
|
||||||
res = [True, '']
|
res = [True, '']
|
||||||
for act in self.action:
|
for act in self.action:
|
||||||
actRes = act(obj)
|
actRes = self.callAction(obj, act, hasParam, param)
|
||||||
if type(actRes) in sutils.sequenceTypes:
|
if type(actRes) in sutils.sequenceTypes:
|
||||||
res[0] = res[0] and actRes[0]
|
res[0] = res[0] and actRes[0]
|
||||||
if self.result.startswith('file'):
|
if self.result.startswith('file'):
|
||||||
|
@ -95,7 +110,7 @@ class Action(Field):
|
||||||
res[0] = res[0] and actRes
|
res[0] = res[0] and actRes
|
||||||
else:
|
else:
|
||||||
# There is only one Python method
|
# There is only one Python method
|
||||||
actRes = self.action(obj)
|
actRes = self.callAction(obj, self.action, hasParam, param)
|
||||||
if type(actRes) in sutils.sequenceTypes:
|
if type(actRes) in sutils.sequenceTypes:
|
||||||
res = list(actRes)
|
res = list(actRes)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -57,8 +57,8 @@ def sendMail(config, to, subject, body, attachments=None, log=None):
|
||||||
else:
|
else:
|
||||||
msg = ''
|
msg = ''
|
||||||
if log:
|
if log:
|
||||||
log('mail disabled%s: should send mail from %s to %s.' % \
|
log('mail disabled%s: should send mail from %s to %d ' \
|
||||||
(msg, fromAddress, str(to)))
|
'recipient(s): %s.' % (msg, fromAddress, len(to), str(to)))
|
||||||
log('subject: %s' % subject)
|
log('subject: %s' % subject)
|
||||||
log('body: %s' % body)
|
log('body: %s' % body)
|
||||||
if attachments and log: log('%d attachment(s).' % len(attachments))
|
if attachments and log: log('%d attachment(s).' % len(attachments))
|
||||||
|
|
|
@ -816,13 +816,15 @@ class BaseMixin:
|
||||||
cssJs['js'] = js or ()
|
cssJs['js'] = js or ()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getAppyTypes(self, layoutType, pageName):
|
def getAppyTypes(self, layoutType, pageName, type=None):
|
||||||
'''Returns the list of fields that belong to a given page (p_pageName)
|
'''Returns the list of fields that belong to a given page (p_pageName)
|
||||||
for a given p_layoutType. If p_pageName is None, fields of all pages
|
for a given p_layoutType. If p_pageName is None, fields of all pages
|
||||||
are returned.'''
|
are returned. If p_type is defined, only fields of this p_type are
|
||||||
|
returned. '''
|
||||||
res = []
|
res = []
|
||||||
for field in self.getAllAppyTypes():
|
for field in self.getAllAppyTypes():
|
||||||
if pageName and (field.page.name != pageName): continue
|
if pageName and (field.page.name != pageName): continue
|
||||||
|
if type and (field.type != type): continue
|
||||||
if not field.isShowable(self, layoutType): continue
|
if not field.isShowable(self, layoutType): continue
|
||||||
res.append(field)
|
res.append(field)
|
||||||
return res
|
return res
|
||||||
|
|
|
@ -72,6 +72,7 @@ input.buttonSmall { width: 100px !important; font-size: 85%; height: 18px;
|
||||||
.help { cursor: help }
|
.help { cursor: help }
|
||||||
.refLink { font-style: italic; padding-left: 5px; font-size: 90%; color: grey }
|
.refLink { font-style: italic; padding-left: 5px; font-size: 90%; color: grey }
|
||||||
.buttons { margin-left: 4px }
|
.buttons { margin-left: 4px }
|
||||||
|
.objectButtons { margin-top: 5px }
|
||||||
.message { position: absolute; top: -40px; left: 50%; font-size: 90%;
|
.message { position: absolute; top: -40px; left: 50%; font-size: 90%;
|
||||||
width: 600px; border: 1px #F0C36D solid; padding: 6px;
|
width: 600px; border: 1px #F0C36D solid; padding: 6px;
|
||||||
background-color: #F9EDBE; text-align: center; margin-left: -300px;
|
background-color: #F9EDBE; text-align: center; margin-left: -300px;
|
||||||
|
|
|
@ -381,22 +381,19 @@ class AbstractWrapper(object):
|
||||||
pxTransitions = Px('''
|
pxTransitions = Px('''
|
||||||
<form var="transitions=targetObj.getTransitions()" if="transitions"
|
<form var="transitions=targetObj.getTransitions()" if="transitions"
|
||||||
var2="formId='trigger_%s' % targetObj.id" method="post"
|
var2="formId='trigger_%s' % targetObj.id" method="post"
|
||||||
id=":formId" action=":targetObj.absolute_url() + '/do'">
|
id=":formId" action=":targetObj.absolute_url() + '/do'"
|
||||||
|
style="display: inline">
|
||||||
<input type="hidden" name="action" value="Trigger"/>
|
<input type="hidden" name="action" value="Trigger"/>
|
||||||
<input type="hidden" name="transition"/>
|
<input type="hidden" name="transition"/>
|
||||||
<!-- Input field for storing the comment coming from the popup -->
|
<!-- Input field for storing the comment coming from the popup -->
|
||||||
<textarea id="comment" name="comment" cols="30" rows="3"
|
<textarea id="comment" name="comment" cols="30" rows="3"
|
||||||
style="display:none"></textarea>
|
style="display:none"></textarea>
|
||||||
<table cellpadding="0" cellspacing="0">
|
<x for="transition in transitions">
|
||||||
<tr valign="middle">
|
<!-- Render a transition or a group of transitions. -->
|
||||||
<td align=":dright" for="transition in transitions">
|
<x if="transition.type == 'transition'">:transition.pxView</x>
|
||||||
<!-- Render a transition or a group of transitions. -->
|
<x if="transition.type == 'group'"
|
||||||
<x if="transition.type == 'transition'">:transition.pxView</x>
|
var2="uiGroup=transition">:uiGroup.px</x>
|
||||||
<x if="transition.type == 'group'"
|
</x>
|
||||||
var2="uiGroup=transition">:uiGroup.px</x>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>''')
|
</form>''')
|
||||||
|
|
||||||
# Displays header information about an object: title, workflow-related info,
|
# Displays header information about an object: title, workflow-related info,
|
||||||
|
@ -458,103 +455,107 @@ class AbstractWrapper(object):
|
||||||
# Shows the range of buttons (next, previous, save,...) and the workflow
|
# Shows the range of buttons (next, previous, save,...) and the workflow
|
||||||
# transitions for a given object.
|
# transitions for a given object.
|
||||||
pxButtons = Px('''
|
pxButtons = Px('''
|
||||||
<table cellpadding="2" cellspacing="0" style="margin-top: 7px"
|
<div class="objectButtons" style=":'float: %s' % dleft"
|
||||||
var="previousPage=phaseObj.getPreviousPage(page)[0];
|
var="previousPage=phaseObj.getPreviousPage(page)[0];
|
||||||
nextPage=phaseObj.getNextPage(page)[0];
|
nextPage=phaseObj.getNextPage(page)[0];
|
||||||
isEdit=layoutType == 'edit';
|
isEdit=layoutType == 'edit';
|
||||||
mayAct=not isEdit and zobj.mayAct();
|
mayAct=not isEdit and zobj.mayAct();
|
||||||
pageInfo=phaseObj.pagesInfo[page]">
|
pageInfo=phaseObj.pagesInfo[page]">
|
||||||
<tr valign="top">
|
<!-- Refresh -->
|
||||||
<!-- Refresh -->
|
<a if="zobj.isDebug()"
|
||||||
<td if="zobj.isDebug()">
|
href=":zobj.getUrl(mode=layoutType, page=page, refresh='yes', \
|
||||||
<a href=":zobj.getUrl(mode=layoutType, page=page, refresh='yes', \
|
inPopup=inPopup)">
|
||||||
inPopup=inPopup)">
|
<img title="Refresh" style="vertical-align:top" src=":url('refresh')"/>
|
||||||
<img title="Refresh" style="vertical-align:top" src=":url('refresh')"/>
|
</a>
|
||||||
</a>
|
<!-- Previous -->
|
||||||
</td>
|
<x if="previousPage and pageInfo.showPrevious"
|
||||||
<!-- Previous -->
|
var2="label=_('page_previous');
|
||||||
<td if="previousPage and pageInfo.showPrevious"
|
buttonWidth=ztool.getButtonWidth(label)">
|
||||||
var2="label=_('page_previous');
|
<!-- Button on the edit page -->
|
||||||
buttonWidth=ztool.getButtonWidth(label)">
|
<x if="isEdit">
|
||||||
<!-- Button on the edit page -->
|
<input type="button" class="button" value=":label"
|
||||||
<x if="isEdit">
|
onClick="submitAppyForm('previous')"
|
||||||
<input type="button" class="button" value=":label"
|
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"/>
|
||||||
onClick="submitAppyForm('previous')"
|
<input type="hidden" name="previousPage" value=":previousPage"/>
|
||||||
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"/>
|
</x>
|
||||||
<input type="hidden" name="previousPage" value=":previousPage"/>
|
<!-- Button on the view page -->
|
||||||
</x>
|
<input if="not isEdit" type="button" class="button" value=":label"
|
||||||
<!-- Button on the view page -->
|
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"
|
||||||
<input if="not isEdit" type="button" class="button" value=":label"
|
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
|
||||||
style=":'%s; %s' % (url('previous', bg=True), buttonWidth)"
|
inPopup=inPopup))"/>
|
||||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=previousPage, \
|
</x>
|
||||||
inPopup=inPopup))"/>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- Save -->
|
<!-- Save -->
|
||||||
<td if="isEdit and pageInfo.showSave">
|
<input if="isEdit and pageInfo.showSave"
|
||||||
<input type="button" class="button" onClick="submitAppyForm('save')"
|
type="button" class="button" onClick="submitAppyForm('save')"
|
||||||
var="label=_('object_save')" value=":label"
|
var2="label=_('object_save')" value=":label"
|
||||||
style=":'%s; %s' % (url('save', bg=True), \
|
style=":'%s; %s' % (url('save', bg=True), \
|
||||||
ztool.getButtonWidth(label))" />
|
ztool.getButtonWidth(label))" />
|
||||||
</td>
|
|
||||||
<!-- Cancel -->
|
|
||||||
<td if="isEdit and pageInfo.showCancel">
|
|
||||||
<input type="button" class="button" onClick="submitAppyForm('cancel')"
|
|
||||||
var="label=_('object_cancel')" value=":label"
|
|
||||||
style=":'%s; %s' % (url('cancel', bg=True), \
|
|
||||||
ztool.getButtonWidth(label))"/>
|
|
||||||
</td>
|
|
||||||
<td if="not isEdit"
|
|
||||||
var2="locked=zobj.isLocked(user, page);
|
|
||||||
editable=pageInfo.showOnEdit and pageInfo.showEdit and \
|
|
||||||
mayAct and zobj.mayEdit()">
|
|
||||||
|
|
||||||
<!-- Edit -->
|
<!-- Cancel -->
|
||||||
<input type="button" class="button" if="editable and not locked"
|
<input if="isEdit and pageInfo.showCancel"
|
||||||
var="label=_('object_edit')" value=":label"
|
type="button" class="button" onClick="submitAppyForm('cancel')"
|
||||||
style=":'%s; %s' % (url('edit', bg=True), \
|
var2="label=_('object_cancel')" value=":label"
|
||||||
ztool.getButtonWidth(label))"
|
style=":'%s; %s' % (url('cancel', bg=True), \
|
||||||
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page, \
|
ztool.getButtonWidth(label))"/>
|
||||||
inPopup=inPopup))"/>
|
|
||||||
|
|
||||||
<!-- Locked -->
|
<x if="not isEdit"
|
||||||
<a if="editable and locked">
|
var2="locked=zobj.isLocked(user, page);
|
||||||
<img style="cursor: help"
|
editable=pageInfo.showOnEdit and pageInfo.showEdit and \
|
||||||
var="lockDate=ztool.formatDate(locked[1]);
|
mayAct and zobj.mayEdit()">
|
||||||
lockMap={'user':ztool.getUserName(locked[0]), \
|
|
||||||
'date':lockDate};
|
|
||||||
lockMsg=_('page_locked', mapping=lockMap)"
|
|
||||||
src=":url('lockedBig')" title=":lockMsg"/></a>
|
|
||||||
<a if="editable and locked and user.has_role('Manager')">
|
|
||||||
<img class="clickable" title=":_('page_unlock')"
|
|
||||||
src=":url('unlockBig')"
|
|
||||||
onclick=":'onUnlockPage(%s,%s)' % (q(zobj.id), q(page))"/></a>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<!-- Next -->
|
<!-- Edit -->
|
||||||
<td if="nextPage and pageInfo.showNext"
|
<input type="button" class="button" if="editable and not locked"
|
||||||
var2="label=_('page_next');
|
var="label=_('object_edit')" value=":label"
|
||||||
buttonWidth=ztool.getButtonWidth(label)">
|
style=":'%s; %s' % (url('edit', bg=True), \
|
||||||
<!-- Button on the edit page -->
|
ztool.getButtonWidth(label))"
|
||||||
<x if="isEdit">
|
onclick=":'goto(%s)' % q(zobj.getUrl(mode='edit', page=page, \
|
||||||
<input type="button" class="button" onClick="submitAppyForm('next')"
|
inPopup=inPopup))"/>
|
||||||
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
|
||||||
value=":label"/>
|
<!-- Locked -->
|
||||||
<input type="hidden" name="nextPage" value=":nextPage"/>
|
<a if="editable and locked">
|
||||||
</x>
|
<img style="cursor: help"
|
||||||
<!-- Button on the view page -->
|
var="lockDate=ztool.formatDate(locked[1]);
|
||||||
<input if="not isEdit" type="button" class="button" value=":label"
|
lockMap={'user':ztool.getUserName(locked[0]), \
|
||||||
|
'date':lockDate};
|
||||||
|
lockMsg=_('page_locked', mapping=lockMap)"
|
||||||
|
src=":url('lockedBig')" title=":lockMsg"/></a>
|
||||||
|
<a if="editable and locked and user.has_role('Manager')">
|
||||||
|
<img class="clickable" title=":_('page_unlock')"
|
||||||
|
src=":url('unlockBig')"
|
||||||
|
onclick=":'onUnlockPage(%s,%s)' % (q(zobj.id), q(page))"/></a>
|
||||||
|
</x>
|
||||||
|
|
||||||
|
<!-- Next -->
|
||||||
|
<x if="nextPage and pageInfo.showNext"
|
||||||
|
var2="label=_('page_next');
|
||||||
|
buttonWidth=ztool.getButtonWidth(label)">
|
||||||
|
<!-- Button on the edit page -->
|
||||||
|
<x if="isEdit">
|
||||||
|
<input type="button" class="button" onClick="submitAppyForm('next')"
|
||||||
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
||||||
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
|
value=":label"/>
|
||||||
inPopup=inPopup))"/>
|
<input type="hidden" name="nextPage" value=":nextPage"/>
|
||||||
</td>
|
</x>
|
||||||
|
<!-- Button on the view page -->
|
||||||
|
<input if="not isEdit" type="button" class="button" value=":label"
|
||||||
|
style=":'%s; %s' % (url('next', bg=True), buttonWidth)"
|
||||||
|
onclick=":'goto(%s)' % q(zobj.getUrl(page=nextPage, \
|
||||||
|
inPopup=inPopup))"/>
|
||||||
|
</x>
|
||||||
|
|
||||||
<!-- Workflow transitions -->
|
<!-- Workflow transitions -->
|
||||||
<td var="targetObj=zobj; buttonsMode='normal'"
|
<x var="targetObj=zobj; buttonsMode='normal'"
|
||||||
if="mayAct and \
|
if="mayAct and \
|
||||||
targetObj.showTransitions(layoutType)">:obj.pxTransitions</td>
|
targetObj.showTransitions(layoutType)">:obj.pxTransitions</x>
|
||||||
</tr>
|
</div>
|
||||||
</table>''')
|
<!-- Fields (actions) defined with layout "buttons" -->
|
||||||
|
<x if="layoutType != 'edit'">
|
||||||
|
<div class="objectButtons" style=":'float: %s' % dleft"
|
||||||
|
var="fields=zobj.getAppyTypes('buttons', page, type='Action');
|
||||||
|
layoutType='view'" if="fields">
|
||||||
|
<x for="field in fields">:field.pxRender</x></div>
|
||||||
|
</x>''')
|
||||||
|
|
||||||
# Displays the fields of a given page for a given object.
|
# Displays the fields of a given page for a given object.
|
||||||
pxFields = Px('''
|
pxFields = Px('''
|
||||||
|
@ -1199,4 +1200,28 @@ class AbstractWrapper(object):
|
||||||
return localRoles
|
return localRoles
|
||||||
|
|
||||||
def raiseUnauthorized(self, msg=None): return self.o.raiseUnauthorized(msg)
|
def raiseUnauthorized(self, msg=None): return self.o.raiseUnauthorized(msg)
|
||||||
|
|
||||||
|
def sendMailIf(self, privilege, subject, body, attachments=None,
|
||||||
|
isRole=False):
|
||||||
|
'''Sends a mail related to this object to any active user having
|
||||||
|
p_privilege on it. If p_isRole is False, p_privilege is a permission.
|
||||||
|
Else, it is a role.'''
|
||||||
|
# Determine the list of recipients based on active users having
|
||||||
|
# p_privilege on p_self.
|
||||||
|
recipients = []
|
||||||
|
for user in self.tool.users:
|
||||||
|
if (user.state == 'inactive'): continue
|
||||||
|
# Check if the user has p_privilege on this object
|
||||||
|
hasPrivilege = isRole and user.has_role or user.has_permission
|
||||||
|
if not hasPrivilege(privilege, self): continue
|
||||||
|
# Get the mail recipient for this user
|
||||||
|
recipient = user.getMailRecipient()
|
||||||
|
if not recipient: continue
|
||||||
|
recipients.append(recipient)
|
||||||
|
if recipients:
|
||||||
|
self.tool.sendMail(recipients, subject, body, attachments)
|
||||||
|
else:
|
||||||
|
name = isRole and 'role' or 'permission'
|
||||||
|
self.log('no recipient for sending mail about %s with %s %s.' % \
|
||||||
|
(self.id, name, privilege))
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue