def plonegroup_contact_transition(contact, event): """ React when a IPloneGroupContact transition is done """ if event.transition and event.transition.id == 'deactivate': # check if the transition is selected registry = getUtility(IRegistry) pp = api.portal.get_tool('portal_properties') errors = [] if contact.UID() in registry[ORGANIZATIONS_REGISTRY]: errors.append(_('This contact is selected in configuration')) elif pp.site_properties.enable_link_integrity_checks: search_value_in_objects(contact, contact.UID(), p_types=[], type_fields={}) storage = ILinkIntegrityInfo(contact.REQUEST) breaches = storage.getIntegrityBreaches() if contact in breaches: errors.append(_("This contact is used in following content: ${items}", mapping={'items': ', '.join(['<a href="%s" target="_blank">%s</a>' % (i.absolute_url(), i.Title()) for i in breaches[contact]])})) if errors: smi = IStatusMessage(contact.REQUEST) smi.addStatusMessage(_('You cannot deactivate this item !'), type='error') smi.addStatusMessage(errors[0], type='error') view_url = getMultiAdapter((contact, contact.REQUEST), name=u'plone_context_state').view_url() # contact.REQUEST['RESPONSE'].redirect(view_url) raise Redirect(view_url)
class PloneGroupUsersGroupsColumn(BaseColumn): """Column that displays Plone groups and users linked to an organization.""" header = _("Groups and users") weight = 5 short = True def renderCell(self, item): """ """ plonegroup_organizations = get_registry_organizations() org_uid = item.UID if org_uid not in plonegroup_organizations: return "-" suffixes = get_all_suffixes(org_uid) group_ids = [ get_plone_group_id(org_uid, suffix) for suffix in suffixes ] url_group_ids = '&group_ids='.join(group_ids) # use _ for i18ndude machinery details_msg = _('Details') details_msg = translate(details_msg, context=self.request) res = u"<div id=\"group-users\" class=\"collapsible\" onclick=\"toggleDetails(" \ u"'collapsible-group-users_{0}', toggle_parent_active=false, parent_tag=null, " \ u"load_view='@@display-group-users?group_ids={1}&short:boolean={2}', base_url='{3}');\"> {4}</div>" \ u"<div id=\"collapsible-group-users_{0}\" class=\"collapsible-content\" style=\"display: none;\">" \ u"<div class=\"collapsible-inner-content\">" \ u"<img src=\"{3}/spinner_small.gif\" /></div></div>".format( org_uid, url_group_ids, self.short, self.table.portal_url, details_msg) return res
def group_deleted(event): """ Raises exception if group cannot be deleted """ group = event.principal portal = api.portal.get() request = portal.REQUEST parts = group.split('_') if len(parts) == 1: return org_uid = parts[0] group_suffix = '_'.join(parts[1:]) if org_uid in get_registry_organizations( ) and group_suffix in get_all_suffixes(org_uid): orga = api.content.find(UID=org_uid)[0].getObject() api.portal.show_message(message=_( "You cannot delete the group '${group}', linked to used organization " "'${orga}'.", mapping={ 'group': group, 'orga': safe_unicode(orga.Title()) }), request=request, type='error') raise Redirect(request.get('ACTUAL_URL'))
def adaptPloneGroupDefinition(organization, event): """ Manage an organization change """ # zope.lifecycleevent.ObjectRemovedEvent : delete # zope.lifecycleevent.ObjectModifiedEvent : edit, rename # is the container who's modified at creation ? # bypass if we are removing the Plone Site if IContainerModifiedEvent.providedBy(event) or \ event.object.portal_type == 'Plone Site': return # is the current organization a part of own organization organization_path = '/'.join(organization.getPhysicalPath()) if not organization_path.startswith( get_own_organization_path( not_found_value='unfound')): # can be unfound too return portal = getSite() # when an organization is removed (and its content), we check if it is used in plonegroup configuration registry_orgs = get_registry_organizations() if IObjectRemovedEvent.providedBy( event) and organization.UID() in registry_orgs: smi = IStatusMessage(organization.REQUEST) smi.addStatusMessage(_('You cannot delete this item !'), type='error') smi.addStatusMessage(_( "This organization or a contained organization is used in plonegroup " "configuration ! Remove it first from the configuration !"), type='error') view_url = getMultiAdapter((organization, organization.REQUEST), name=u'plone_context_state').view_url() organization.REQUEST['RESPONSE'].redirect(view_url) raise Redirect(view_url) return pcat = portal.portal_catalog brains = pcat(portal_type='organization', path=organization_path) changes = False for brain in brains: orga = brain.getObject() orga_uid = orga.UID() if orga_uid in registry_orgs: if addOrModifyOrganizationGroups(orga, orga_uid): changes = True if changes: invalidate_sopgv_cache() invalidate_sov_cache() invalidate_soev_cache() invalidate_ssoev_cache()
def group_users(self, group): """ """ res = [] patterns = {} # use _ for i18ndude machinery user_tag_title = _('View Plone user') user_tag_title = translate(user_tag_title, context=self.request) group_tag_title = _('View Plone group') group_tag_title = translate(group_tag_title, context=self.request) patterns[0] = "<img src='%s/user.png'> " % self.portal_url patterns[1] = "<img src='%s/group.png'> " % self.portal_url if self.is_manager: patterns[0] = "<a class='user-or-group-level-{{index}}' href='{portal_url}/" \ "@@user-information?userid={{member_id}}' " \ "title=\"{user_tag_title}\"><acronym>{pattern}</acronym></a> ".format( **{'portal_url': self.portal_url, 'pattern': patterns[0].strip(), 'user_tag_title': user_tag_title}) patterns[1] = "<a class='user-or-group-level-{{index}}' href='{portal_url}/" \ "@@usergroup-groupmembership?groupname={{member_id}}' " \ "title=\"{group_tag_title}\"><acronym>{pattern}</acronym></a> ".format( **{'portal_url': self.portal_url, 'pattern': patterns[1].strip(), 'group_tag_title': group_tag_title}) for index, principal in self._get_groups_and_members( group, keep_subgroups=self.is_manager): # member may be a user or group isGroup = base_hasattr(principal, 'isGroup') and principal.isGroup() or 0 principal_title = principal.getProperty('fullname') or \ principal.getProperty('title') or principal.getId() if self.is_manager: principal_title = principal_title + " ({0})".format( principal.id) principal_title = "<div class='user-or-group user-or-group-level-{0}'>{1}</div>".format( index, principal_title) value = patterns[isGroup].format(**{ 'member_id': principal.id, 'index': index }) + principal_title res.append((index, principal_title, value)) # sort on member_title res = sorted(res) # just keep values return "".join([v[2] for v in res])
def fields(self): self.init() # second init with user recognized fields = [] description = _( u'You can <span class="cross_icon">remove</span> an assignment with the ' u'<span class="cross_icon">cross icon</span>. ' u'You can <span class="auto_append">add</span> a new assignment with the ' u'<span class="auto_append">blue line</span>. ' u'You can <span class="new_line">complete</span> it on the ' u'<span class="new_line">brown line</span>.') self.get_user_manageable_functions() for function in self.functions_orgs: fld = DGFListField( __name__=function, title=_( u"Assignments for groups related to '${function}' function", mapping={'function': self.functions[function]}), description=description, required=False, value_type=DictRow(title=u"org_users", schema=IOrganisationsUsers, required=False)) fields.append(fld) fields = sorted(fields, key=lambda x: x.title) self.get_user_manageable_groups() if self.groupids: fld = DGFListField(__name__='_groups_', title=_('Global groups assignments.'), description=description, required=False, value_type=DictRow(title=u"users", schema=IGroupsUsers, required=False)) fields.insert(0, fld) fld = schema.TextLine( __name__='_old_values_', title=u'not_showed', required=False, ) fields.append(fld) self.fieldnames = [afield.__name__ for afield in fields] return field.Fields(*fields)
class IFunctionSchema(Interface): """ Function identification schema """ fct_id = schema.TextLine( title=_("Plone group suffix id"), # description=_("Plone group suffix description"), required=True) fct_title = schema.TextLine( title=_("Plone group suffix title"), # description=_("Plone group title description"), required=True) fct_orgs = schema.List( title=_("Plone group suffix organizations"), description=_("Plone group organizations description"), value_type=schema.Choice( vocabulary='collective.contact.plonegroup.browser.settings.' 'SortedSelectedOrganizationsElephantVocabulary'), required=True) fct_management = schema.Bool( title=_("Manageable function groups?"), required=False, default=False, ) enabled = schema.Bool( title=_(u'Enabled?'), default=True, required=False, )
def __call__(self, context, root_portal_type='organization', root_id=PLONEGROUP_ORG): portal = getSite() terms = [] pcat = portal.portal_catalog brains = pcat.unrestrictedSearchResults(portal_type=root_portal_type, id=root_id) if not brains: terms.append( SimpleTerm( None, token="unfound", title= _(u"You must define one '${root_portal_type}' with id '${pgo}' !", mapping={ 'root_portal_type': root_portal_type, 'pgo': root_id, }))) return SimpleVocabulary(terms) elif len(brains) > 1: terms.append( SimpleTerm(None, token="multifound", title=_( u"You must have only one '${root_portal_type}' " "with id '${pgo}' !", mapping={ 'root_portal_type': root_portal_type, 'pgo': root_id }))) return SimpleVocabulary(terms) own_orga = brains[0]._unrestrictedGetObject() self.listSubOrganizations(terms, own_orga) return SimpleVocabulary(terms)
def plonegroup_contact_transition(contact, event): """ React when a IPloneGroupContact transition is done """ if event.transition and event.transition.id == 'deactivate': # check if the transition is selected pp = api.portal.get_tool('portal_properties') errors = [] if contact.UID() in get_registry_organizations(): errors.append(_('This contact is selected in configuration')) elif pp.site_properties.enable_link_integrity_checks: search_value_in_objects(contact, contact.UID(), p_types=[], type_fields={}) storage = ILinkIntegrityInfo(contact.REQUEST) breaches = storage.getIntegrityBreaches() if contact in breaches: errors.append( _("This contact is used in following content: ${items}", mapping={ 'items': ', '.join([ '<a href="%s" target="_blank">%s</a>' % (i.absolute_url(), i.Title()) for i in breaches[contact] ]) })) if errors: smi = IStatusMessage(contact.REQUEST) smi.addStatusMessage(_('You cannot deactivate this item !'), type='error') smi.addStatusMessage(errors[0], type='error') view_url = getMultiAdapter((contact, contact.REQUEST), name=u'plone_context_state').view_url() # contact.REQUEST['RESPONSE'].redirect(view_url) raise Redirect(view_url)
class PlonegroupActionsColumn(ActionsColumn): """ """ header = _("Actions") weight = 20 params = { 'useIcons': True, 'showHistory': False, 'showActions': True, 'showOwnDelete': True, 'showArrows': False, 'showTransitions': False, 'showPloneGroupLink': True } cssClasses = {'td': 'actions-column'}
def group_deleted(event): """ Raises exception if group cannot be deleted """ group = event.principal portal = api.portal.get() request = portal.REQUEST parts = group.split('_') if len(parts) == 1: return group_suffix = '_'.join(parts[1:]) registry = getUtility(IRegistry) if parts[0] in registry[ORGANIZATIONS_REGISTRY] and group_suffix in get_all_suffixes(parts[0]): orga = api.content.find(UID=parts[0])[0].getObject() api.portal.show_message(message=_("You cannot delete the group '${group}', linked to used organization " "'${orga}'.", mapping={'group': group, 'orga': safe_unicode(orga.Title())}), request=request, type='error') raise Redirect(request.get('ACTUAL_URL'))
class SelectedInPlonegroupColumn(BooleanColumn): """Column that displays Yes/No depending on fact that organization is selected in plonegroup organizations.""" header = _("Selected in plonegroup") weight = 10 def renderHeadCell(self): """ """ portal = api.portal.get() plonegroup_url = '{0}/@@contact-plonegroup-settings'.format( portal.absolute_url()) return translate("Selected in plonegroup", domain='collective.contact.plonegroup', mapping={'plonegroup_url': plonegroup_url}, context=self.request) def getValue(self, item): """ """ plonegroup_organizations = get_registry_organizations() org_uid = item.UID return bool(org_uid in plonegroup_organizations)
class ManageOwnGroupUsers(EditForm): """ Manage own groups users """ label = _(u'Own groups management view') description = _(u'Own groups management description') successMessage = _(u'Own groups users succesfully updated.') noChangesMessage = _(u'No changes were made.') def __init__(self, context, request): self.context = context self.request = request self.functions = {} # will contain function title by function id self.functions_orgs = {} # will contain org list by function id self.groupids = {} # will contain group title by group id self.fieldnames = [] def init(self): """ user is now recognized """ self.current_user = api.user.get_current() # self.current_user = api.user.get(userid='chef') self.current_user_id = self.current_user.getId() self.current_user_groups = [ g for g in api.group.get_groups(user=self.current_user) if g ] def get_manageable_functions(self): """ get all manageable functions """ for fct in get_registry_functions(as_copy=False): if fct['fct_management']: self.functions[fct['fct_id']] = fct['fct_title'] return self.functions.keys() def get_user_manageable_functions(self): """ get user manageable functions """ manageable_functions = self.get_manageable_functions() for group in self.current_user_groups: parts = group.id.split('_') if len(parts) == 1: continue group_suffix = '_'.join(parts[1:]) if group_suffix not in manageable_functions: continue if group_suffix not in self.functions_orgs: self.functions_orgs[group_suffix] = [] org = get_organization(parts[0]) if org not in self.functions_orgs[group_suffix]: self.functions_orgs[group_suffix].append( get_organization(parts[0])) def get_manageable_groups(self): """ get selected manageable groups """ return get_registry_groups_mgt() def get_user_manageable_groups(self): """ get user manageable groups """ manageable_groups = self.get_manageable_groups() for group in self.current_user_groups: if group.id not in manageable_groups: continue self.groupids[group.id] = group.getProperty('title') def getContent(self): return GroupsConfigurationAdapter(self) @property def fields(self): self.init() # second init with user recognized fields = [] description = _( u'You can <span class="cross_icon">remove</span> an assignment with the ' u'<span class="cross_icon">cross icon</span>. ' u'You can <span class="auto_append">add</span> a new assignment with the ' u'<span class="auto_append">blue line</span>. ' u'You can <span class="new_line">complete</span> it on the ' u'<span class="new_line">brown line</span>.') self.get_user_manageable_functions() for function in self.functions_orgs: fld = DGFListField( __name__=function, title=_( u"Assignments for groups related to '${function}' function", mapping={'function': self.functions[function]}), description=description, required=False, value_type=DictRow(title=u"org_users", schema=IOrganisationsUsers, required=False)) fields.append(fld) fields = sorted(fields, key=lambda x: x.title) self.get_user_manageable_groups() if self.groupids: fld = DGFListField(__name__='_groups_', title=_('Global groups assignments.'), description=description, required=False, value_type=DictRow(title=u"users", schema=IGroupsUsers, required=False)) fields.insert(0, fld) fld = schema.TextLine( __name__='_old_values_', title=u'not_showed', required=False, ) fields.append(fld) self.fieldnames = [afield.__name__ for afield in fields] return field.Fields(*fields) # def datagridInitialise(self, subform, widget): # pass # def datagridUpdateWidgets(self, subform, widgets, widget): # pass def updateWidgets(self): super(ManageOwnGroupUsers, self).updateWidgets() for wid in self.widgets: if wid == '_old_values_': self.widgets[wid].mode = HIDDEN_MODE else: self.widgets[wid].allow_reorder = False self.widgets[wid].allow_insert = False self.widgets[wid].allow_delete = True self.widgets[wid].auto_append = True @button.buttonAndHandler(_z3cf('Apply'), name='apply') def handleApply(self, action): data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return changes = False users = {} old_values = eval(data.pop('_old_values_')) for name in old_values: try: new_value = data[ name] # If the field is not in the data, then go on to the next one except KeyError: continue new_value = set([(dic['group'], dic['user']) for dic in data[name]]) old_value = set([(dic['group'], dic['user']) for dic in old_values[name]]) if old_value == new_value: continue for action, result in (('removed', old_value - new_value), ('added', new_value - old_value)): for (group_id, user_id) in result: if group_id is None or user_id is None: required_message = _( u"There was a problem in added assignments. " u"Don't forget to complete the 2 columns! " u"You have to redo all the manipulations.") api.portal.show_message(message=required_message, request=self.request, type='error') raise Redirect(self.request.get('ACTUAL_URL')) if user_id == self.current_user_id: user_message = _( u"You cannot remove your user from a group!") api.portal.show_message(message=user_message, request=self.request, type='error') raise Redirect(self.request.get('ACTUAL_URL')) if name != '_groups_': group_id = get_plone_group_id(group_id, name) if group_id not in users: users[group_id] = [ u.id for u in api.user.get_users(groupname=group_id) ] if action == 'removed' and user_id in users[group_id]: api.group.remove_user(groupname=group_id, username=user_id) changes = True elif action == 'added' and user_id not in users[group_id]: api.group.add_user(groupname=group_id, username=user_id) changes = True if changes: api.portal.show_message(message=self.successMessage, request=self.request) else: api.portal.show_message(message=self.noChangesMessage, request=self.request, type='warn') self.request.response.redirect(self.request.get('ACTUAL_URL')) @button.buttonAndHandler(PMF(u'return_to_view'), name='cancel') def handleCancel(self, action): self.request.response.redirect(self.request.get('URL1'))
class IOrganizationSchema(Interface): """ Organization identification schema """ org_id = schema.TextLine(title=_("Organization id"), required=True) org_title = schema.TextLine(title=_("Organization title"), required=True)
class IContactPlonegroupConfig(Interface): """ Configuration schema """ # plone.registry cannot store schema.Choice different from named vocabularies ! organizations = schema.List( title=_(u'Selected organizations'), description= _(u"Choose multiple organization levels for which you want to create a plone group." ), required=True, value_type=schema.Choice( vocabulary=u'collective.contact.plonegroup.organization_services', )) widget('organizations', size=15) functions = schema.List( title=_(u'Function list'), description=_( u'Each defined function will suffix each organization plone group.' ), required=True, value_type=DictRow(title=_("Function"), schema=IFunctionSchema)) widget('functions', DataGridFieldFactory, auto_append=False) groups_management = schema.List( title=_(u'Selected global groups can be managed by a contained user'), required=False, value_type=schema.Choice( vocabulary=u'collective.contact.plonegroup.global_groups'), ) widget('groups_management', CheckBoxFieldWidget, multiple='multiple', size=15) @invariant def validateSettings(data): if not data.organizations: raise Invalid(_(u"You must choose at least one organization !")) if len(data.organizations) == 1 and data.organizations[0] is None: raise Invalid( _(u"You must correct the organization error first !")) if not data.functions: raise Invalid(_(u"You must define at least one function !")) # only able to delete a function (suffix) if every linked Plone groups are empty stored_suffixes = get_all_suffixes() saved_suffixes = [func['fct_id'] for func in data.functions] removed_suffixes = list(set(stored_suffixes) - set(saved_suffixes)) for removed_suffix in removed_suffixes: # check that every organizations including not selected # linked suffixed Plone group is empty for org_uid in get_organizations(only_selected=False, the_objects=False): plone_group_id = get_plone_group_id(org_uid, removed_suffix) plone_group = api.group.get(plone_group_id) if plone_group and plone_group.getMemberIds(): raise Invalid( _(u"can_not_remove_function_every_plone_groups_not_empty", mapping={ 'removed_function': removed_suffix, 'plone_group_id': plone_group_id })) # only able to select orgs for an existing function (suffix) if # every linked Plone groups of not selected orgs are empty stored_functions = get_registry_functions() old_functions = { dic['fct_id']: { 'fct_title': dic['fct_title'], 'fct_orgs': dic['fct_orgs'], 'enabled': dic['enabled'] } for dic in stored_functions } new_functions = { dic['fct_id']: { 'fct_title': dic['fct_title'], 'fct_orgs': dic['fct_orgs'], 'enabled': dic['enabled'] } for dic in data.functions } for new_function, new_function_infos in new_functions.items(): if new_function_infos['fct_orgs'] and \ old_functions[new_function]['fct_orgs'] != new_function_infos['fct_orgs']: # check that Plone group is empty for not selected fct_orgs for org_uid in get_organizations(only_selected=False, the_objects=False): if org_uid in new_function_infos['fct_orgs']: continue plone_group_id = get_plone_group_id(org_uid, new_function) plone_group = api.group.get(plone_group_id) # use getGroupMembers to ignore '<not found>' users if plone_group and plone_group.getGroupMembers(): raise Invalid( _(u"can_not_select_function_orgs_every_other_plone_groups_not_empty", mapping={ 'function': new_function, 'plone_group_id': plone_group_id })) elif new_function_infos['enabled'] is False: # check that Plone groups are all empty for org_uid in get_organizations(only_selected=False, the_objects=False): plone_group_id = get_plone_group_id(org_uid, new_function) plone_group = api.group.get(plone_group_id) # use getGroupMembers to ignore '<not found>' users if plone_group and plone_group.getGroupMembers(): raise Invalid( _(u"can_not_disable_suffix_plone_groups_not_empty", mapping={ 'disabled_function': new_function, 'plone_group_id': plone_group_id }))
def handleApply(self, action): data, errors = self.extractData() if errors: self.status = self.formErrorsMessage return changes = False users = {} old_values = eval(data.pop('_old_values_')) for name in old_values: try: new_value = data[ name] # If the field is not in the data, then go on to the next one except KeyError: continue new_value = set([(dic['group'], dic['user']) for dic in data[name]]) old_value = set([(dic['group'], dic['user']) for dic in old_values[name]]) if old_value == new_value: continue for action, result in (('removed', old_value - new_value), ('added', new_value - old_value)): for (group_id, user_id) in result: if group_id is None or user_id is None: required_message = _( u"There was a problem in added assignments. " u"Don't forget to complete the 2 columns! " u"You have to redo all the manipulations.") api.portal.show_message(message=required_message, request=self.request, type='error') raise Redirect(self.request.get('ACTUAL_URL')) if user_id == self.current_user_id: user_message = _( u"You cannot remove your user from a group!") api.portal.show_message(message=user_message, request=self.request, type='error') raise Redirect(self.request.get('ACTUAL_URL')) if name != '_groups_': group_id = get_plone_group_id(group_id, name) if group_id not in users: users[group_id] = [ u.id for u in api.user.get_users(groupname=group_id) ] if action == 'removed' and user_id in users[group_id]: api.group.remove_user(groupname=group_id, username=user_id) changes = True elif action == 'added' and user_id not in users[group_id]: api.group.add_user(groupname=group_id, username=user_id) changes = True if changes: api.portal.show_message(message=self.successMessage, request=self.request) else: api.portal.show_message(message=self.noChangesMessage, request=self.request, type='warn') self.request.response.redirect(self.request.get('ACTUAL_URL'))
def validateSettings(data): if not data.organizations: raise Invalid(_(u"You must choose at least one organization !")) if len(data.organizations) == 1 and data.organizations[0] is None: raise Invalid( _(u"You must correct the organization error first !")) if not data.functions: raise Invalid(_(u"You must define at least one function !")) # only able to delete a function (suffix) if every linked Plone groups are empty stored_suffixes = get_all_suffixes() saved_suffixes = [func['fct_id'] for func in data.functions] removed_suffixes = list(set(stored_suffixes) - set(saved_suffixes)) for removed_suffix in removed_suffixes: # check that every organizations including not selected # linked suffixed Plone group is empty for org_uid in get_organizations(only_selected=False, the_objects=False): plone_group_id = get_plone_group_id(org_uid, removed_suffix) plone_group = api.group.get(plone_group_id) if plone_group and plone_group.getMemberIds(): raise Invalid( _(u"can_not_remove_function_every_plone_groups_not_empty", mapping={ 'removed_function': removed_suffix, 'plone_group_id': plone_group_id })) # only able to select orgs for an existing function (suffix) if # every linked Plone groups of not selected orgs are empty stored_functions = get_registry_functions() old_functions = { dic['fct_id']: { 'fct_title': dic['fct_title'], 'fct_orgs': dic['fct_orgs'], 'enabled': dic['enabled'] } for dic in stored_functions } new_functions = { dic['fct_id']: { 'fct_title': dic['fct_title'], 'fct_orgs': dic['fct_orgs'], 'enabled': dic['enabled'] } for dic in data.functions } for new_function, new_function_infos in new_functions.items(): if new_function_infos['fct_orgs'] and \ old_functions[new_function]['fct_orgs'] != new_function_infos['fct_orgs']: # check that Plone group is empty for not selected fct_orgs for org_uid in get_organizations(only_selected=False, the_objects=False): if org_uid in new_function_infos['fct_orgs']: continue plone_group_id = get_plone_group_id(org_uid, new_function) plone_group = api.group.get(plone_group_id) # use getGroupMembers to ignore '<not found>' users if plone_group and plone_group.getGroupMembers(): raise Invalid( _(u"can_not_select_function_orgs_every_other_plone_groups_not_empty", mapping={ 'function': new_function, 'plone_group_id': plone_group_id })) elif new_function_infos['enabled'] is False: # check that Plone groups are all empty for org_uid in get_organizations(only_selected=False, the_objects=False): plone_group_id = get_plone_group_id(org_uid, new_function) plone_group = api.group.get(plone_group_id) # use getGroupMembers to ignore '<not found>' users if plone_group and plone_group.getGroupMembers(): raise Invalid( _(u"can_not_disable_suffix_plone_groups_not_empty", mapping={ 'disabled_function': new_function, 'plone_group_id': plone_group_id }))