예제 #1
0
 def test_get_vocab(self):
     vocab = get_vocab(self.portal, 'plone.app.vocabularies.PortalTypes')
     self.assertTrue(isinstance(vocab, SimpleVocabulary))
     vocab = get_vocab(None,
                       'plone.app.vocabularies.PortalTypes',
                       only_factory=True)
     self.assertTrue(isinstance(vocab, PortalTypesVocabulary))
예제 #2
0
 def __init__(self, context, request):
     super(PSTExportAsXML, self).__init__(context, request)
     self.ploneview = getMultiAdapter((context, request), name='plone')
     self.plan_vocab = get_vocab(
         self.context, 'imio.project.core.content.project.plan_vocabulary')
     self.manager_vocab = get_vocab(
         self.context,
         'imio.project.core.content.project.manager_vocabulary')
     self.repr_resp_vocab = get_vocab(
         self.context,
         'imio.project.pst.content.operational.representative_responsible_vocabulary'
     )
예제 #3
0
    def test_pm_CreatorsForFacetedVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.creatorsforfacetedvocabulary"
                   vocabulary, especially because it is cached.'''
        cfg = self.meetingConfig
        self.changeUser('pmCreator1')
        pmFolder = self.getMeetingFolder()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.creatorsforfacetedfiltervocabulary",
            only_factory=True)
        # once get, it is cached
        self.assertEqual(len(vocab(pmFolder)), 3)

        # if a new pmFolder is created, then the cache is cleaned
        # get pmFolder for user 'pmManager'
        self.changeUser('pmManager')
        pmFolder = self.getMeetingFolder()
        # cache was cleaned
        self.assertEqual(len(vocab(pmFolder)), 4)

        cfg.setUsersHiddenInDashboardFilter(('pmCreator1', ))
        cfg.at_post_edit_script()
        # cache was cleaned and pmCreator is not in the list anymore
        self.assertEqual(len(vocab(pmFolder)), 3)

        cfg.setUsersHiddenInDashboardFilter(())
        cfg.at_post_edit_script()
        # cache was cleaned and pmCreator is back in the list
        self.assertEqual(len(vocab(pmFolder)), 4)
예제 #4
0
    def test_pm_AdviceTypesVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.advicetypesvocabulary"
           vocabulary, especially because it is cached.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        cfg.setUsedAdviceTypes(('positive', 'negative'))
        pmFolder = self.getMeetingFolder()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.advicetypesvocabulary",
            only_factory=True)
        # once get, it is cached
        self.assertEqual(sorted([term.token for term in vocab(pmFolder)]), [
            'asked_again', 'considered_not_given_hidden_during_redaction',
            'hidden_during_redaction', 'negative', 'not_given', 'positive'
        ])

        # change the MeetingConfig.usedAdvicesTypes
        cfg.setUsedAdviceTypes(('positive', ))
        # cached
        self.assertEqual(sorted([term.token for term in vocab(pmFolder)]), [
            'asked_again', 'considered_not_given_hidden_during_redaction',
            'hidden_during_redaction', 'negative', 'not_given', 'positive'
        ])
        cfg.at_post_edit_script()
        # cache invalidated
        self.assertEqual(sorted([term.token for term in vocab(pmFolder)]), [
            'asked_again', 'considered_not_given_hidden_during_redaction',
            'hidden_during_redaction', 'not_given', 'positive'
        ])
예제 #5
0
    def test_pm_ItemClassifiersVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.classifiersvocabulary"
           vocabulary, especially because it is cached. It relies on the categoriesvocabulary.'''
        self.changeUser('siteadmin')
        pmFolder = self.getMeetingFolder()
        cfg = self.meetingConfig
        cfg.setUseGroupsAsCategories(False)
        vocab = get_vocab(
            cfg,
            "Products.PloneMeeting.vocabularies.classifiersvocabulary",
            only_factory=True)
        # once get, it is cached
        terms = vocab(pmFolder)
        # every existing categories are shown, no matter it is disabled
        nbOfCategories = len(
            cfg.getCategories(catType='classifiers', onlySelectable=False))
        self.assertEqual(len(terms), nbOfCategories)
        # here we make sure it is cached by changing a category title
        # manually without using the processForm way
        classifier1 = cfg.classifiers.classifier1
        classifier1.title = u'New title'
        terms = vocab(pmFolder)
        classifier1_id = classifier1.getId()
        self.assertNotEquals(terms.by_token[classifier1_id].title,
                             cfg.categories.development.title)
        # right correctly edit the category, the vocabulary is invalidated
        notify(ObjectModifiedEvent(classifier1))
        terms = vocab(pmFolder)
        self.assertEqual(terms.by_token[classifier1_id].title,
                         cfg.classifiers.classifier1.title)

        # if we add/remove a category, then the cache is cleaned too
        # add a classifier
        newClassifier = self.create('meetingcategory',
                                    id='newclassifier',
                                    title='New classifier',
                                    is_classifier=True)
        # cache was cleaned, the new value is available
        terms = vocab(pmFolder)
        self.assertEqual([term.title for term in vocab(pmFolder)], [
            u'Classifier 2', u'Classifier 3', u'New classifier', u'New title'
        ])

        # disable a classifier
        self._disableObj(newClassifier)
        self.assertEqual([term.title for term in vocab(pmFolder)], [
            u'Classifier 2', u'Classifier 3', u'New title',
            u'New classifier (Inactive)'
        ])
        # term.value is the classifier id
        self.assertEqual(
            [term.value for term in vocab(pmFolder)],
            ['classifier2', 'classifier3', 'classifier1', 'newclassifier'])

        # remove a classifier
        self.portal.restrictedTraverse('@@delete_givenuid')(
            newClassifier.UID())
        # cache was cleaned
        self.assertEqual([term.title for term in vocab(pmFolder)],
                         [u'Classifier 2', u'Classifier 3', u'New title'])
예제 #6
0
 def _add_finance_advice(item, newItem, last_transition):
     if item['toDiscuss'] and \
        item['category'] in ['affaires-juridiques', 'remboursement'] and \
        last_transition == 'prevalidate':
         finance_group = finance_group_uid()
         if finance_group:
             finance_advice_id = '{0}__rowid__unique_id_002'.format(
                 finance_group)
             optional_advisers = get_vocab(
                 newItem,
                 'Products.PloneMeeting.vocabularies.itemoptionaladvicesvocabulary'
             )
             if finance_advice_id not in optional_advisers:
                 _memos.clear()
                 finance_group = finance_group_uid()
                 finance_advice_id = '{0}__rowid__unique_id_002'.format(
                     finance_group)
             newItem.setOptionalAdvisers((finance_advice_id, ))
             newItem.updateLocalRoles()
             wfTool.doActionFor(newItem, 'wait_advices_from_prevalidated')
             newItem.setCompleteness('completeness_complete')
             newItem.updateLocalRoles()
             with api.env.adopt_user('dfin'):
                 advice = createContentInContainer(
                     newItem, 'meetingadvicefinances', **{
                         'advice_group': finance_group,
                         'advice_type': u'positive_finance',
                         'advice_comment': RichTextValue(u'Mon commentaire')
                     })
                 wfTool.doActionFor(advice, 'proposeToFinancialEditor')
                 wfTool.doActionFor(advice, 'proposeToFinancialReviewer')
                 wfTool.doActionFor(advice, 'proposeToFinancialManager')
                 wfTool.doActionFor(advice, 'signFinancialAdvice')
             return True
     return False
예제 #7
0
    def _turn_ids_into_uids(self, data):
        # turn annex_type id into content_category calculated id
        content_category = data.get("content_category", None)
        annex_type = None
        # get selectable categories
        if data["@type"] == "annexDecision":
            self.request.set('force_use_item_decision_annexes_group', True)
        vocab = get_vocab(self.context,
                          'collective.iconifiedcategory.categories')
        # when content_category provided, it must exist
        if content_category is not None:
            annex_group = get_annexes_config(self.context,
                                             data["@type"],
                                             annex_group=True)
            # get given content_category or the first one
            annex_type = annex_group.get(content_category)
            if not annex_type:
                raise BadRequest(ANNEX_CONTENT_CATEGORY_ERROR %
                                 content_category)
            annex_type_value = calculate_category_id(annex_type)
            if annex_type_value not in vocab:
                raise BadRequest(ANNEX_CONTENT_CATEGORY_ERROR %
                                 content_category)
        else:
            # use the default annex_type, the first selectable
            if not vocab._terms:
                raise BadRequest(ANNEX_NO_CONTENT_CATEGORY_AVAILABLE_ERROR)
            annex_type_value = vocab._terms[0].token
        data["content_category"] = annex_type_value
        # cleanup
        if data["@type"] == "annexDecision":
            self.request.set('force_use_item_decision_annexes_group', False)

        return data
예제 #8
0
 def render(self):
     used_vote_terms = get_vocab(
         self.context,
         "Products.PloneMeeting.vocabularies.usedvotevaluesvocabulary")
     self.used_vote_values = [term.token for term in used_vote_terms._terms]
     self.groups = _build_voting_groups(self.context)
     return self.template()
예제 #9
0
 def showAddAnnex(self):
     """ """
     portal_types = api.portal.get_tool('portal_types')
     annexTypeInfo = portal_types['annex']
     vocab = get_vocab(self.context, 'collective.iconifiedcategory.categories')
     show = annexTypeInfo in self.context.allowedContentTypes() and len(vocab)
     return vocab, show
예제 #10
0
    def test_pm_MeetingDatesVocabularyMCAware(self):
        '''Test that "Products.PloneMeeting.vocabularies.meetingdatesvocabulary"
           vocabulary, is MeetingConfig aware, especially because it is cached.'''
        cfg = self.meetingConfig
        cfg2 = self.meetingConfig2
        self.changeUser('pmManager')
        pmFolder = self.getMeetingFolder()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.meetingdatesvocabulary",
            only_factory=True)
        # create Meetings in cfg1
        self.create('Meeting')
        self.create('Meeting', date=datetime(2015, 5, 6))
        # create Meetings in cfg2
        self.setMeetingConfig(cfg2.getId())
        self.create('Meeting')
        self.create('Meeting', date=datetime(2016, 5, 6))

        self.setMeetingConfig(cfg.getId())
        pmFolder = self.getMeetingFolder()
        terms_cfg1 = [term.token for term in vocab(pmFolder)]
        self.setMeetingConfig(cfg2.getId())
        pmFolder = self.getMeetingFolder()
        terms_cfg2 = [term.token for term in vocab(pmFolder)]
        self.assertNotEqual(terms_cfg1, terms_cfg2)
예제 #11
0
 def test_pm_ListCategoriesOfOtherMCs(self):
     '''Test the vocabulary of the 'category_mapping_when_cloning_to_other_mc' field.'''
     cfg = self.meetingConfig
     cfg2 = self.meetingConfig2
     # use special chars in MC title to check for unicodeDecodeErrors
     cfg.setTitle("héhé")
     cfg2.setTitle("hàhà")
     # by default, items of meetingConfig can be sent to meetingConfig2
     # as meetingConfig2 use categories, it will appear in a category of meetingConfig
     aCatInMC = cfg.categories.development
     vocab_factory = get_vocab(
         None, u"Products.PloneMeeting.content.category."
         "category_mapping_when_cloning_to_other_mc_vocabulary", only_factory=True)
     self.assertEqual(len(vocab_factory(aCatInMC)), 8)
     # disabled categories are also returned
     self.assertFalse(cfg2.categories.marketing.enabled)
     self.assertTrue("{0}.marketing".format(cfg2.getId()) in vocab_factory(aCatInMC))
     # only terms of cfg2
     self.assertFalse([term for term in vocab_factory(aCatInMC)
                       if cfg2.getId() not in term.token])
     # but as meetingConfig does not use categories, a category of meetingConfig2 will not see it
     aCatInMC2 = cfg2.categories.deployment
     self.assertEqual(len(vocab_factory(aCatInMC2)), 0)
     # activate categories in both meetingConfigs
     cfg.setUseGroupsAsCategories(False)
     # still not enough...
     self.assertEqual(len(vocab_factory(aCatInMC2)), 0)
     # ... we must also specify that elements of self.meetingConfig2 can be sent to self.meetingConfig
     cfg2.setMeetingConfigsToCloneTo(
         ({'meeting_config': '%s' % cfg.getId(),
           'trigger_workflow_transitions_until': NO_TRIGGER_WF_TRANSITION_UNTIL}, ))
     self.assertEqual(len(vocab_factory(aCatInMC2)), 3)
     # only terms of cfg
     self.assertFalse([term for term in vocab_factory(aCatInMC2)
                       if cfg.getId() not in term.token])
예제 #12
0
    def handleSaveRemoveAdviceInheritance(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return
        advice_infos = self._advice_infos(data)
        if not advice_infos.mayRemoveInheritedAdvice():
            raise Unauthorized

        # if 'ask_localy', check if advice_id may be asked locally, if it is not the case
        # return a portal message but do not remove the inherited advice
        advice_asked_locally = False
        if data['inherited_advice_action'] == 'ask_locally':
            if self.context.showOptionalAdvisers():
                advisers_vocab = get_vocab(
                    self.context,
                    self.context.getField(
                        'optionalAdvisers').vocabulary_factory, **{
                            'include_selected': False,
                            'include_not_selectable_values': False
                        })
                if data['advice_uid'] in advisers_vocab:
                    optionalAdvisers = list(
                        self.context.getOptionalAdvisers(computed=True))
                    if data['advice_uid'] not in optionalAdvisers:
                        optionalAdvisers.append(data['advice_uid'])
                        self.context.setOptionalAdvisers(optionalAdvisers)
                    advice_asked_locally = True
            if not advice_asked_locally:
                api.portal.show_message(message=_(
                    'remove_advice_inheritance_ask_locally_not_configured'),
                                        request=self.request,
                                        type='warning')
                self.request.RESPONSE.redirect(self.context.absolute_url())
                return
        del self.context.adviceIndex[data['advice_uid']]
        self.context.update_local_roles()
        if advice_asked_locally:
            api.portal.show_message(message=_(
                'remove_advice_inheritance_removed_and_asked_locally'),
                                    request=self.request)
        else:
            api.portal.show_message(
                message=_('remove_advice_inheritance_removed'),
                request=self.request)
        # in case an adviser removed inherited advice and may not
        # see the item anymore, we redirect him to a viewable place
        cleanMemoize(
            self.context,
            prefixes=['borg.localrole.workspace.checkLocalRolesAllowed'])
        url = findViewableURL(self.context, self.request)
        self.request.RESPONSE.redirect(url)

        # add logging message to fingerpointing log
        extras = 'object={0} advice_uid={1} inherited_advice_action={2}'.format(
            repr(self.context), data['advice_uid'],
            data['inherited_advice_action'])
        fplog('remove_advice_inheritance', extras=extras)
        return
예제 #13
0
 def showAddAnnexDecision(self):
     """ """
     portal_types = api.portal.get_tool('portal_types')
     annexTypeInfo = portal_types['annexDecision']
     self.request.set('force_use_item_decision_annexes_group', True)
     vocab = get_vocab(self.context, 'collective.iconifiedcategory.categories')
     self.request.set('force_use_item_decision_annexes_group', False)
     show = annexTypeInfo in self.context.allowedContentTypes() and len(vocab)
     return vocab, show
예제 #14
0
 def test_pm_AskedAdvicesVocabularyMCAware(self):
     '''Test the "Products.PloneMeeting.vocabularies.askedadvicesvocabulary"
        vocabulary, is MeetingConfig aware, especially because it is cached.'''
     self.changeUser('siteadmin')
     cfg = self.meetingConfig
     cfg2 = self.meetingConfig2
     customAdvisers = [{
         'row_id': 'unique_id_000',
         'org': self.developers_uid,
         'gives_auto_advice_on': '',
         'for_item_created_from': '2012/01/01',
         'delay': '2',
         'delay_label': ''
     }, {
         'row_id': 'unique_id_123',
         'org': self.vendors_uid,
         'gives_auto_advice_on': '',
         'for_item_created_from': '2012/01/01',
         'delay': '5',
         'delay_label': ''
     }, {
         'row_id': 'unique_id_456',
         'org': self.vendors_uid,
         'gives_auto_advice_on': '',
         'for_item_created_from': '2012/01/01',
         'delay': '10',
         'delay_label': ''
     }, {
         'row_id': 'unique_id_789',
         'org': self.vendors_uid,
         'gives_auto_advice_on': '',
         'for_item_created_from': '2012/01/01',
         'delay': '20',
         'delay_label': ''
     }]
     cfg.setCustomAdvisers(customAdvisers)
     customAdvisers = [{
         'row_id': 'unique_id_999',
         'org': self.developers_uid,
         'gives_auto_advice_on': '',
         'for_item_created_from': '2012/01/01',
         'delay': '20',
         'delay_label': ''
     }]
     cfg2.setCustomAdvisers(customAdvisers)
     pmFolder = self.getMeetingFolder()
     vocab = get_vocab(
         pmFolder,
         "Products.PloneMeeting.vocabularies.askedadvicesvocabulary",
         only_factory=True)
     terms_cfg1 = [term.token for term in vocab(pmFolder)]
     self.setMeetingConfig(cfg2.getId())
     pmFolder = self.getMeetingFolder()
     terms_cfg2 = [term.token for term in vocab(pmFolder)]
     self.assertNotEqual(terms_cfg1, terms_cfg2)
예제 #15
0
 def test_pm_AdviceTypesVocabularyMCAware(self):
     '''Test the "Products.PloneMeeting.vocabularies.advicetypesvocabulary"
        vocabulary, is MeetingConfig aware, especially because it is cached.'''
     cfg = self.meetingConfig
     cfg2 = self.meetingConfig2
     self.changeUser('siteadmin')
     cfg.setUsedAdviceTypes(('positive', 'negative'))
     cfg2.setUsedAdviceTypes(('positive', ))
     vocab = get_vocab(
         cfg,
         "Products.PloneMeeting.vocabularies.advicetypesvocabulary",
         only_factory=True)
     pmFolder = self.getMeetingFolder()
     terms_cfg1 = [term.token for term in vocab(pmFolder)]
     self.setMeetingConfig(cfg2.getId())
     pmFolder = self.getMeetingFolder()
     terms_cfg2 = [term.token for term in vocab(pmFolder)]
     self.assertNotEqual(terms_cfg1, terms_cfg2)
예제 #16
0
    def test_pm_CreatorsVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.creatorsvocabulary"
           vocabulary, especially because it is cached.'''
        self.changeUser('pmCreator1')
        pmFolder = self.getMeetingFolder()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.creatorsvocabulary",
            only_factory=True)
        # once get, it is cached
        self.assertEqual(len(vocab(pmFolder)), 3)

        # if a new pmFolder is created, then the cache is cleaned
        # get pmFolder for user 'pmManager'
        self.changeUser('pmManager')
        pmFolder = self.getMeetingFolder()
        # cache was cleaned
        self.assertEqual(len(vocab(pmFolder)), 4)
예제 #17
0
 def test_pm_PMConditionAwareCollectionVocabulary(self):
     """Test the 'conditionawarecollectionvocabulary'
        essentially because it is cached. """
     # test with 2 members having same groups
     # as pmCreator1
     self.changeUser('pmCreator1')
     creator1_groups = self.member.getGroups()
     pmFolder = self.getMeetingFolder()
     searches = self.meetingConfig.searches
     searchAllItems = searches.searches_items.searchallitems
     vocab_name = "Products.PloneMeeting.vocabularies.conditionawarecollectionvocabulary"
     vocab = get_vocab(pmFolder, vocab_name, only_factory=True)
     self.assertTrue("/pmCreator1/" in vocab(searchAllItems,
                                             pmFolder)._terms[0].title[1])
     # as pmCreator1b
     self.changeUser('pmCreator1b')
     creator1_groups = self.member.getGroups()
     creator1b_groups = self.member.getGroups()
     self.assertEqual(creator1_groups, creator1b_groups)
     self.assertTrue("/pmCreator1b/" in vocab(searchAllItems,
                                              pmFolder)._terms[0].title[1])
     # invalidated if a collection is edited
     self.assertEqual(
         vocab(searchAllItems, pmFolder)._terms[0].title[0], u'My items')
     searchMyItems = searches.searches_items.searchmyitems
     searchMyItems.setTitle(u'My items edited')
     notify(ObjectModifiedEvent(searchMyItems))
     self.assertEqual(
         vocab(searchAllItems, pmFolder)._terms[0].title[0],
         u'My items edited')
     # invalidated when user groups changed
     # make pmCreator1b no more a creator
     vocab_values = get_vocab_values(searchAllItems, vocab_name,
                                     **{'real_context': pmFolder})
     self._removePrincipalFromGroups(self.member.id,
                                     [self.developers_creators])
     no_group_vocab_values = get_vocab_values(searchAllItems, vocab_name,
                                              **{'real_context': pmFolder})
     self.assertNotEqual(vocab_values, no_group_vocab_values)
     self._addPrincipalToGroup(self.member.id, self.developers_observers)
     observer_vocab_values = get_vocab_values(searchAllItems, vocab_name,
                                              **{'real_context': pmFolder})
     self.assertNotEqual(no_group_vocab_values, observer_vocab_values)
예제 #18
0
 def test_pm_ItemCategoriesVocabularyMCAware(self):
     '''Test that "Products.PloneMeeting.vocabularies.categoriesvocabulary"
        vocabulary, is MeetingConfig aware, especially because it is cached.'''
     self.changeUser('siteadmin')
     pmFolder = self.getMeetingFolder()
     cfg = self.meetingConfig
     cfg.setUseGroupsAsCategories(False)
     vocab = get_vocab(
         cfg,
         "Products.PloneMeeting.vocabularies.categoriesvocabulary",
         only_factory=True)
     terms_cfg1 = [term.token for term in vocab(pmFolder)]
     # now in cfg2
     cfg2 = self.meetingConfig2
     self.setMeetingConfig(cfg2.getId())
     cfg2.setUseGroupsAsCategories(False)
     pmFolder = self.getMeetingFolder()
     terms_cfg2 = [term.token for term in vocab(pmFolder)]
     self.assertNotEqual(terms_cfg1, terms_cfg2)
예제 #19
0
 def test_pm_DisabledCollectionsAreIgnored(self):
     """If a DashboardCollection is disabled in the MeetingConfig,
        it is not displayed in the vocabulary."""
     searches = self.meetingConfig.searches
     searchAllItems = searches.searches_items.searchallitems
     searchAllItems_path = searchAllItems.absolute_url_path()
     self.changeUser('pmCreator1')
     pmFolder = self.getMeetingFolder()
     vocab = get_vocab(
         pmFolder,
         "Products.PloneMeeting.vocabularies.conditionawarecollectionvocabulary",
         only_factory=True)
     self.assertTrue(
         searchAllItems_path in vocab(searches, real_context=pmFolder))
     # disable it then test again
     self.changeUser('siteadmin')
     self._disableObj(searchAllItems, notify_event=True)
     self.changeUser('pmCreator1')
     self.assertFalse(
         searchAllItems_path in vocab(searches, real_context=pmFolder))
예제 #20
0
    def test_pm_MeetingDatesVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.meetingdatesvocabulary"
           vocabulary, especially because it is cached.'''
        self._removeConfigObjectsFor(self.meetingConfig)
        self.changeUser('pmManager')
        pmFolder = self.getMeetingFolder()
        # create a meeting
        meeting = self.create('Meeting', date=datetime(2015, 5, 5))
        meetingUID = meeting.UID()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.meetingdatesvocabulary",
            only_factory=True)
        # once get, it is cached
        vocab(pmFolder)
        self.assertEqual([term.token for term in vocab(pmFolder)._terms],
                         [ITEM_NO_PREFERRED_MEETING_VALUE, meetingUID])

        # if we add/remove/edit a meeting, then the cache is cleaned
        # add a meeting
        meeting2 = self.create('Meeting', date=datetime(2015, 6, 6))
        meeting2UID = meeting2.UID()
        # cache was cleaned
        self.assertEqual(
            [term.token for term in vocab(pmFolder)._terms],
            [ITEM_NO_PREFERRED_MEETING_VALUE, meeting2UID, meetingUID])
        # edit a meeting
        self.assertEqual(
            vocab(pmFolder).by_token[meetingUID].title, u'05/05/2015')
        meeting.date = datetime(2015, 7, 7)
        meeting._update_after_edit()
        # cache was cleaned
        self.assertEqual(
            vocab(pmFolder).by_token[meetingUID].title, u'07/07/2015')

        # remove a meeting
        self.portal.restrictedTraverse('@@delete_givenuid')(meeting.UID())
        # cache was cleaned
        self.assertEqual([term.token for term in vocab(pmFolder)._terms],
                         [ITEM_NO_PREFERRED_MEETING_VALUE, meeting2UID])
예제 #21
0
 def test_pm_validate_category_mapping_when_cloning_to_other_mc(self):
     '''Test the 'category_mapping_when_cloning_to_other_mc' constraint.
        It just validates that we can not define more than one value for the same meetingConfig.'''
     dev_cat = self.meetingConfig.categories.development
     constraint = IMeetingCategory['category_mapping_when_cloning_to_other_mc'].constraint
     dev_cat_vocab = get_vocab(
         dev_cat,
         u"Products.PloneMeeting.content.category."
         u"category_mapping_when_cloning_to_other_mc_vocabulary")
     values = dev_cat_vocab.by_token.keys()
     # one value is ok
     self.assertTrue(constraint([values[0]]))
     # but not 2 for the same meetingConfig...
     error_msg = translate('error_can_not_select_several_cat_for_same_mc',
                           domain='PloneMeeting',
                           context=self.request)
     with self.assertRaises(Invalid) as cm:
         constraint(values)
     self.assertEqual(cm.exception.message, error_msg)
     # simulate a third meetingConfig, select one single value of existing meetingConfig2 and
     # one of unexisting meetingConfig3, the validation is ok...
     self.assertTrue(constraint([values[0], 'meeting-config-dummy.category_name']))
예제 #22
0
def secret_votes_default(context):
    """Default values for secret votes."""
    res = []
    vote_number = vote_number_default()
    item_votes = context.get_item_votes(
        vote_number=vote_number,
        ignored_vote_values=[NOT_VOTABLE_LINKED_TO_VALUE])
    used_vote_terms = get_vocab(
        context,
        "Products.PloneMeeting.vocabularies.usedvotevaluesvocabulary",
        vote_number=vote_number)
    usedVoteValues = [
        term.token for term in used_vote_terms._terms
        if term.token != NOT_ENCODED_VOTE_VALUE
    ]
    for usedVoteValue in usedVoteValues:
        data = {
            'vote_value_id': usedVoteValue,
            'vote_value': usedVoteValue,
            'vote_count': item_votes['votes'].get(usedVoteValue) or 0
        }
        res.append(data)
    return res
예제 #23
0
    def test_pm_ProposingGroupsForFacetedVocabulary(self):
        '''Test that vocabulary "Products.PloneMeeting.vocabularies.proposinggroupsforfacetedfiltervocabulary"
           relies on MeetingConfig.groupsHiddenInDashboardFilter.'''
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        pmFolder = self.getMeetingFolder()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.proposinggroupsforfacetedfiltervocabulary",
            only_factory=True)
        # by default when MeetingConfig.groupsHiddenInDashboardFilter is empty, every groups are returned
        self.assertEqual(cfg.getGroupsHiddenInDashboardFilter(), ())
        self.assertEqual([term.title for term in vocab(pmFolder)],
                         [u'Developers', u'Vendors', u'End users (Inactive)'])
        # now define values in MeetingConfig.groupsHiddenInDashboardFilter
        cfg.setGroupsHiddenInDashboardFilter((self.vendors_uid, ))
        cfg.at_post_edit_script()
        self.assertEqual([term.title for term in vocab(pmFolder)],
                         [u'Developers', u'End users (Inactive)'])

        # select "End users"
        self._select_organization(self.endUsers_uid)
        self.assertEqual([term.title for term in vocab(pmFolder)],
                         [u'Developers', u'End users'])
예제 #24
0
 def test_pm_AskedAdvicesVocabularyWithWrongContext(self):
     '''Test the "Products.PloneMeeting.vocabularies.askedadvicesvocabulary"
        vocabulary, when receiving wrong context, may occur during site install or
        DashboardCollection edit.'''
     self.changeUser('pmManager')
     item = self.create('MeetingItem')
     meeting = self.create('Meeting')
     cfg = self.meetingConfig
     vocab = get_vocab(
         cfg,
         "Products.PloneMeeting.vocabularies.askedadvicesvocabulary",
         only_factory=True)
     # working context
     pmFolder = self.getMeetingFolder()
     self.assertEqual(len(vocab(pmFolder)), 2)
     self.assertEqual(len(vocab(cfg)), 2)
     self.assertEqual(len(vocab(item)), 2)
     self.assertEqual(len(vocab(meeting)), 2)
     # do not fail on weird context
     self.assertEqual(len(vocab(self.portal)), 0)
     self.assertEqual(len(vocab(self.portal.contacts)), 0)
     self.assertEqual(len(vocab(self.app)), 0)
     self.request['PUBLISHED'] = None
     self.assertEqual(len(vocab(None)), 0)
예제 #25
0
    def test_pm_ProposingGroupsVocabularies(self):
        '''Test proposingGroup related cached vocabularies.'''
        self.changeUser('siteadmin')
        pmFolder = self.getMeetingFolder()
        vocab1 = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.proposinggroupsvocabulary",
            only_factory=True)
        vocab2 = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.everyorganizationsacronymsvocabulary",
            only_factory=True)
        vocab3 = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.proposinggroupsforfacetedfiltervocabulary",
            only_factory=True)
        vocab4 = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.associatedgroupsvocabulary",
            only_factory=True)
        # once get, it is cached
        self.assertEqual(len(vocab1(pmFolder)), 3)
        # contains My organization and external organizations
        self.assertEqual(len(vocab2(pmFolder)), 6)
        self.assertEqual(len(vocab3(pmFolder)), 3)
        # when nothing in config, just displays the orgs selected in plonegroup
        self.assertEqual(len(vocab4(pmFolder)), 2)

        # if we add/remove/edit an organozation, then the cache is cleaned
        # add an organization
        new_org = self.create('organization', title='NewOrg', acronym='N.O.')
        new_org_uid = new_org.UID()
        self._select_organization(new_org_uid)
        # cache was cleaned
        self.assertEqual(len(vocab1(pmFolder)), 4)
        self.assertEqual(len(vocab2(pmFolder)), 7)
        self.assertEqual(len(vocab3(pmFolder)), 4)
        self.assertEqual(len(vocab4(pmFolder)), 3)

        # edit a group
        self.assertEqual(
            vocab1(pmFolder).by_token[new_org_uid].title, new_org.Title())
        self.assertEqual(
            vocab2(pmFolder).by_token[new_org_uid].title, new_org.acronym)
        self.assertEqual(
            vocab3(pmFolder).by_token[new_org_uid].title, new_org.Title())
        self.assertEqual(
            vocab4(pmFolder).by_token[new_org_uid].title, new_org.Title())
        new_org.title = u'Modified title'
        new_org.acronym = u'Modified acronym'
        notify(ObjectModifiedEvent(new_org))
        # cache was cleaned
        self.assertEqual(
            vocab1(pmFolder).by_token[new_org_uid].title, new_org.Title())
        self.assertEqual(
            vocab2(pmFolder).by_token[new_org_uid].title, new_org.acronym)
        self.assertEqual(
            vocab3(pmFolder).by_token[new_org_uid].title, new_org.Title())
        self.assertEqual(
            vocab4(pmFolder).by_token[new_org_uid].title, new_org.Title())

        # remove an organization (unselect it first)
        self._select_organization(new_org_uid, remove=True)
        self.portal.restrictedTraverse('@@delete_givenuid')(new_org_uid)
        # cache was cleaned
        self.assertEqual(len(vocab1(pmFolder)), 3)
        self.assertEqual(len(vocab2(pmFolder)), 6)
        self.assertEqual(len(vocab3(pmFolder)), 3)
        self.assertEqual(len(vocab4(pmFolder)), 2)
        # activate "End users"
        self.assertEqual([term.title for term in vocab1(pmFolder)],
                         [u'Developers', u'Vendors', u'End users (Inactive)'])
        self.assertEqual([term.title for term in vocab2(pmFolder)], [
            u'None', u'Devel', u'Devil', u'EndUsers', u'OrgOutside1',
            u'OrgOutside2'
        ])
        self.assertEqual([term.title for term in vocab3(pmFolder)],
                         [u'Developers', u'Vendors', u'End users (Inactive)'])
        self.assertEqual([term.title for term in vocab4(pmFolder)],
                         [u'Developers', u'Vendors'])
        self._select_organization(self.endUsers_uid)
        self.assertEqual([term.title for term in vocab1(pmFolder)],
                         [u'Developers', u'End users', u'Vendors'])
        self.assertEqual([term.title for term in vocab2(pmFolder)], [
            u'None', u'Devel', u'Devil', u'EndUsers', u'OrgOutside1',
            u'OrgOutside2'
        ])
        self.assertEqual([term.title for term in vocab3(pmFolder)],
                         [u'Developers', u'End users', u'Vendors'])
        self.assertEqual([term.title for term in vocab4(pmFolder)],
                         [u'Developers', u'End users', u'Vendors'])
예제 #26
0
    def test_pm_GroupsInChargeVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.groupsinchargevocabulary"
           vocabulary, especially because it is cached.'''
        cfg = self.meetingConfig
        cfg.setUseGroupsAsCategories(False)
        self._enableField('classifier')
        self.changeUser('siteadmin')
        org1 = self.create('organization',
                           id='org1',
                           title='Org 1',
                           acronym='Org1')
        org1_uid = org1.UID()
        org2 = self.create('organization',
                           id='org2',
                           title='Org 2',
                           acronym='Org2')
        org2_uid = org2.UID()
        org3 = self.create('organization',
                           id='org3',
                           title='Org 3',
                           acronym='Org3')
        org3_uid = org3.UID()
        vocab = get_vocab(
            org1,
            "Products.PloneMeeting.vocabularies.groupsinchargevocabulary",
            only_factory=True)

        # for now, no group in charge
        meetingFolder = self.getMeetingFolder()
        self.assertEqual(len(vocab(meetingFolder)), 0)
        # define some group in charge, vocabulary is invalidated when an org is modified
        self.vendors.groups_in_charge = (org1_uid, )
        notify(ObjectModifiedEvent(self.vendors))
        self.developers.groups_in_charge = (org2_uid, )
        notify(ObjectModifiedEvent(self.developers))
        self.assertEqual([term.title for term in vocab(meetingFolder)],
                         ['Org 1', 'Org 2'])

        # create an new org with a groupInCharge directly
        org4 = self.create('organization',
                           id='org4',
                           title='Org 4',
                           acronym='Org4',
                           groups_in_charge=(org3_uid, ))
        org4_uid = org4.UID()
        self._select_organization(org4_uid)
        self.assertEqual([term.title for term in vocab(meetingFolder)],
                         ['Org 1', 'Org 2', 'Org 3'])

        # change a group in charge
        self.vendors.groups_in_charge = (org4_uid, )
        notify(ObjectModifiedEvent(self.vendors))
        self.assertEqual([term.title for term in vocab(meetingFolder)],
                         ['Org 2', 'Org 3', 'Org 4'])

        # unselect a group in charge
        self.vendors.groups_in_charge = ()
        notify(ObjectModifiedEvent(self.vendors))
        self.assertEqual([term.title for term in vocab(meetingFolder)],
                         ['Org 2', 'Org 3'])

        # category, creating an organization invalidates the vocabulary cache
        org5 = self.create('organization',
                           id='org5',
                           title='Org 5',
                           acronym='Org5')
        org5_uid = org5.UID()
        # an already used
        cfg.categories.development.groups_in_charge = [org3_uid]
        # already existing no more used
        cfg.categories.research.groups_in_charge = [org4_uid]
        # new
        cfg.categories.events.groups_in_charge = [org5_uid]
        self.assertEqual([term.title for term in vocab(meetingFolder)],
                         ['Org 2', 'Org 3', 'Org 4', 'Org 5'])

        # classifier, creating an organization invalidates the vocabulary cache
        org6 = self.create('organization',
                           id='org6',
                           title='Org 6',
                           acronym='Org6')
        org6_uid = org6.UID()
        # already existing
        cfg.classifiers.classifier1.groups_in_charge = [org5_uid]
        # new
        cfg.classifiers.classifier2.groups_in_charge = [org6_uid]
        self.assertEqual([term.title for term in vocab(meetingFolder)],
                         ['Org 2', 'Org 3', 'Org 4', 'Org 5', 'Org 6'])
예제 #27
0
    def vote_counts(self):
        """Returns informations regarding votes count."""
        data = []
        counts = []
        for vote_number in range(len(self.item_votes)):
            sub_counts = []
            total_votes = self.context.getVoteCount('any_votable', vote_number)
            number_of_votes_msg = translate('number_of_voters',
                                            domain='PloneMeeting',
                                            context=self.request)
            res = [
                u'<span title="{0}">{1}</span>'.format(number_of_votes_msg,
                                                       total_votes)
            ]
            formated_total_votes = total_votes
            pattern = u'<span class="vote_value_{0}" title="{1}">{2}</span>'

            # specify how much voted for this vote if secret
            if self.votesAreSecret:
                voted = self.context.getVoteCount('any_voted', vote_number)
                formated_total_votes = "{0} / {1}".format(voted, total_votes)
            sub_counts.append((number_of_votes_msg, formated_total_votes,
                               'vote_value_number_of_voters'))

            # compute votes not encoded for first secret vote
            # taking into account linked votes
            if self.votesAreSecret:
                linked_vote_numbers = _get_linked_item_vote_numbers(
                    self.context, self.meeting, vote_number) or [0]
                if not linked_vote_numbers or vote_number == min(
                        linked_vote_numbers):
                    total_voted = 0
                    for linked_vote_number in linked_vote_numbers:
                        total_voted += self.context.getVoteCount(
                            'any_voted', linked_vote_number)
                    translated_used_vote_value = translate(
                        'vote_value_not_yet',
                        domain='PloneMeeting',
                        context=self.request)
                    count = total_votes - total_voted
                    res.append(
                        pattern.format("not_yet", translated_used_vote_value,
                                       count))
                    sub_counts.append((translated_used_vote_value, count,
                                       'vote_value_not_yet'))

            used_vote_terms = get_vocab(
                self.context,
                "Products.PloneMeeting.vocabularies.usedvotevaluesvocabulary",
                vote_number=vote_number)
            usedVoteValues = [term.token for term in used_vote_terms._terms]
            for usedVoteValue in usedVoteValues:
                translated_used_vote_value = translate(
                    'vote_value_{0}'.format(usedVoteValue),
                    domain='PloneMeeting',
                    context=self.request)
                count = self.context.getVoteCount(usedVoteValue, vote_number)
                res.append(
                    pattern.format(usedVoteValue, translated_used_vote_value,
                                   count))
                sub_counts.append((translated_used_vote_value, count,
                                   'vote_value_' + usedVoteValue))
            votes = u" / ".join(res)
            data.append(votes)
            counts.append(sub_counts)
        return data, counts
예제 #28
0
    def _extra_include(self, result):
        """ """
        extra_include = self._get_asked_extra_include()
        if "categories" in extra_include:
            categories = self.context.getCategories(onlySelectable=False)
            result["extra_include_categories"] = []
            for category in categories:
                serializer = self._get_serializer(category, "categories")
                result["extra_include_categories"].append(serializer())
            result["extra_include_categories_items_total"] = len(categories)

        if "classifiers" in extra_include:
            classifiers = self.context.getCategories(catType='classifiers',
                                                     onlySelectable=False)
            result["extra_include_classifiers"] = []
            for classifier in classifiers:
                serializer = self._get_serializer(classifier, "classifiers")
                result["extra_include_classifiers"].append(serializer())
            result["extra_include_classifiers_items_total"] = len(classifiers)

        if "pod_templates" in extra_include:
            pod_templates = [
                obj for obj in self.context.podtemplates.objectValues()
                if getattr(obj, 'enabled', False)
            ]
            result["extra_include_pod_templates"] = []
            for pod_template in pod_templates:
                serializer = self._get_serializer(pod_template,
                                                  "pod_templates")
                result["extra_include_pod_templates"].append(serializer())
            result["extra_include_pod_templates_items_total"] = len(
                pod_templates)

        if "searches" in extra_include:
            collections = [
                obj
                for obj in self.context.searches.searches_items.objectValues()
                if (obj.portal_type == 'DashboardCollection' and obj.enabled)
            ]
            result["extra_include_searches"] = []
            for collection in collections:
                serializer = self._get_serializer(collection, "searches")
                result["extra_include_searches"].append(serializer())
            result["extra_include_searches_items_total"] = len(collections)

        if "proposing_groups" in extra_include:
            orgs = self.context.getUsingGroups(theObjects=True)
            result["extra_include_proposing_groups"] = []
            for org in orgs:
                serializer = self._get_serializer(org, "proposing_groups")
                result["extra_include_proposing_groups"].append(serializer())
            result["extra_include_proposing_groups_items_total"] = len(orgs)

        if "associated_groups" in extra_include:
            vocab = get_vocab(
                self.context,
                'Products.PloneMeeting.vocabularies.associatedgroupsvocabulary'
            )
            org_uids = [term.value for term in vocab._terms]
            orgs = uuidsToObjects(org_uids, ordered=True)
            result["extra_include_associated_groups"] = []
            for org in orgs:
                serializer = self._get_serializer(org, "associated_groups")
                result["extra_include_associated_groups"].append(serializer())
            result["extra_include_associated_groups_items_total"] = len(orgs)

        if "groups_in_charge" in extra_include:
            vocab = get_vocab(
                self.context,
                'Products.PloneMeeting.vocabularies.groupsinchargevocabulary')
            org_uids = [term.value for term in vocab._terms]
            orgs = uuidsToObjects(org_uids, ordered=True)
            result["extra_include_groups_in_charge"] = []
            for org in orgs:
                serializer = self._get_serializer(org, "groups_in_charge")
                result["extra_include_groups_in_charge"].append(serializer())
            result["extra_include_groups_in_charge_items_total"] = len(orgs)

        return result
예제 #29
0
    def test_MayChangeDelayTo(self):
        """Method MeetingItem.mayChangeDelayTo is made to control the 'available_on'
           of customAdvisers used for finance advice (5, 10 or 20 days).
           - 10 days is the only advice selectable thru the item edit form;
           - the delay '5' must be changed using the change delay widdget;
           - the delay '20' is reserved to finance advisers."""
        cfg = self.meetingConfig
        self.changeUser('siteadmin')
        self._configureFinancesAdvice(cfg)
        # put users in finances group
        self._setupFinancesGroup()
        self.changeUser('pmCreator1')
        item = self.create('MeetingItem')
        # by default, only the 10 days delay is selectable
        optional_advisers = get_vocab(
            item,
            'Products.PloneMeeting.vocabularies.itemoptionaladvicesvocabulary')
        self.assertEqual([term.token for term in optional_advisers], [
            'not_selectable_value_delay_aware_optional_advisers',
            '{0}__rowid__unique_id_002'.format(finance_group_uid()),
            'not_selectable_value_non_delay_aware_optional_advisers',
            self.developers_uid, self.vendors_uid
        ])
        # select the 10 days delay
        item.setOptionalAdvisers(
            ('%s__rowid__unique_id_002' % finance_group_uid(), ))
        item.at_post_edit_script()
        self.assertEqual(item.adviceIndex[finance_group_uid()]['delay'], '10')
        # Managers, are also required to use change delay widget for 5/20 delays
        self.changeUser('pmManager')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))

        # user having 'Modify portal content' may select 10 but not others
        self.changeUser('pmCreator1')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))
        # may select 5 if using change delay widget
        # aka 'managing_available_delays' is found in the REQUEST
        self.request.set('managing_available_delays', True)
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))
        # change to 5 days
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_003'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.assertEqual(item.adviceIndex[finance_group_uid()]['delay'], '5')
        # could back to 10 days
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))
        # Managers have bypass when using change delay widget
        self.changeUser('pmManager')
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))

        # now, when item is 'wait_advices_from_prevalidated', finance advisers
        # may select the 20 days delay
        self.changeUser('pmReviewer1')
        self.proposeItem(item)
        self.do(item, 'wait_advices_from_prevalidated')
        self.changeUser('pmFinController')
        # may change to 20
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertFalse(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))
        # change to 20 days
        item.setOptionalAdvisers(
            ('{0}__rowid__unique_id_004'.format(finance_group_uid()), ))
        item.at_post_edit_script()
        self.assertEqual(item.adviceIndex[finance_group_uid()]['delay'], '20')
        # once to 20, may back to 10
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))

        # if item no more waiting finances advice, finances may not
        # change delay anymore
        self.do(item, 'backTo_proposed_to_refadmin_from_waiting_advices')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertFalse(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))

        # if advice delay is set to 20, user have edit rights may not change it anymore
        self.changeUser('pmRefAdmin1')
        self.assertFalse(item.adapted().mayChangeDelayTo(5))
        self.assertFalse(item.adapted().mayChangeDelayTo(10))
        self.assertFalse(item.adapted().mayChangeDelayTo(20))

        # only a Manager will be able to change that delay now
        self.changeUser('pmManager')
        self.assertTrue(item.adapted().mayChangeDelayTo(5))
        self.assertTrue(item.adapted().mayChangeDelayTo(10))
        self.assertTrue(item.adapted().mayChangeDelayTo(20))
예제 #30
0
    def test_pm_AskedAdvicesVocabulary(self):
        '''Test the "Products.PloneMeeting.vocabularies.askedadvicesvocabulary"
           vocabulary, especially because it is cached.'''
        self.changeUser('siteadmin')
        cfg = self.meetingConfig
        customAdvisers = [{
            'row_id': 'unique_id_000',
            'org': self.developers_uid,
            'gives_auto_advice_on': '',
            'for_item_created_from': '2012/01/01',
            'delay': '2',
            'delay_label': ''
        }, {
            'row_id': 'unique_id_123',
            'org': self.vendors_uid,
            'gives_auto_advice_on': '',
            'for_item_created_from': '2012/01/01',
            'delay': '5',
            'delay_label': ''
        }, {
            'row_id': 'unique_id_456',
            'org': self.vendors_uid,
            'gives_auto_advice_on': '',
            'for_item_created_from': '2012/01/01',
            'delay': '10',
            'delay_label': ''
        }, {
            'row_id': 'unique_id_789',
            'org': self.vendors_uid,
            'gives_auto_advice_on': '',
            'for_item_created_from': '2012/01/01',
            'delay': '20',
            'delay_label': ''
        }]
        cfg.setCustomAdvisers(customAdvisers)
        pmFolder = self.getMeetingFolder()
        vocab = get_vocab(
            pmFolder,
            "Products.PloneMeeting.vocabularies.askedadvicesvocabulary",
            only_factory=True)
        # we have 4 delay-aware advisers and 2 adviser groups selectable as optional
        delayAdvisers = [
            adviser for adviser in cfg.getCustomAdvisers() if adviser['delay']
        ]
        self.assertEqual(len(delayAdvisers), 4)
        self.assertEqual(len(get_organizations(not_empty_suffix='advisers')),
                         2)
        # once get, it is cached, it includes customAdvisers and MeetingConfig.selectableAdvisers
        self.assertEqual(len(vocab(pmFolder)), 6)

        # if we select a new organization, then the cache is cleaned
        # add an organization
        new_org = self.create('organization',
                              title='New organization',
                              acronym='N.G.')
        new_org_uid = new_org.UID()
        cfg.setSelectableAdvisers(cfg.getSelectableAdvisers() +
                                  (new_org_uid, ))
        # cache was cleaned
        self.assertEqual(len(vocab(pmFolder)), 7)
        # edit an organization
        new_org_term_id = 'real_org_uid__{0}'.format(new_org_uid)
        self.assertEqual(
            vocab(pmFolder).by_token[new_org_term_id].title,
            'New organization (Inactive)')
        new_org.title = u'Modified title'
        notify(ObjectModifiedEvent(new_org))
        # cache was cleaned
        self.assertEqual(
            vocab(pmFolder).by_token[new_org_term_id].title,
            'Modified title (Inactive)')
        # select the organization, cache is cleaned
        self._select_organization(new_org_uid)
        self.assertEqual(
            vocab(pmFolder).by_token[new_org_term_id].title, 'Modified title')
        # remove an organization
        # first need to unselect it
        self._select_organization(new_org_uid, remove=True)
        self.portal.restrictedTraverse('@@delete_givenuid')(new_org_uid)
        # cache was cleaned
        self.assertEqual(len(vocab(pmFolder)), 6)

        # if we add/remove/edit a customAdviser, then the cache is cleaned
        # add a customAdviser
        customAdvisers.append({
            'row_id': 'unique_id_999',
            'org': self.vendors_uid,
            'gives_auto_advice_on': '',
            'for_item_created_from': '2012/01/01',
            'delay': '11',
            'delay_label': 'New delay'
        })
        cfg.setCustomAdvisers(customAdvisers)
        cfg.at_post_edit_script()
        self.assertEqual(len(vocab(pmFolder)), 7)
        self.assertTrue(
            'delay_row_id__unique_id_999' in vocab(pmFolder).by_token)
        # delay is displayed in customAdviser title
        self.assertTrue('11 day(s)' in vocab(
            pmFolder).by_token['delay_row_id__unique_id_999'].title)
        # edit a customAdviser
        customAdvisers[-1]['delay'] = '12'
        cfg.setCustomAdvisers(customAdvisers)
        cfg.at_post_edit_script()
        self.assertTrue('12 day(s)' in vocab(
            pmFolder).by_token['delay_row_id__unique_id_999'].title)
        # remove a customAdviser
        customAdvisers = customAdvisers[:-1]
        cfg.setCustomAdvisers(customAdvisers)
        cfg.at_post_edit_script()
        self.assertEqual(len(vocab(pmFolder)), 6)
        # power advisers are taken into account by the vocabulary
        cfg.setPowerAdvisersGroups([self.endUsers_uid])
        cfg.at_post_edit_script()
        self.assertEqual(len(vocab(pmFolder)), 7)