Allow return to initial state of a validation request #22008
This commit is contained in:
parent
abd293f19c
commit
6f94857196
|
@ -107,6 +107,40 @@ class CanValidateOrRefuse(grok.View):
|
|||
return False
|
||||
|
||||
|
||||
class CanCancelRefusal(grok.View):
|
||||
grok.name("can_cancel_refusal")
|
||||
grok.context(IDmsFile)
|
||||
grok.require('zope2.View')
|
||||
|
||||
def render(self):
|
||||
|
||||
workflow = api.portal.get_tool('portal_workflow')
|
||||
with api.env.adopt_user('admin'):
|
||||
review_history = workflow.getInfoFor(self.context, 'review_history')
|
||||
if not review_history:
|
||||
return False
|
||||
last_transition = review_history[-1]
|
||||
return last_transition.get('action') == 'refuse' and \
|
||||
last_transition.get('actor') == api.user.get_current().id
|
||||
|
||||
|
||||
class CanCancelValidation(grok.View):
|
||||
grok.name("can_cancel_validation")
|
||||
grok.context(IDmsFile)
|
||||
grok.require('zope2.View')
|
||||
|
||||
def render(self):
|
||||
|
||||
workflow = api.portal.get_tool('portal_workflow')
|
||||
with api.env.adopt_user('admin'):
|
||||
review_history = workflow.getInfoFor(self.context, 'review_history')
|
||||
if not review_history:
|
||||
return False
|
||||
last_transition = review_history[-1]
|
||||
return last_transition.get('action') == 'validate' and \
|
||||
last_transition.get('actor') == api.user.get_current().id
|
||||
|
||||
|
||||
class CanBeTrashedDmsFile(grok.View):
|
||||
""""""
|
||||
grok.name('can_be_trashed')
|
||||
|
|
|
@ -27,7 +27,15 @@ msgstr "L'élément ${title} a été supprimé."
|
|||
msgid "${title} is locked and cannot be deleted."
|
||||
msgstr "L'élément ${title} est verrouillé et ne peut être supprimé."
|
||||
|
||||
#: ../subscribers/document.py:430
|
||||
#: ../subscribers/document.py:417
|
||||
msgid "A previously refused version has returned to waiting validation"
|
||||
msgstr "Un document précédemment refusé est revenu en attente de validation"
|
||||
|
||||
#: ../subscribers/document.py:415
|
||||
msgid "A previously validated version has returned to waiting validation"
|
||||
msgstr "Un document précédemment validé est revenu en attente de validation"
|
||||
|
||||
#: ../subscribers/document.py:495
|
||||
msgid "A validation request has been refused"
|
||||
msgstr "Une demande de validation a été refusée."
|
||||
|
||||
|
@ -86,10 +94,18 @@ msgstr "Visible par"
|
|||
msgid "Cancel"
|
||||
msgstr "Annuler"
|
||||
|
||||
#: ../menu.py:58
|
||||
msgid "Cancel refusal of ${version}"
|
||||
msgstr "Annuler le refus de la version ${version}"
|
||||
|
||||
#: ../menu.py:56
|
||||
msgid "Cancel validate and finish ${version}"
|
||||
msgstr "Annuler la validation et finalisation de la version ${version}"
|
||||
|
||||
#: ../menu.py:57
|
||||
msgid "Cancel validation of ${version}"
|
||||
msgstr "Annuler la validation de la version ${version}"
|
||||
|
||||
#: ../browser/refuse.py:33
|
||||
#: ../browser/send_by_email.py:35
|
||||
#: ../browser/to_process.py:25
|
||||
|
|
|
@ -61,6 +61,14 @@ msgstr "Retour en à traiter"
|
|||
msgid "Back to writing"
|
||||
msgstr "Retour à la rédaction"
|
||||
|
||||
#: ../profiles/default/workflows/versionnote_workflow/definition.xml
|
||||
msgid "Cancel refusal"
|
||||
msgstr "Annuler le refus"
|
||||
|
||||
#: ../profiles/default/workflows/versionnote_workflow/definition.xml
|
||||
msgid "Cancel validation"
|
||||
msgstr "Annuler la validation"
|
||||
|
||||
#: ../profiles/default/workflows/incomingapfmail_workflow/definition.xml
|
||||
#: ../profiles/default/workflows/incomingmail_workflow/definition.xml
|
||||
#: ../profiles/default/workflows/pfwbgeddocument_workflow/definition.xml
|
||||
|
|
|
@ -29,7 +29,15 @@ msgstr ""
|
|||
msgid "${title} is locked and cannot be deleted."
|
||||
msgstr ""
|
||||
|
||||
#: ../subscribers/document.py:430
|
||||
#: ../subscribers/document.py:417
|
||||
msgid "A previously refused version has returned to waiting validation"
|
||||
msgstr ""
|
||||
|
||||
#: ../subscribers/document.py:415
|
||||
msgid "A previously validated version has returned to waiting validation"
|
||||
msgstr ""
|
||||
|
||||
#: ../subscribers/document.py:495
|
||||
msgid "A validation request has been refused"
|
||||
msgstr ""
|
||||
|
||||
|
@ -88,10 +96,18 @@ msgstr ""
|
|||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
#: ../menu.py:58
|
||||
msgid "Cancel refusal of ${version}"
|
||||
msgstr ""
|
||||
|
||||
#: ../menu.py:56
|
||||
msgid "Cancel validate and finish ${version}"
|
||||
msgstr ""
|
||||
|
||||
#: ../menu.py:57
|
||||
msgid "Cancel validation of ${version}"
|
||||
msgstr ""
|
||||
|
||||
#: ../browser/refuse.py:33
|
||||
#: ../browser/send_by_email.py:35
|
||||
#: ../browser/to_process.py:25
|
||||
|
|
|
@ -62,6 +62,14 @@ msgstr ""
|
|||
msgid "Back to writing"
|
||||
msgstr ""
|
||||
|
||||
#: ../profiles/default/workflows/versionnote_workflow/definition.xml
|
||||
msgid "Cancel refusal"
|
||||
msgstr ""
|
||||
|
||||
#: ../profiles/default/workflows/versionnote_workflow/definition.xml
|
||||
msgid "Cancel validation"
|
||||
msgstr ""
|
||||
|
||||
#: ../profiles/default/workflows/incomingapfmail_workflow/definition.xml
|
||||
#: ../profiles/default/workflows/incomingmail_workflow/definition.xml
|
||||
#: ../profiles/default/workflows/pfwbgeddocument_workflow/definition.xml
|
||||
|
|
|
@ -54,6 +54,8 @@ dmsfile_wfactions_mapping = {'ask_opinion': _(u"Ask opinion about version ${vers
|
|||
'send_with_docbow': _(u"Send version ${version} with PES"),
|
||||
'restore_from_trash': _(u"Restore version ${version}"),
|
||||
'back_to_draft': _(u"Cancel validate and finish ${version}"),
|
||||
'cancel-validation': _(u"Cancel validation of ${version}"),
|
||||
'cancel-refusal': _(u"Cancel refusal of ${version}"),
|
||||
}
|
||||
|
||||
|
||||
|
@ -232,6 +234,21 @@ class CustomMenu(menu.WorkflowMenu):
|
|||
title = IGNORE_(title, mapping={'version': version})
|
||||
cssClass += ' version-action version-id-%s' % context.id
|
||||
cssClass += ' version-action-%s' % action['id']
|
||||
|
||||
# limit cancelling actions to a version's rightful owner
|
||||
# guards are bypassed for Managers, so call them at least once
|
||||
action_guards = {
|
||||
'cancel-refusal': 'can_cancel_refusal',
|
||||
'cancel-validation': 'can_cancel_validation',
|
||||
}
|
||||
if action['id'] in action_guards:
|
||||
view = queryMultiAdapter(
|
||||
(context, request),
|
||||
name=action_guards.get(action['id'])
|
||||
)
|
||||
if view and not view.render():
|
||||
action['allowed'] = False
|
||||
|
||||
else:
|
||||
title = action['title']
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
<exit-transition transition_id="submit"/>
|
||||
<exit-transition transition_id="ask_opinion"/>
|
||||
<exit-transition transition_id="obsolete"/>
|
||||
<exit-transition transition_id="cancel-refusal"/>
|
||||
<permission-map name="View" acquired="False">
|
||||
<permission-role>Editor</permission-role>
|
||||
<permission-role>Manager</permission-role>
|
||||
|
@ -109,6 +110,7 @@
|
|||
<exit-transition transition_id="finish"/>
|
||||
<exit-transition transition_id="obsolete"/>
|
||||
<exit-transition transition_id="submit"/>
|
||||
<exit-transition transition_id="cancel-validation"/>
|
||||
<permission-map name="View" acquired="False">
|
||||
<permission-role>Editor</permission-role>
|
||||
<permission-role>Manager</permission-role>
|
||||
|
@ -203,6 +205,18 @@
|
|||
<guard-expression>here/@@can_validate_or_refuse</guard-expression>
|
||||
</guard>
|
||||
</transition>
|
||||
<transition transition_id="cancel-validation" title="Cancel validation" new_state="pending" trigger="USER" before_script="" after_script="" i18n:attributes="title">
|
||||
<action url="" category="workflow" icon="" i18n:translate="">Cancel validation</action>
|
||||
<guard>
|
||||
<guard-expression>here/@@can_cancel_validation</guard-expression>
|
||||
</guard>
|
||||
</transition>
|
||||
<transition transition_id="cancel-refusal" title="Cancel refusal" new_state="pending" trigger="USER" before_script="" after_script="" i18n:attributes="title">
|
||||
<action url="" category="workflow" icon="" i18n:translate="">Cancel validation</action>
|
||||
<guard>
|
||||
<guard-expression>here/@@can_cancel_refusal</guard-expression>
|
||||
</guard>
|
||||
</transition>
|
||||
<variable variable_id="action" for_catalog="False" for_status="True" update_always="True">
|
||||
<description>Previous transition</description>
|
||||
<default>
|
||||
|
|
|
@ -100,6 +100,16 @@ def change_validation_state(context, event):
|
|||
if api.content.get_state(validation) == 'todo':
|
||||
api.content.transition(validation, 'validate')
|
||||
validation.reindexObject(idxs=['review_state'])
|
||||
elif event.transition.id == 'cancel-validation':
|
||||
for ref in catalog.findRelations(query):
|
||||
validation = ref.from_object
|
||||
api.content.transition(validation, 'cancel-validation')
|
||||
validation.reindexObject(idxs=['review_state'])
|
||||
elif event.transition.id == 'cancel-refusal':
|
||||
for ref in catalog.findRelations(query):
|
||||
validation = ref.from_object
|
||||
api.content.transition(validation, 'cancel-refusal')
|
||||
validation.reindexObject(idxs=['review_state'])
|
||||
|
||||
|
||||
@grok.subscribe(IDmsFile, IObjectWillBeRemovedEvent)
|
||||
|
@ -397,6 +407,63 @@ def email_notification_of_tasks(context, event):
|
|||
job = async.queueJob(email_notification_of_tasks_sync, **kwargs)
|
||||
|
||||
|
||||
@grok.subscribe(IValidation, IAfterTransitionEvent)
|
||||
def email_notification_of_validation_reversal(context, event):
|
||||
"""Notify a validation requester when their previously validated
|
||||
(or refused) request has returned to pending state"""
|
||||
if not event.transition:
|
||||
return
|
||||
elif event.transition.id == 'cancel-validation':
|
||||
comment = translate(_('A previously validated version has returned to waiting validation'), context=context.REQUEST)
|
||||
elif event.transition.id == 'cancel-refusal':
|
||||
comment = translate(_('A previously refused version has returned to waiting validation'), context=context.REQUEST)
|
||||
else:
|
||||
return
|
||||
|
||||
# go up in the acquisition chain to find the document
|
||||
document = None
|
||||
for obj in aq_chain(context):
|
||||
obj = aq_parent(obj)
|
||||
if IDmsDocument.providedBy(obj):
|
||||
document = obj
|
||||
break
|
||||
if not document:
|
||||
return
|
||||
|
||||
email_enquirer = None
|
||||
for enquirer in (context.enquirer or []):
|
||||
member = context.portal_membership.getMemberById(enquirer)
|
||||
if member:
|
||||
email_enquirer = member.getProperty('email', None)
|
||||
if email_enquirer:
|
||||
break
|
||||
|
||||
if not email_enquirer:
|
||||
return
|
||||
|
||||
email_from = api.user.get_current().email or api.portal.get().getProperty('email_from_address') or 'admin@localhost'
|
||||
|
||||
subject = '%s - %s' % (context.title, document.title)
|
||||
|
||||
body = comment + \
|
||||
'\n\n' + \
|
||||
translate(_('Title: %s'), context=context.REQUEST) % context.title + \
|
||||
'\n\n' + \
|
||||
translate(_('Document: %s'), context=context.REQUEST) % document.title + \
|
||||
'\n\n' + \
|
||||
translate(_('Document Address: %s'), context=context.REQUEST) % document.absolute_url()
|
||||
|
||||
body += '\n\n\n-- \n' + translate(_('Sent by GED'))
|
||||
body = body.encode('utf-8')
|
||||
|
||||
try:
|
||||
context.MailHost.send(body, email_enquirer, email_from, subject, charset='utf-8')
|
||||
except Exception as e:
|
||||
# do not abort transaction in case of email error
|
||||
log = logging.getLogger('pfwbged.policy')
|
||||
log.exception(e)
|
||||
|
||||
|
||||
@grok.subscribe(IValidation, IAfterTransitionEvent)
|
||||
def email_notification_of_refused_task(context, event):
|
||||
if event.new_state.id != 'refused':
|
||||
|
|
Reference in New Issue