This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
pfwbged.policy/src/pfwbged/policy/subscribers/document.py

791 lines
31 KiB
Python
Raw Normal View History

2013-11-05 11:50:36 +01:00
import logging
import datetime
2013-11-05 11:50:36 +01:00
2013-08-30 20:35:24 +02:00
from Acquisition import aq_chain, aq_parent
2013-06-25 16:11:09 +02:00
from five import grok
from DateTime import DateTime
2013-06-25 16:11:09 +02:00
2013-07-02 16:32:29 +02:00
from zc.relation.interfaces import ICatalog
2013-08-30 20:35:24 +02:00
from zope.container.interfaces import INameChooser
from zope.i18n import translate, negotiate
2013-07-02 16:32:29 +02:00
from zope.component import getUtility
from zope.component.interfaces import ComponentLookupError
2013-07-02 16:32:29 +02:00
from zope.intid.interfaces import IIntIds
from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
2013-07-04 10:41:17 +02:00
from OFS.interfaces import IObjectWillBeRemovedEvent
from zope.annotation.interfaces import IAnnotations
2013-06-25 16:11:09 +02:00
2013-07-02 16:32:29 +02:00
from plone import api
from plone.stringinterp.adapters import _recursiveGetMembersFromIds
2013-07-02 16:32:29 +02:00
from Products.DCWorkflow.interfaces import IAfterTransitionEvent
from plone.app.discussion.interfaces import ICommentingTool, IConversation
2013-07-02 16:32:29 +02:00
2013-06-25 16:11:09 +02:00
from collective.z3cform.rolefield.field import LocalRolesToPrincipalsDataManager
from collective.dms.basecontent.dmsdocument import IDmsDocument
from collective.dms.basecontent.source import PrincipalSource
from collective.task.content.task import IBaseTask, ITask
2013-07-02 16:32:29 +02:00
from collective.task.content.validation import IValidation
from collective.task.content.information import IInformation
2013-06-25 16:11:09 +02:00
from collective.task.interfaces import IBaseTask
from collective.dms.basecontent.dmsfile import IDmsFile, IDmsAppendixFile
from pfwbged.folder.folder import IFolder
2013-06-25 16:11:09 +02:00
2013-08-30 20:35:24 +02:00
from pfwbged.basecontent.behaviors import IPfwbDocument
from pfwbged.policy.indexers import reindex_after_version_changes
2013-08-30 20:35:24 +02:00
from pfwbged.policy import _
from mail import changeWorkflowState
2013-08-30 20:35:24 +02:00
try:
from plone.app.async.interfaces import IAsyncService
except ImportError:
IAsyncService = None
2013-08-30 20:35:24 +02:00
def has_pfwbgeddocument_workflow(obj):
wtool = api.portal.get_tool('portal_workflow')
return 'pfwbgeddocument_workflow' in wtool.getChainFor(obj)
2013-06-25 16:11:09 +02:00
def has_incomingmail_workflow(obj):
wtool = api.portal.get_tool('portal_workflow')
chain = wtool.getChainFor(obj)
if 'incomingmail_workflow' in chain:
return True
if 'incomingapfmail_workflow' in chain:
return True
return False
2013-06-25 16:11:09 +02:00
@grok.subscribe(IBaseTask, IObjectAddedEvent)
def set_role_on_document(context, event):
2013-07-20 12:58:28 +02:00
"""Add Reader role to document for the responsible of an
information, opinion, validation.
"""
# recipient_groups is the "Visible par" field
2013-06-25 16:11:09 +02:00
if not ITask.providedBy(context):
document = context.getParentNode()
2013-07-02 16:34:14 +02:00
if IDmsDocument.providedBy(document):
new_recipients = tuple(frozenset(document.recipient_groups or []) | frozenset(context.responsible or []))
2013-07-02 16:34:14 +02:00
cansee_dm = LocalRolesToPrincipalsDataManager(document, IDmsDocument['recipient_groups'])
cansee_dm.set(new_recipients)
document.reindexObjectSecurity()
2013-06-25 16:11:09 +02:00
# do we have to set Editor role on document for ITask ? (if so, remove something for IDmsMail ?)
2013-07-02 16:32:29 +02:00
2013-07-20 12:58:28 +02:00
2013-07-02 16:32:29 +02:00
@grok.subscribe(IDmsFile, IAfterTransitionEvent)
def change_validation_state(context, event):
2013-07-20 12:58:28 +02:00
"""If version state is draft, change validation state from todo to refused (transition refuse).
If version state is validated, change validation state from todo to validated (transition validate).
"""
2013-07-02 16:32:29 +02:00
intids = getUtility(IIntIds)
catalog = getUtility(ICatalog)
2013-08-30 11:27:03 +02:00
version_intid = intids.queryId(context)
if version_intid is None:
return
2013-07-02 16:32:29 +02:00
query = {'to_id': version_intid,
'from_interfaces_flattened': IValidation,
'from_attribute': 'target'}
if event.new_state.id == 'refused':
2013-07-02 16:32:29 +02:00
for ref in catalog.findRelations(query):
validation = ref.from_object
if api.content.get_state(validation) == 'todo':
api.content.transition(validation, 'refuse')
2013-07-19 18:17:33 +02:00
validation.reindexObject(idxs=['review_state'])
2013-07-02 16:32:29 +02:00
elif event.new_state.id == 'validated':
for ref in catalog.findRelations(query):
validation = ref.from_object
if api.content.get_state(validation) == 'todo':
api.content.transition(validation, 'validate')
2013-07-19 18:17:33 +02:00
validation.reindexObject(idxs=['review_state'])
elif event.transition and event.transition.id == 'cancel-validation':
for ref in catalog.findRelations(query):
validation = ref.from_object
if api.content.get_state(validation) == 'validated':
api.content.transition(validation, 'cancel-validation')
validation.reindexObject(idxs=['review_state'])
elif event.transition and event.transition.id == 'cancel-refusal':
for ref in catalog.findRelations(query):
validation = ref.from_object
if api.content.get_state(validation) == 'refused':
api.content.transition(validation, 'cancel-refusal')
validation.reindexObject(idxs=['review_state'])
reindex_after_version_changes(aq_parent(context))
2013-07-04 10:41:17 +02:00
2013-07-20 12:58:28 +02:00
2013-07-04 10:41:17 +02:00
@grok.subscribe(IDmsFile, IObjectWillBeRemovedEvent)
def delete_tasks(context, event):
2013-07-20 12:58:28 +02:00
"""Delete validations and opinions when a version is deleted.
"""
try:
intids = getUtility(IIntIds)
except ComponentLookupError: # when we remove the Plone site
return
2013-07-04 10:41:17 +02:00
catalog = getUtility(ICatalog)
version_intid = intids.getId(context)
query = {'to_id': version_intid,
'from_interfaces_flattened': IBaseTask,
'from_attribute': 'target'}
for rv in catalog.findRelations(query):
2013-07-11 15:11:26 +02:00
obj = rv.from_object
#obj.aq_parent.manage_delObjects([obj.getId()]) # we don't want to verify Delete object permission on object
del aq_parent(obj)[obj.getId()]
reindex_after_version_changes(aq_parent(context))
@grok.subscribe(IDmsFile, IObjectAddedEvent)
def version_is_signed_at_creation(context, event):
"""If checkbox signed is checked, finish version without validation after creation"""
if context.signed:
api.content.transition(context, 'finish_without_validation')
2013-09-03 17:40:01 +02:00
context.reindexObject(idxs=['review_state'])
reindex_after_version_changes(aq_parent(context))
2013-08-30 20:35:24 +02:00
### Workflow for other documents
# @grok.subscribe(IPfwbDocument, IObjectAddedEvent)
2013-08-30 20:35:24 +02:00
def create_task_after_creation(context, event):
"""Create a task attributed to creator after document creation"""
# only applies to "other documents"
if not has_pfwbgeddocument_workflow(context):
return
2013-08-30 20:35:24 +02:00
creator = context.Creator()
params = {'responsible': [],
'title': translate(_(u'Process document'),
context=context.REQUEST),
}
task_id = context.invokeFactory('task', 'process-document', **params)
task = context[task_id]
datamanager = LocalRolesToPrincipalsDataManager(task, ITask['responsible'])
datamanager.set((creator,))
task.reindexObject()
@grok.subscribe(ITask, IAfterTransitionEvent)
def task_in_progress(context, event):
"""When a task change state, change parent state
"""
if event.new_state.id == 'in-progress':
# go up in the acquisition chain to find the first task (i.e. the one which is just below the document)
for obj in aq_chain(context):
obj = aq_parent(obj)
if IPfwbDocument.providedBy(obj):
break
# only applies to "other documents"
if not has_pfwbgeddocument_workflow(obj):
return
document = obj
try:
api.content.transition(obj=document, transition='to_process')
except api.exc.InvalidParameterError:
pass
else:
document.reindexObject(idxs=['review_state'])
2013-09-03 17:40:01 +02:00
elif event.new_state.id == 'abandoned':
obj = aq_parent(context)
if not IPfwbDocument.providedBy(obj):
return
# only applies to "other documents"
if not has_pfwbgeddocument_workflow(obj):
return
document = obj
api.content.transition(obj=document, transition='directly_noaction')
document.reindexObject(idxs=['review_state'])
elif event.transition and event.transition.id == 'return-responsibility':
obj = aq_parent(context)
if not IPfwbDocument.providedBy(obj):
return
# only applies to "other documents"
if not has_pfwbgeddocument_workflow(obj):
return
document = obj
api.content.transition(obj=document, transition='back_to_assigning')
document.reindexObject(idxs=['review_state'])
2013-08-30 20:35:24 +02:00
def transition_tasks(obj, types, status, transition):
portal_catalog = api.portal.get_tool('portal_catalog')
tasks = portal_catalog.unrestrictedSearchResults(
portal_type=types, path='/'.join(obj.getPhysicalPath()))
for brain in tasks:
task = brain._unrestrictedGetObject()
if api.content.get_state(obj=task) == status:
print 'changing task', task
with api.env.adopt_user('admin'):
api.content.transition(obj=task, transition=transition)
task.reindexObject(idxs=['review_state'])
2013-08-30 20:35:24 +02:00
@grok.subscribe(IDmsFile, IAfterTransitionEvent)
def version_note_finished(context, event):
"""Launched when version note is finished.
"""
if event.new_state.id == 'finished':
context.reindexObject(idxs=['review_state'])
portal_catalog = api.portal.get_tool('portal_catalog')
document = context.getParentNode()
state = api.content.get_state(obj=document)
# if parent is an outgoing mail, change its state to ready_to_send
2014-03-18 16:29:57 +01:00
if document.portal_type in ('dmsoutgoingmail', 'pfwb.apfoutgoingmail') and state == 'writing':
with api.env.adopt_user('admin'):
api.content.transition(obj=document, transition='finish')
2013-08-30 20:35:24 +02:00
document.reindexObject(idxs=['review_state'])
2013-09-03 17:40:01 +02:00
elif IPfwbDocument.providedBy(document) and has_pfwbgeddocument_workflow(document):
if state == 'processing':
with api.env.adopt_user('admin'):
api.content.transition(obj=document, transition='process')
2013-09-03 17:40:01 +02:00
elif state == "assigning":
transition_tasks(document, types=('task'), status='todo', transition='take-responsibility')
2013-09-03 17:40:01 +02:00
# the document is now in processing state because the task is in progress
api.content.transition(obj=document, transition='process')
document.reindexObject(idxs=['review_state'])
2013-08-30 20:35:24 +02:00
if not has_incomingmail_workflow(document):
# for all documents (but not incoming mails), we transition all
# todo tasks to abandon.
transition_tasks(obj=document, status='todo',
transition='abandon',
types=('opinion', 'validation', 'task',))
document.reindexObject(idxs=['review_state'])
2013-08-30 20:35:24 +02:00
version_notes = portal_catalog.unrestrictedSearchResults(portal_type='dmsmainfile',
path='/'.join(document.getPhysicalPath()))
# make obsolete other versions
for version_brain in version_notes:
version = version_brain._unrestrictedGetObject()
if api.content.get_state(obj=version) in ('draft', 'pending', 'refused', 'validated'):
2013-08-30 20:35:24 +02:00
api.content.transition(obj=version, transition='obsolete')
version.reindexObject(idxs=['review_state'])
context.__ac_local_roles_block__ = False
context.reindexObjectSecurity()
reindex_after_version_changes(document)
2013-08-30 20:35:24 +02:00
@grok.subscribe(IDmsDocument, IAfterTransitionEvent)
2013-08-30 20:35:24 +02:00
def document_is_processed(context, event):
"""When document is processed, close all tasks"""
portal_catalog = api.portal.get_tool('portal_catalog')
if has_pfwbgeddocument_workflow(context) and event.new_state.id not in ('processing',):
tasks = portal_catalog.unrestrictedSearchResults(portal_type='task',
path='/'.join(context.getPhysicalPath()))
for brain in tasks:
task = brain._unrestrictedGetObject()
if api.content.get_state(obj=task) == 'in-progress':
with api.env.adopt_user('admin'):
api.content.transition(obj=task, transition='mark-as-done')
2013-09-03 17:40:01 +02:00
task.reindexObject(idxs=['review_state'])
2013-08-30 20:35:24 +02:00
@grok.subscribe(IDmsDocument, IAfterTransitionEvent)
def document_is_finished(context, event):
"""When document is done for, abandon all tasks"""
portal_catalog = api.portal.get_tool('portal_catalog')
if event.new_state.id not in ('considered', 'noaction', 'sent'):
return
if has_incomingmail_workflow(context):
return
print 'document_is_finished'
transition_tasks(obj=context, status='todo',
transition='abandon',
types=('task', 'opinion', 'validation'))
@grok.subscribe(IDmsDocument, IAfterTransitionEvent)
2013-08-30 20:35:24 +02:00
def document_is_reopened(context, event):
"""When a document is reoponed, create a new task"""
if has_pfwbgeddocument_workflow(context) and event.transition is not None and event.new_state.id == 'assigning':
# Task responsibility has just been returned
if event.old_state.id == 'processing':
return
2013-08-30 20:35:24 +02:00
creator = api.user.get_current().getId()
params = {'responsible': [],
'title': translate(_(u'Process document'),
context=context.REQUEST),
}
chooser = INameChooser(context)
task_id = chooser.chooseName('process-document', context)
task_id = context.invokeFactory('task', task_id, **params)
task = context[task_id]
datamanager = LocalRolesToPrincipalsDataManager(task, ITask['responsible'])
datamanager.set((creator,))
task.reindexObject()
def email_notification_of_tasks_sync(context, event, document, absolute_url, target_language=None):
"""Notify recipients of new tasks by email"""
log = logging.getLogger('pfwbged.policy')
log.info('sending notifications')
document.reindexObject(idxs=['allowedRolesAndUsers'])
2013-11-04 18:32:39 +01:00
for enquirer in (context.enquirer or []):
2014-02-12 11:48:20 +01:00
member = context.portal_membership.getMemberById(enquirer)
if member:
email_from = member.getProperty('email', None)
if email_from:
break
else:
email_from = api.user.get_current().email or api.portal.get().getProperty('email_from_address') or 'admin@localhost'
responsible_labels = []
for responsible in (context.responsible or []):
try:
responsible_labels.append(
PrincipalSource(context).getTerm(responsible).title)
except LookupError:
pass
responsible_label = ', '.join(responsible_labels)
kwargs = {'target_language': target_language}
subject = '%s - %s' % (context.title, document.title)
body = translate(_('You received a request for action in the GED.'), **kwargs)
if responsible_label:
body += '\n\n' + translate(_('Assigned to: %s'), **kwargs) % responsible_label
body += '\n\n' + \
translate(_('Title: %s'), **kwargs) % context.title + \
2013-11-05 11:50:36 +01:00
'\n\n' + \
translate(_('Document: %s'), **kwargs) % document.title + \
2013-11-05 11:50:36 +01:00
'\n\n' + \
translate(_('Document Address: %s'), **kwargs) % absolute_url + \
'\n\n'
try:
body += translate(_('Deadline: %s'), **kwargs) % context.deadline + '\n\n'
except AttributeError:
pass
if context.note:
body += translate(_('Note:'), **kwargs) + '\n\n' + context.note
2014-12-09 07:39:32 +01:00
body += '\n\n\n-- \n' + translate(_('Sent by GED'), **kwargs)
2013-11-05 11:50:36 +01:00
body = body.encode('utf-8')
log.info('sending notifications to %r' % context.responsible)
members = []
for member in _recursiveGetMembersFromIds(api.portal.get(), (context.responsible or [])):
email = member.getProperty('email', None)
if not email:
continue
try:
2013-11-05 11:50:36 +01:00
context.MailHost.send(body, email, 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)
2014-02-24 16:41:05 +01:00
@grok.subscribe(IBaseTask, IObjectAddedEvent)
def email_notification_of_tasks(context, event):
# go up in the acquisition chain to find the document, this cannot be done
# in the async job as absolute_url() needs the request object to give a
# correct result
document = None
for obj in aq_chain(context):
obj = aq_parent(obj)
if IDmsDocument.providedBy(obj):
document = obj
break
if not document:
return
absolute_url = document.absolute_url()
# request is also required to get the target language
target_language = negotiate(context.REQUEST)
kwargs = {
'context': context,
'event': event,
'document': document,
'absolute_url': absolute_url,
'target_language': target_language
}
if IAsyncService is None:
return email_notification_of_tasks_sync(**kwargs)
async = getUtility(IAsyncService)
log = logging.getLogger('pfwbged.policy')
log.info('sending notifications async')
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 and event.transition.id == 'cancel-validation':
comment = translate(_('A previously validated version has returned to waiting validation'), context=context.REQUEST)
elif event.transition and 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)
2014-02-24 16:41:05 +01:00
@grok.subscribe(IValidation, IAfterTransitionEvent)
def email_notification_of_refused_task(context, event):
if event.new_state.id != 'refused':
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'
2014-02-24 16:41:05 +01:00
subject = '%s - %s' % (context.title, document.title)
body = translate(_('A validation request has been refused'), context=context.REQUEST) + \
2014-02-24 16:41:05 +01:00
'\n\n' + \
translate(_('Title: %s'), context=context.REQUEST) % context.title + \
2014-02-24 16:41:05 +01:00
'\n\n' + \
translate(_('Document: %s'), context=context.REQUEST) % document.title + \
2014-02-24 16:41:05 +01:00
'\n\n' + \
translate(_('Document Address: %s'), context=context.REQUEST) % document.absolute_url() + \
2014-02-24 16:41:05 +01:00
'\n\n'
conversation = IConversation(context)
if conversation and conversation.getComments():
last_comment = list(conversation.getComments())[-1]
if (datetime.datetime.utcnow() - last_comment.creation_date).seconds < 120:
# comment less than two minutes ago, include it.
body += translate(_('Note:'), context=context.REQUEST) + '\n\n' + last_comment.text
body += '\n\n\n-- \n' + translate(_('Sent by GED'))
2014-02-24 16:41:05 +01:00
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(ITask, IObjectWillBeRemovedEvent)
def email_notification_of_canceled_subtask(context, event):
document = None
for obj in aq_chain(context):
obj = aq_parent(obj)
if IDmsDocument.providedBy(obj):
document = obj
break
if not document:
return
absolute_url = document.absolute_url()
recipient_emails = []
for recipient in _recursiveGetMembersFromIds(api.portal.get(), (context.responsible or [])):
email = recipient.getProperty('email', None)
if email:
recipient_emails.append(email)
if not recipient_emails:
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 = translate(_('One of your tasks has been cancelled'), context=context.REQUEST) + \
'\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() + \
'\n\n\n\n-- \n' + \
translate(_('Sent by GED'))
body = body.encode('utf-8')
for recipient_email in recipient_emails:
try:
context.MailHost.send(body, recipient_email, 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(IInformation, IObjectWillBeRemovedEvent)
def email_notification_of_canceled_information(context, event):
document = None
for obj in aq_chain(context):
obj = aq_parent(obj)
if IDmsDocument.providedBy(obj):
document = obj
break
if not document:
return
absolute_url = document.absolute_url()
responsible = context.responsible[0]
principal = api.user.get(responsible)
recipient_email = principal.getProperty('email') if principal else None
if recipient_email:
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 = translate(_('One document is not mentioned for your information anymore'), context=context.REQUEST) + \
'\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() + \
'\n\n\n\n-- \n' + \
translate(_('Sent by GED'))
body = body.encode('utf-8')
try:
context.MailHost.send(body, recipient_email, 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, IObjectWillBeRemovedEvent)
def email_notification_of_canceled_validation(context, event):
document = None
for obj in aq_chain(context):
obj = aq_parent(obj)
if IDmsDocument.providedBy(obj):
document = obj
break
if not document:
return
absolute_url = document.absolute_url()
recipient_emails = []
for recipient in _recursiveGetMembersFromIds(api.portal.get(), (context.responsible or [])):
email = recipient.getProperty('email', None)
if email:
recipient_emails.append(email)
if not recipient_emails:
return
for enquirer in (context.enquirer or []):
member = context.portal_membership.getMemberById(enquirer)
if member:
email_from = member.getProperty('email', None)
if email_from:
break
else:
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 = translate(_('A validation request previously sent to you has been deleted'), context=context.REQUEST) + \
'\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() + \
'\n\n\n\n-- \n' + \
translate(_('Sent by GED'))
body = body.encode('utf-8')
for recipient_email in recipient_emails:
try:
context.MailHost.send(body, recipient_email, 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(IDmsDocument, IObjectModifiedEvent)
def log_some_history(context, event):
for description in event.descriptions:
if not hasattr(description, 'attributes'):
continue
for field in ('treated_by', 'treating_groups', 'recipient_groups'):
if not field in description.attributes:
continue
annotations = IAnnotations(context)
if not 'pfwbged_history' in annotations:
annotations['pfwbged_history'] = []
value = getattr(context, field)
annotations['pfwbged_history'].append({'time': DateTime(),
'action_id': 'pfwbged_field',
'action': _('New value for %s: %s') % (field, ', '.join(value)),
'actor_name': api.user.get_current().getId(),
'attribute': field,
'value': value,
})
2014-10-24 14:04:15 +02:00
# assign it back as a change to the list won't trigger the
# annotation to be saved on disk.
annotations['pfwbged_history'] = annotations['pfwbged_history'][:]
@grok.subscribe(IFolder, IObjectAddedEvent)
@grok.subscribe(IDmsDocument, IObjectAddedEvent)
def set_owner_role_on_document(context, event):
"""Makes sure a new document gets its owner role set properly."""
for creator in context.creators:
context.manage_setLocalRoles(creator, ['Owner'])
@grok.subscribe(IBaseTask, IObjectAddedEvent)
def set_permissions_on_task_on_add(context, event):
'''Gives read access to a task for persons that are handling the document'''
# 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
if not hasattr(document, 'treated_by') or not document.treated_by:
return
with api.env.adopt_user('admin'):
for user_id in document.treated_by:
context.manage_addLocalRoles(user_id, ['Reader'])
context.reindexObjectSecurity()
context.reindexObject(idxs=['allowedRolesAndUsers'])
document.reindexObject(idxs=['allowedRolesAndUsers'])
# not enabled for now, see #4516
#@grok.subscribe(IDmsDocument, IObjectModifiedEvent)
def set_permissions_on_task_from_doc(context, event):
portal_catalog = api.portal.get_tool('portal_catalog')
tasks = portal_catalog.unrestrictedSearchResults(
portal_type=['task', 'validation'],
path='/'.join(context.getPhysicalPath()))
if not tasks:
return
for description in event.descriptions:
if not hasattr(description, 'attributes'):
continue
for field in ('treated_by', 'treating_groups', 'recipient_groups'):
if field in description.attributes:
break
else:
return
with api.env.adopt_user('admin'):
tasks = [x.getObject() for x in tasks]
user_ids = []
for user_id, roles in document.get_local_roles():
if 'Reader' in roles or 'Editor' in roles:
user_ids.append(user_id)
for task in tasks:
for task_user_id, task_roles in task.get_local_roles():
if 'Reader' in task_roles and task_user_id not in user_ids:
task.manage_delLocalRoles([task_user_id])
for user_id in user_ids:
task.manage_addLocalRoles(user_id, ['Reader'])
task.reindexObjectSecurity()
task.reindexObject(idxs=['allowedRolesAndUsers'])
@grok.subscribe(IDmsAppendixFile, IObjectAddedEvent)
@grok.subscribe(IDmsFile, IObjectAddedEvent)
def set_permissions_on_files_on_add(context, event):
'''Gives read access to a version/appendix for persons that are handling
the document'''
# 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
if not hasattr(document, 'treated_by') or not document.treated_by:
return
with api.env.adopt_user('admin'):
for user_id in document.treated_by:
context.manage_addLocalRoles(user_id, ['Reader', 'Reviewer'])
context.reindexObjectSecurity()
context.reindexObject(idxs=['allowedRolesAndUsers'])
document.reindexObject(idxs=['allowedRolesAndUsers'])
reindex_after_version_changes(document)