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

import logging
import datetime
from Acquisition import aq_chain, aq_parent
from five import grok
from DateTime import DateTime
from zc.relation.interfaces import ICatalog
from zope.container.interfaces import INameChooser
from zope.i18n import translate, negotiate
from zope.component import getUtility
from zope.component.interfaces import ComponentLookupError
from zope.intid.interfaces import IIntIds
from zope.lifecycleevent.interfaces import IObjectAddedEvent, IObjectModifiedEvent
from OFS.interfaces import IObjectWillBeRemovedEvent
from zope.annotation.interfaces import IAnnotations
from plone import api
from plone.stringinterp.adapters import _recursiveGetMembersFromIds
from Products.DCWorkflow.interfaces import IAfterTransitionEvent
from plone.app.discussion.interfaces import ICommentingTool, IConversation
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
from collective.task.content.validation import IValidation
from collective.task.content.information import IInformation
from collective.task.interfaces import IBaseTask
from collective.dms.basecontent.dmsfile import IDmsFile, IDmsAppendixFile
from pfwbged.folder.folder import IFolder
from pfwbged.basecontent.behaviors import IPfwbDocument
from pfwbged.policy.indexers import reindex_after_version_changes
from pfwbged.policy import _
from mail import changeWorkflowState
try:
from plone.app.async.interfaces import IAsyncService
except ImportError:
IAsyncService = None
def has_pfwbgeddocument_workflow(obj):
wtool = api.portal.get_tool('portal_workflow')
return 'pfwbgeddocument_workflow' in wtool.getChainFor(obj)
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
@grok.subscribe(IBaseTask, IObjectAddedEvent)
def set_role_on_document(context, event):
"""Add Reader role to document for the responsible of an
information, opinion, validation.
"""
# recipient_groups is the "Visible par" field
if not ITask.providedBy(context):
document = context.getParentNode()
if IDmsDocument.providedBy(document):
new_recipients = tuple(frozenset(document.recipient_groups or []) | frozenset(context.responsible or []))
cansee_dm = LocalRolesToPrincipalsDataManager(document, IDmsDocument['recipient_groups'])
cansee_dm.set(new_recipients)
document.reindexObjectSecurity()
# do we have to set Editor role on document for ITask ? (if so, remove something for IDmsMail ?)
@grok.subscribe(IDmsFile, IAfterTransitionEvent)
def change_validation_state(context, event):
"""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).
"""
intids = getUtility(IIntIds)
catalog = getUtility(ICatalog)
version_intid = intids.queryId(context)
if version_intid is None:
return
query = {'to_id': version_intid,
'from_interfaces_flattened': IValidation,
'from_attribute': 'target'}
if event.new_state.id == 'refused':
for ref in catalog.findRelations(query):
validation = ref.from_object
if api.content.get_state(validation) == 'todo':
api.content.transition(validation, 'refuse')
validation.reindexObject(idxs=['review_state'])
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')
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))
@grok.subscribe(IDmsFile, IObjectWillBeRemovedEvent)
def delete_tasks(context, event):
"""Delete validations and opinions when a version is deleted.
"""
try:
intids = getUtility(IIntIds)
except ComponentLookupError: # when we remove the Plone site
return
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):
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')
context.reindexObject(idxs=['review_state'])
reindex_after_version_changes(aq_parent(context))
### Workflow for other documents
# @grok.subscribe(IPfwbDocument, IObjectAddedEvent)
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
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'])
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'])
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'])
@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
if document.portal_type in ('dmsoutgoingmail', 'pfwb.apfoutgoingmail') and state == 'writing':
with api.env.adopt_user('admin'):
api.content.transition(obj=document, transition='finish')
document.reindexObject(idxs=['review_state'])
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')
elif state == "assigning":
transition_tasks(document, types=('task'), status='todo', transition='take-responsibility')
# 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'])
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'])
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'):
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)
@grok.subscribe(IDmsDocument, IAfterTransitionEvent)
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')
task.reindexObject(idxs=['review_state'])
@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)
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
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'])
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'
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 + \
'\n\n' + \
translate(_('Document: %s'), **kwargs) % document.title + \
'\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
body += '\n\n\n-- \n' + translate(_('Sent by GED'), **kwargs)
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:
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)
@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)
@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'
subject = '%s - %s' % (context.title, document.title)
body = translate(_('A validation request has been refused'), 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'
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'))
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,
})
# 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)