def notify_configuration_changed(event):
    """Event subscriber that is called every time the configuration changed.
    """
    portal = getSite()
    wftool = getToolByName(portal, 'portal_workflow', None)

    if IRecordModifiedEvent.providedBy(event):
        # Discussion control panel setting changed
        if event.record.fieldName == 'moderation_enabled':
            # Moderation enabled has changed
            if event.record.value == True:
                # Enable moderation workflow
                wftool.setChainForPortalTypes(('Discussion Item',),
                                              'comment_review_workflow')
            else:
                # Disable moderation workflow
                wftool.setChainForPortalTypes(('Discussion Item',),
                                              'one_state_workflow')

    if IConfigurationChangedEvent.providedBy(event):
        # Types control panel setting changed
        if 'workflow' in event.data:
            registry = queryUtility(IRegistry)
            settings = registry.forInterface(IDiscussionSettings, check=False)
            wf = wftool.getChainForPortalType('Discussion Item')[0]
            if wf == 'one_state_workflow':
                settings.moderation_enabled = False
            elif wf == 'comment_review_workflow':
                settings.moderation_enabled = True
            else:
                # Custom workflow
                pass
def notify_configuration_changed(event):
    """Event subscriber that is called every time the configuration changed.
    """
    portal = getSite()
    wftool = getToolByName(portal, 'portal_workflow', None)

    if IRecordModifiedEvent.providedBy(event):
        # Discussion control panel setting changed
        if event.record.fieldName == 'moderation_enabled':
            # Moderation enabled has changed
            if event.record.value == True:
                # Enable moderation workflow
                wftool.setChainForPortalTypes(('Discussion Item', ),
                                              'comment_review_workflow')
            else:
                # Disable moderation workflow
                wftool.setChainForPortalTypes(('Discussion Item', ),
                                              'one_state_workflow')

    if IConfigurationChangedEvent.providedBy(event):
        # Types control panel setting changed
        if 'workflow' in event.data:
            registry = queryUtility(IRegistry)
            settings = registry.forInterface(IDiscussionSettings, check=False)
            workflow_chain = wftool.getChainForPortalType('Discussion Item')
            if workflow_chain:
                workflow = workflow_chain[0]
                if workflow == 'one_state_workflow':
                    settings.moderation_enabled = False
                elif workflow == 'comment_review_workflow':
                    settings.moderation_enabled = True
                else:
                    # Custom workflow
                    pass
 def set_what(self, value):
     if IConfigurationChangedEvent.providedBy(value):
         self.data["what"] = "configuration"
         self.what_info = self.get_configuration_changed_info()
         self.event = value
     elif IRecordModifiedEvent.providedBy(value):
         self.data["what"] = "registry"
         self.what_info = self.get_record_modified_info()
         self.event = value
Example #4
0
def user_related_modification(event):
    """
        Manage user modification
          * ignored Products.PluggableAuthService.interfaces.events.IPrincipalCreatedEvent
          * ignored Products.PluggableAuthService.interfaces.events.IPrincipalDeletedEvent
    """
    # we pass if the config change is not related to users
    if IConfigurationChangedEvent.providedBy(event) and not isinstance(event.context, UserDataConfiglet):
        return
    # we pass if the registry change is not related to plonegroup
    if (IRecordModifiedEvent.providedBy(event) and event.record.interfaceName and
            event.record.interface != IContactPlonegroupConfig):
        return
    invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.AssignedUsersVocabulary')
Example #5
0
def user_related_modification(event):
    """
        Manage user modification
          * ignored Products.PluggableAuthService.interfaces.events.IPrincipalCreatedEvent
          * ignored Products.PluggableAuthService.interfaces.events.IPrincipalDeletedEvent
    """
    # we pass if the config change is not related to users
    if IConfigurationChangedEvent.providedBy(event) and not isinstance(event.context, UserDataConfiglet):
        return

    # we pass if the registry change is not related to plonegroup
    if (IRecordModifiedEvent.providedBy(event) and event.record.interfaceName and
            event.record.interface != IContactPlonegroupConfig):
        return
    invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.AssignedUsersWithDeactivatedVocabulary')
    invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.AssignedUsersForFacetedFilterVocabulary')
Example #6
0
def registry_logger(event):
    """Log registry events like records being modified."""
    # subscriber is registered even if package has not yet been installed
    # ignore any error caused by missing registry records
    try:
        record = IFingerPointingSettings.__identifier__ + '.audit_registry'
        audit_registry = api.portal.get_registry_record(record)
    except InvalidParameterError:
        return

    if audit_registry:
        user, ip = get_request_information()

        if IRecordModifiedEvent.providedBy(event):
            action = 'modify'
            extras = u'object={0} value={1}'.format(event.record, event.record.value)

        log_info(AUDIT_MESSAGE.format(user, ip, action, extras))
Example #7
0
def registry_logger(event):
    """Log registry events like records being modified."""
    # subscriber is registered even if package has not yet been installed
    # ignore any error caused by missing registry records
    try:
        record = IFingerPointingSettings.__identifier__ + '.audit_registry'
        audit_registry = api.portal.get_registry_record(record)
    except InvalidParameterError:
        return

    if audit_registry:
        user, ip = get_request_information()

        if IRecordModifiedEvent.providedBy(event):
            action = 'modify'
            extras = u'object={0} value={1}'.format(event.record,
                                                    event.record.value)

        log_info(AUDIT_MESSAGE.format(user, ip, action, extras))
Example #8
0
def modified_pipeline(obj, event):
    if event.record.__name__ != 'collective.contact.importexport.interfaces.IPipelineConfiguration.pipeline':
        return
    if IRecordModifiedEvent.providedBy(
            event) and event.oldValue == event.newValue:
        return
    if not hasattr(event, 'newValue'):
        new_val = event.record.value
    else:
        new_val = event.newValue
    if new_val is None:
        return
    new_val = safe_encode(new_val)
    path = get_main_path()
    pipeline_path = os.path.join(path, 'pipeline.cfg')
    fd = open(pipeline_path, 'w')
    fd.writelines(new_val)
    fd.close()
    logger.info('Pipeline was placed in {}'.format(pipeline_path))
Example #9
0
def wsclient_configuration_changed(event):
    """ call original subscriber and do more stuff """
    if IRecordModifiedEvent.providedBy(event):
        # generated_actions changed, we need to update generated actions in portal_actions
        if event.record.fieldName == 'generated_actions':
            notify_configuration_changed(event)
            portal = api.portal.get()
            ids = []
            object_buttons = portal.portal_actions.object_buttons
            portlet_actions = portal.portal_actions.object_portlet
            for object_button in object_buttons.objectValues():
                if object_button.id.startswith('plonemeeting_wsclient_action_'):
                    ids.append(object_button.id)
                    if object_button.id in portlet_actions:
                        api.content.delete(portlet_actions[object_button.id])
                    api.content.copy(object_button, portlet_actions)
            existing_pos = portlet_actions.getObjectPosition('im-listing')
            for i, aid in enumerate(ids):
                portlet_actions.moveObjectToPosition(aid, existing_pos + i)
def registry_logger(event):
    """Log registry events like records being modified."""
    name = IFingerPointingSettings.__identifier__ + '.audit_registry'
    audit_registry = api.portal.get_registry_record(name, default=False)
    if not audit_registry:
        return

    user, ip = get_request_information()

    if IRecordModifiedEvent.providedBy(event):
        action = 'modify'
        extras = 'object={0} value={1}'.format(
            event.record,
            safe_utf8(event.record.value),
        )
    else:  # should never happen
        action = '-'
        extras = 'object' + event.record

    log_info(AUDIT_MESSAGE.format(user, ip, action, extras))
def registry_logger(event):
    """Log registry events like records being modified."""
    name = IFingerPointingSettings.__identifier__ + '.audit_registry'
    audit_registry = api.portal.get_registry_record(name, default=False)
    if not audit_registry:
        return

    user, ip = get_request_information()

    if IRecordModifiedEvent.providedBy(event):
        action = 'modify'
        extras = 'object={0} value={1}'.format(
            event.record,
            _safe_native_string(event.record.value),
        )
    else:  # should never happen
        action = '-'
        extras = 'object' + event.record

    log_info(AUDIT_MESSAGE.format(user, ip, action, extras))
def registry_logger(event):
    """Log registry events like records being modified."""
    name = IFingerPointingSettings.__identifier__ + '.audit_registry'
    audit_registry = api.portal.get_registry_record(name, default=False)
    if not audit_registry:
        return

    user, ip = get_request_information()

    if IRecordModifiedEvent.providedBy(event):
        action = 'modify'
        extra_info = {
            'object': event.record,
            'value': safe_utf8(event.record.value)
        }
    else:  # should never happen
        action = '-'
        extra_info = {'object': event.record}

    extras = log_info.format_extras('registry', event, action, extra_info)
    log_info(AUDIT_MESSAGE.format(user, ip, action, extras))
Example #13
0
def notify_configuration_changed(event):
    """Event subscriber that is called every time the configuration changed."""
    portal = getSite()

    if IRecordModifiedEvent.providedBy(event):
        # generated_actions changed, we need to update generated actions in portal_actions
        if event.record.fieldName == 'generated_actions':
            # if generated_actions have been changed, remove every existing generated_actions then recreate them
            # first remove every actions starting with ACTION_SUFFIX
            object_buttons = portal.portal_actions.object_buttons
            for object_button in object_buttons.objectValues():
                if object_button.id.startswith(ACTION_SUFFIX):
                    object_buttons.manage_delObjects([object_button.id])
            # then recreate them
            i = 1
            ws4pmSettings = getMultiAdapter((portal, portal.REQUEST), name='ws4pmclient-settings')
            for actToGen in event.record.value:
                actionId = "%s%d" % (ACTION_SUFFIX, i)
                action = Action(
                    actionId,
                    title=translate(
                        'Send to',
                        domain='imio.pm.wsclient',
                        mapping={
                            'meetingConfigTitle':
                            ws4pmSettings.getMeetingConfigTitle(actToGen['pm_meeting_config_id']),
                        },
                        context=portal.REQUEST),
                    description='', i18n_domain='imio.pm.wsclient',
                    url_expr='string:${object_url}/@@send_to_plonemeeting_form?meetingConfigId=%s'
                             % actToGen['pm_meeting_config_id'],
                    icon_expr='string:${portal_url}/++resource++imio.pm.wsclient.images/send_to_plonemeeting.png',
                    available_expr=actToGen['condition'] or '',
                    # make sure we have a tuple as permissions value
                    permissions=actToGen['permissions'] and (actToGen['permissions'], ) or ('View', ),
                    visible=True)
                object_buttons._setObject(actionId, action)
                i = i + 1
    def get_what_info(self, event):
        #Archetypes
        if IObjectEditedEvent.providedBy(event):
            return self.get_object_modified_info(event)
        elif IObjectInitializedEvent.providedBy(event):
            return self.get_object_modified_info(event)
        #plone
        elif IConfigurationChangedEvent.providedBy(event):
            return self.get_configuration_changed_info(event)
#        #plone.app.form (formlib)
#        elif IEditSavedEvent.providedBy(event):
#            return 'editsaved'
#        #plone.app.iterate
#        elif ICheckinEvent.providedBy(event):
#            return 'checkedin'
#        elif ICheckoutEvent.providedBy(event):
#            return 'checkedout'
#        elif IWorkingCopyDeletedEvent.providedBy(event):
#            return 'workingcopydeleted'
#        #plone.app.registry
        elif IRecordModifiedEvent.providedBy(event):
            return self.get_record_modified_info(event)
        # DCWorkflow
        elif ITransitionEvent.providedBy(event):
            return self.get_transition_info(event)
        # CMFCore
        elif IActionSucceededEvent.providedBy(event):
            return self.get_action_succeed_info(event)
        # zope
        elif IObjectCopiedEvent.providedBy(event):
            return self.get_object_copied_info(event)
        elif IObjectMovedEvent.providedBy(event):
            return self.get_object_moved_info(event)
        elif IObjectAddedEvent.providedBy(event):
            return self.get_object_moved_info(event)
        elif IObjectRemovedEvent.providedBy(event):
            return self.get_object_moved_info(event)
Example #15
0
def notify_configuration_changed(event):
    """
    Event subscriber that is called when configuration has changed.
    """

    portal = getSite()
    js_registry = getToolByName(portal, 'portal_javascripts', None)

    if IRecordModifiedEvent.providedBy(event):
        # AddThis control panel setting changed
        if event.record.fieldName == 'addthis_load_asynchronously':
            jsid = '++resource++collective.addthis/addthis.js'
            if event.record.value == True:
                # Enable asynchronous loading
                js = js_registry.getResource(jsid)
                if not js.getEnabled():
                    js.setEnabled(True)
            else:
                # Disable asynchronous loading
                js = js_registry.getResource(jsid)
                if js.getEnabled():
                    js.setEnabled(False)

                js_registry
def notify_configuration_changed(event):
    """
    Event subscriber that is called when configuration has changed.
    """

    portal = getSite()
    js_registry = getToolByName(portal, 'portal_javascripts', None)

    if IRecordModifiedEvent.providedBy(event):
        # AddThis control panel setting changed
        if event.record.fieldName == 'addthis_load_asynchronously':
            jsid = '++resource++collective.addthis/addthis.js'
            if event.record.value == True:
                # Enable asynchronous loading
                js = js_registry.getResource(jsid)
                if not js.getEnabled():
                    js.setEnabled(True)
            else:
                # Disable asynchronous loading
                js = js_registry.getResource(jsid)
                if js.getEnabled():
                    js.setEnabled(False)

                js_registry
Example #17
0
def imiodmsmail_settings_changed(event):
    """ Manage a record change """
    if (IRecordModifiedEvent.providedBy(event) and event.record.interfaceName
            and event.record.interface != IImioDmsMailConfig):
        return
    if event.record.fieldName == 'mail_types':
        invalidate_cachekey_volatile_for(
            'imio.dms.mail.vocabularies.IMMailTypesVocabulary')
        invalidate_cachekey_volatile_for(
            'imio.dms.mail.vocabularies.IMActiveMailTypesVocabulary')
    if event.record.fieldName == 'omail_types':
        invalidate_cachekey_volatile_for(
            'imio.dms.mail.vocabularies.OMMailTypesVocabulary')
        invalidate_cachekey_volatile_for(
            'imio.dms.mail.vocabularies.OMActiveMailTypesVocabulary')
    if event.record.fieldName == 'assigned_user_check':
        update_transitions_auc_config('dmsincomingmail')  # i_e ok
        n_plus_x = 'imio.dms.mail.wfadaptations.IMServiceValidation' in \
                   [adapt['adaptation'] for adapt in get_applied_adaptations()]
        snoi = False
        if event.newValue == u'no_check' or not n_plus_x:
            snoi = True
        portal = api.portal.get()
        folder = portal['incoming-mail']['mail-searches']
        if folder['to_treat_in_my_group'].showNumberOfItems != snoi:
            folder['to_treat_in_my_group'].showNumberOfItems = snoi  # noqa
            folder['to_treat_in_my_group'].reindexObject()
    if event.record.fieldName in ('org_templates_encoder_can_edit',
                                  'org_email_templates_encoder_can_edit'):
        folder_id = ('email' in event.record.fieldName) and 'oem' or 'om'
        portal = api.portal.get()
        main_folder = portal.templates[folder_id]
        s_orgs = get_registry_organizations()
        roles = ['Reader']
        all_roles = ['Reader', 'Contributor', 'Editor']
        if api.portal.get_registry_record(event.record.__name__):
            roles = list(all_roles)
        for uid in s_orgs:
            if uid not in main_folder:
                continue
            folder = main_folder[uid]
            groupname = '{}_encodeur'.format(uid)
            api.group.revoke_roles(groupname=groupname,
                                   roles=all_roles,
                                   obj=folder)
            api.group.grant_roles(groupname=groupname, roles=roles, obj=folder)

    if event.record.fieldName == 'imail_group_encoder':
        if api.portal.get_registry_record(
                'imio.dms.mail.browser.settings.IImioDmsMailConfig.imail_group_encoder'
        ):
            configure_group_encoder(['dmsincomingmail', 'dmsincoming_email'])
    if event.record.fieldName == 'omail_group_encoder':
        if api.portal.get_registry_record(
                'imio.dms.mail.browser.settings.IImioDmsMailConfig.omail_group_encoder'
        ):
            # configure_group_encoder(['dmsoutgoingmail', 'dmsoutgoing_email'])
            configure_group_encoder(['dmsoutgoingmail'])
    if event.record.fieldName == 'contact_group_encoder':
        if api.portal.get_registry_record(
                'imio.dms.mail.browser.settings.IImioDmsMailConfig.contact_group_encoder'
        ):
            configure_group_encoder(
                ['organization', 'person', 'held_position', 'contact_list'],
                contacts_part=True)
            # set permission on contacts directory
            portal = api.portal.get()
            portal['contacts'].manage_permission(
                'imio.dms.mail: Write mail base fields',
                ('Manager', 'Site Administrator', 'Contributor'),
                acquire=1)
    if event.record.fieldName == 'groups_hidden_in_dashboard_filter':
        invalidate_cachekey_volatile_for(
            'imio.dms.mail.vocabularies.TreatingGroupsForFacetedFilterVocabulary'
        )
    if event.record.fieldName == 'users_hidden_in_dashboard_filter':
        invalidate_cachekey_volatile_for(
            'imio.dms.mail.vocabularies.AssignedUsersForFacetedFilterVocabulary'
        )
    if event.record.__name__ == 'imio.dms.mail.imail_folder_period' and event.newValue is not None:
        portal = api.portal.get()
        setattr(portal[MAIN_FOLDERS['dmsincomingmail']], 'folder_period',
                event.newValue)
    if event.record.__name__ == 'imio.dms.mail.omail_folder_period' and event.newValue is not None:
        portal = api.portal.get()
        setattr(portal[MAIN_FOLDERS['dmsoutgoingmail']], 'folder_period',
                event.newValue)
Example #18
0
def contact_plonegroup_change(event):
    """Event handler when contact.plonegroup records are modified.

    * update workflow dms config (new groups).
    * invalidate vocabulary caches.
    * set localroles on contacts for _encodeur groups.
    * add a directory by organization in templates/om, templates/oem and contacts/contact-lists-folder.
    * set local roles on contacts, incoming-mail for group_encoder.
    """
    if (IRecordModifiedEvent.providedBy(event) and event.record.interfaceName and
            event.record.interface == IContactPlonegroupConfig):
        registry = getUtility(IRegistry)
        s_orgs = get_registry_organizations()
        s_fcts = get_registry_functions()
        if not s_fcts or not s_orgs:
            return
        # we update dms config
        update_transitions_auc_config('dmsincomingmail')  # i_e ok
        update_transitions_levels_config(['dmsincomingmail', 'dmsoutgoingmail', 'task'])  # i_e ok
        # invalidate vocabularies caches
        invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.CreatingGroupVocabulary')
        invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.ActiveCreatingGroupVocabulary')
        invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.TreatingGroupsWithDeactivatedVocabulary')
        invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.TreatingGroupsForFacetedFilterVocabulary')

        portal = api.portal.get()
        # contributor on a contact can edit too
        for folder in (portal['outgoing-mail'], portal['contacts'],
                       portal['contacts']['contact-lists-folder']['common']):
            dic = folder.__ac_local_roles__
            for principal in dic.keys():
                if principal.endswith('_encodeur'):
                    del dic[principal]
            for uid in s_orgs:
                dic["%s_encodeur" % uid] = ['Contributor']
            folder._p_changed = True
        # we add a directory by organization in templates/om
        om_folder = portal.templates.om
        oem_folder = portal.templates.oem
        base_model = om_folder.get('main', None)
        cl_folder = portal.contacts['contact-lists-folder']
        for uid in s_orgs:
            obj = uuidToObject(uid, unrestricted=True)
            full_title = obj.get_full_title(separator=' - ', first_index=1)
            if uid not in om_folder:
                folder = api.content.create(container=om_folder, type='Folder', id=uid, title=full_title)
                # alsoProvides(folder, IActionsPanelFolderOnlyAdd)  # made now in subscriber
                # alsoProvides(folder, INextPrevNotNavigable)
                roles = ['Reader']
                if registry['imio.dms.mail.browser.settings.IImioDmsMailConfig.org_templates_encoder_can_edit']:
                    roles += ['Contributor', 'Editor']
                api.group.grant_roles(groupname='%s_encodeur' % uid, roles=roles, obj=folder)
                folder.reindexObjectSecurity()
                if base_model and base_model.has_been_modified():
                    logger.info("Copying %s in %s" % (base_model, '/'.join(folder.getPhysicalPath())))
                    api.content.copy(source=base_model, target=folder)
            if uid not in oem_folder:
                folder = api.content.create(container=oem_folder, type='Folder', id=uid, title=full_title)
                roles = ['Reader']
                if registry['imio.dms.mail.browser.settings.IImioDmsMailConfig.org_email_templates_encoder_can_edit']:
                    roles += ['Contributor', 'Editor']
                api.group.grant_roles(groupname='%s_encodeur' % uid, roles=roles, obj=folder)
                folder.reindexObjectSecurity()
                # if base_model and base_model.has_been_modified():
                #    logger.info("Copying %s in %s" % (base_model, '/'.join(folder.getPhysicalPath())))
                #    api.content.copy(source=base_model, target=folder)
            if uid not in cl_folder:
                folder = api.content.create(container=cl_folder, type='Folder', id=uid, title=full_title)
                folder.setLayout('folder_tabular_view')
                roles = ['Reader', 'Contributor', 'Editor']
                api.group.grant_roles(groupname='%s_encodeur' % uid, roles=roles, obj=folder)
                folder.reindexObjectSecurity()
        # we manage local roles to give needed permissions related to group_encoder
        options_config = {portal['incoming-mail']: ['imail_group_encoder'],
                          portal['outgoing-mail']: ['omail_group_encoder'],
                          portal['contacts']: ['imail_group_encoder', 'omail_group_encoder', 'contact_group_encoder'],
                          portal['contacts']['contact-lists-folder']['common']: ['imail_group_encoder',
                                                                                 'omail_group_encoder',
                                                                                 'contact_group_encoder']}
        ge_config = {opt: api.portal.get_registry_record('imio.dms.mail.browser.settings.IImioDmsMailConfig.{}'.format(
            opt), default=False) for opt in ('imail_group_encoder', 'omail_group_encoder', 'contact_group_encoder')}

        group_encoder_config = [dic for dic in s_fcts if dic['fct_id'] == CREATING_GROUP_SUFFIX]  # noqa F812
        if group_encoder_config:
            orgs = group_encoder_config[0]['fct_orgs']
            for folder in options_config:
                if any([ge_config[opt] for opt in options_config[folder]]):
                    dic = folder.__ac_local_roles__
                    for principal in dic.keys():
                        if principal.endswith(CREATING_GROUP_SUFFIX):
                            del dic[principal]
                    for uid in orgs:
                        dic["{}_{}".format(uid, CREATING_GROUP_SUFFIX)] = ['Contributor']
                    folder._p_changed = True
def connection_setting_update(settings, event):
    if IRecordModifiedEvent.providedBy(event):
        # Memcached control panel setting changed
        if event.record.fieldName == 'memcached_hosts':
            mc_client = getUtility(ICacheChooser)
            mc_client.connection_setting_number += 1
Example #20
0
def manageIImioDmsMailConfigChange(event):
    """ Manage a record change """
    if (IRecordModifiedEvent.providedBy(event) and event.record.interfaceName and
            event.record.interface == IImioDmsMailConfig and event.record.fieldName == 'mail_types'):
        invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.IMMailTypesVocabulary')
        invalidate_cachekey_volatile_for('imio.dms.mail.vocabularies.IMActiveMailTypesVocabulary')
def detectContactPlonegroupChange(event):
    """
        Manage our record changes
    """
    if IRecordModifiedEvent.providedBy(
            event):  # and event.record.interface == IContactPlonegroupConfig:
        changes = False
        # this can be called before plonegroup is installed and registry contains relevant keys
        try:
            registry_orgs = get_registry_organizations()
        except InvalidParameterError:
            registry_orgs = []
        if event.record.fieldName == 'organizations' and registry_orgs:
            old_set = set(event.oldValue)
            new_set = set(event.newValue)
            # we detect a new organization
            add_set = new_set.difference(old_set)
            for orga_uid in add_set:
                orga = uuidToObject(orga_uid)
                for fct_dic in get_registry_functions():
                    enabled = fct_dic['enabled']
                    if enabled is False:
                        continue
                    fct_orgs = fct_dic['fct_orgs']
                    if fct_orgs and orga_uid not in fct_orgs:
                        continue
                    if addOrModifyGroup(orga, fct_dic['fct_id'],
                                        fct_dic['fct_title']):
                        changes = True
            # we detect a removed organization. We dont do anything on exsiting groups
            if old_set.difference(new_set):
                changes = True
        elif event.record.fieldName == 'functions' and registry_orgs:
            old_functions = {
                dic['fct_id']: {
                    'fct_title': dic['fct_title'],
                    'fct_orgs': dic['fct_orgs'],
                    'enabled': dic['enabled']
                }
                for dic in event.oldValue
            }
            old_set = set(old_functions.keys())
            new_functions = {
                dic['fct_id']: {
                    'fct_title': dic['fct_title'],
                    'fct_orgs': dic['fct_orgs'],
                    'enabled': dic['enabled']
                }
                for dic in event.newValue
            }
            new_set = set(new_functions.keys())
            # we detect a new function
            add_set = new_set.difference(old_set)
            for new_id in add_set:
                new_title = new_functions[new_id]['fct_title']
                new_orgs = new_functions[new_id]['fct_orgs']
                enabled = new_functions[new_id]['enabled']
                for orga_uid in registry_orgs:
                    if new_orgs and orga_uid not in new_orgs:
                        continue
                    if enabled is False:
                        continue
                    orga = uuidToObject(orga_uid)
                    if addOrModifyGroup(orga, new_id, new_title):
                        changes = True
            # we detect a removed function
            # We may remove Plone groups as we checked before that every are empty
            removed_set = old_set.difference(new_set)
            for removed_id in removed_set:
                for orga_uid in get_organizations(only_selected=False,
                                                  the_objects=False):
                    plone_group_id = get_plone_group_id(orga_uid, removed_id)
                    plone_group = api.group.get(plone_group_id)
                    if plone_group:
                        api.group.delete(plone_group_id)
                        changes = True
            # we detect existing functions for which 'fct_orgs' changed
            for new_id, new_function_infos in new_functions.items():
                new_title = new_function_infos['fct_title']
                new_orgs = new_function_infos['fct_orgs']
                enabled = new_function_infos['enabled']
                if not new_orgs and enabled is True:
                    # we have to make sure Plone groups are created for every selected organizations
                    for orga_uid in registry_orgs:
                        orga = uuidToObject(orga_uid)
                        if addOrModifyGroup(orga, new_id, new_title):
                            changes = True
                else:
                    # fct_orgs changed, we remove every linked Plone groups
                    # except ones defined in new_orgs
                    for orga_uid in get_organizations(only_selected=False,
                                                      the_objects=False):
                        if enabled is True and orga_uid in new_orgs:
                            # make sure Plone group is created or updated if suffix title changed
                            orga = uuidToObject(orga_uid)
                            if addOrModifyGroup(orga, new_id, new_title):
                                changes = True
                        else:
                            # make sure Plone group is deleted
                            plone_group_id = get_plone_group_id(
                                orga_uid, new_id)
                            plone_group = api.group.get(plone_group_id)
                            if plone_group:
                                api.group.delete(plone_group_id)
                                changes = True

        if changes:
            invalidate_sopgv_cache()
            invalidate_sov_cache()
            invalidate_soev_cache()
            invalidate_ssoev_cache()